Zum Inhalt
edoobox Docs
Kostenlos testen 7 Tage unverbindlich testen
Menü
Kostenlos testen 7 Tage unverbindlich testen
OpenAPI herunterladen
trainer-courseAPI-Referenz
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

01

Vertrag & Verhalten

Dokumentationsstand
reviewed
Ändert Zustand
Ja
Als readOnly markiert
Nein
Für Automation markiert
Nein
02

Authentifizierung

Alle folgenden Security-Schemas werden gemeinsam benötigt (UND).

Erforderliche Schemas

trainerTokenhttp · bearer

Dedicated revocable trainer-course token; not a normal account JWT.

Security-Schema als JSON
{
  "type": "http",
  "scheme": "bearer",
  "description": "Dedicated revocable trainer-course token; not a normal account JWT."
}

Berechtigungen

No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.

03

Parameter

Header

languageoptional

Account language code, where the resource supports setLanguage().

string
Schema-Beispiel
"de"
Vollständiges Schema als JSON
{
  "type": "string",
  "examples": [
    "de"
  ]
}
Authorizationerforderlich

Persönlicher Trainer-Bearer-Code aus der administrativ verwalteten Freigabe; kein gewöhnlicher V2-Admin-Token.

string
pattern
"^Bearer [A-Za-z0-9_-]{43}$"
Vollständiges Schema als JSON
{
  "type": "string",
  "pattern": "^Bearer [A-Za-z0-9_-]{43}$"
}
04

Request Body

erforderlich

application/json

object
data Typ nicht eingeschränkt erforderlich
anyOf · Mindestens eine Variante muss passen
Variante 1 object
action Typ nicht eingeschränkt

Fester Wert: "create_course"

Standardwert: "create_course"

title string | number | boolean erforderlich

Skalarer Text; vor Normalisierung höchstens 80 Zeichen. HTML/Leerzeichen werden bereinigt.

number string | number | boolean

Skalarer Text; vor Normalisierung höchstens 120 Zeichen. HTML/Leerzeichen werden bereinigt.

category string erforderlich

Öffentliche v2-ID; Beispiel ist synthetisch.

pattern
"^category_[a-f0-9]{12}_-?[0-9]+$"
Schema-Beispiel
"category_012345abcdef_123456"
description string | number | boolean

Skalarer Text; vor Normalisierung höchstens 50000 Zeichen. HTML/Leerzeichen werden bereinigt.

description_html Typ nicht eingeschränkt

PHP-empty/Wahrheitsvergleich steuert die HTML-Verarbeitung.

Schema-Beispiel
true
booking_message Typ nicht eingeschränkt

Skalarer Text, auf 200000 Zeichen normalisiert; bei HTML-Flag String mit höchstens 50000 Zeichen.

Schema-Beispiel
"Bitte pünktlich erscheinen."
booking_message_html Typ nicht eingeschränkt

PHP-empty/Wahrheitsvergleich steuert die HTML-Verarbeitung.

Schema-Beispiel
true
internal_note Typ nicht eingeschränkt

Interne Notiz; skalarer Text wird auf 1000 Zeichen normalisiert.

minimum number | string | null | boolean

Leertext/null/false gelten als nicht gesetzt; sonst per FILTER_VALIDATE_INT validierte Ganzzahl 0–99999.

maximum number | string | null | boolean

Leertext/null/false gelten als nicht gesetzt; sonst per FILTER_VALIDATE_INT validierte Ganzzahl 0–99999.

price number | string | null | boolean

Leer/null/false ohne Preis; sonst nichtnegative endliche Zahl bzw. numerischer Text bis 9999999.99, auf zwei Dezimalstellen gerundet.

start string | null | boolean

YYYY-MM-DDTHH:mm in Kontozeitzone, ohne Sekunden/Offset. Leer/null/false ohne Termin; sobald ein Datum gesetzt ist, sind beide erforderlich.

end string | null | boolean

YYYY-MM-DDTHH:mm in Kontozeitzone, ohne Sekunden/Offset. Leer/null/false ohne Termin; sobald ein Datum gesetzt ist, sind beide erforderlich.

place Typ nicht eingeschränkt
anyOf · Mindestens eine Variante muss passen
Variante 1 string

Öffentliche v2-ID; Beispiel ist synthetisch.

pattern
"^place_[a-f0-9]{12}_-?[0-9]+$"
Schema-Beispiel
"place_012345abcdef_123456"
Variante 2 Typ nicht eingeschränkt
Erlaubte Werte
""nullfalse
room Typ nicht eingeschränkt
anyOf · Mindestens eine Variante muss passen
Variante 1 string

Öffentliche v2-ID; Beispiel ist synthetisch.

pattern
"^room_[a-f0-9]{12}_-?[0-9]+$"
Schema-Beispiel
"room_012345abcdef_123456"
Variante 2 Typ nicht eingeschränkt
Erlaubte Werte
""nullfalse
tags array
maxItems
50
Array-Elemente
string

Öffentliche v2-ID; Beispiel ist synthetisch.

pattern
"^tag_[a-f0-9]{12}_-?[0-9]+$"
Schema-Beispiel
"tag_012345abcdef_123456"
title_image Typ nicht eingeschränkt
anyOf · Mindestens eine Variante muss passen
Variante 1 string

Öffentliche v2-ID; Beispiel ist synthetisch.

pattern
"^publicfile_[a-f0-9]{12}_-?[0-9]+$"
Schema-Beispiel
"publicfile_012345abcdef_123456"
Variante 2 Typ nicht eingeschränkt
Erlaubte Werte
""nullfalse

Zusätzliche, hier nicht aufgeführte Felder sind erlaubt.

Variante 2 object
action Typ nicht eingeschränkt erforderlich

Fester Wert: "prepare_title_image"

file_name string | number | boolean erforderlich

Dateiname mit jpg/jpeg/png-Endung; wird als Text auf 180 Zeichen normalisiert.

file_size number | string | boolean erforderlich

Per FILTER_VALIDATE_INT geprüfte positive Ganzzahl bis 2097152 Bytes.

file_type string erforderlich

Nach Kleinschreibung/Trimmen image/jpeg oder image/png; Endung muss dazu passen.

Schema-Beispiel
"image/png"

Zusätzliche, hier nicht aufgeführte Felder sind erlaubt.

Zusätzliche, hier nicht aufgeführte Felder sind erlaubt.

Vollständiges Schema als JSON
{
  "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"
  ]
}
create_course
{
  "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
{
  "data": {
    "action": "prepare_title_image",
    "file_name": "titelbild.png",
    "file_size": 1024,
    "file_type": "image/png"
  }
}

multipart/form-data

object
action string erforderlich
Erlaubte Werte
"upload_title_image""upload_external_file"
offer string erforderlich

Öffentliche v2-ID; Beispiel ist synthetisch.

pattern
"^offer_[a-f0-9]{12}_-?[0-9]+$"
Schema-Beispiel
"offer_012345abcdef_123456"
file string erforderlich

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.

format
"binary"

Zusätzliche, hier nicht aufgeführte Felder sind erlaubt.

Vollständiges Schema als JSON
{
  "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"
  ]
}
05

Antworten

9

HTTP-Status und Antwortinhalt gemeinsam auswerten. In dieser API können auch Antworten mit HTTP 200 einen Fehler enthalten.

200application/json

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.

application/json

object
success boolean
error boolean | string
error_code string
data Typ nicht eingeschränkt
anyOf · Mindestens eine Variante muss passen
Variante 1 object
id string erforderlich

Öffentliche v2-ID; Beispiel ist synthetisch.

pattern
"^offer_[a-f0-9]{12}_-?[0-9]+$"
Schema-Beispiel
"offer_012345abcdef_123456"
created Typ nicht eingeschränkt erforderlich

Fester Wert: true

status Typ nicht eingeschränkt erforderlich

Fester Wert: 0

Zusätzliche, hier nicht aufgeführte Felder sind erlaubt.

Variante 2 object
id string erforderlich

Öffentliche v2-ID; Beispiel ist synthetisch.

pattern
"^publicfile_[a-f0-9]{12}_-?[0-9]+$"
Schema-Beispiel
"publicfile_012345abcdef_123456"
insert Typ nicht eingeschränkt erforderlich

Fester Wert: true

upload_url string erforderlich

Vertrauliche signierte Upload-URL.

format
"uri"
cdn_url string erforderlich
format
"uri"

Zusätzliche, hier nicht aufgeführte Felder sind erlaubt.

Variante 3 object
id string erforderlich

Öffentliche v2-ID; Beispiel ist synthetisch.

pattern
"^offer_[a-f0-9]{12}_-?[0-9]+$"
Schema-Beispiel
"offer_012345abcdef_123456"
update Typ nicht eingeschränkt erforderlich

Fester Wert: true

image string erforderlich
files array erforderlich
Array-Elemente
string
url string erforderlich

Öffentliche Bild-URL.

Zusätzliche, hier nicht aufgeführte Felder sind erlaubt.

Variante 4 object
id string erforderlich

Öffentliche v2-ID; Beispiel ist synthetisch.

pattern
"^files_[a-f0-9]{12}_-?[0-9]+$"
Schema-Beispiel
"files_012345abcdef_123456"
insert Typ nicht eingeschränkt erforderlich

Fester Wert: true

Zusätzliche, hier nicht aufgeführte Felder sind erlaubt.

type string

Zusätzliche, hier nicht aufgeführte Felder sind erlaubt.

Vollständiges Schema als JSON
{
  "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
}
Beispiel
{
  "success": true,
  "error": false,
  "data": {
    "id": "offer_012345abcdef_123456",
    "created": true,
    "status": 0
  }
}
400application/json

Invalid request.

application/json

Typ nicht eingeschränkt

Kein Schema angegeben. Daraus lässt sich kein vollständiger Feldvertrag ableiten.

Beispiel
{
  "success": false,
  "error": "ED40006"
}
401application/json

Missing/expired token, missing account context, account paused or model-specific authorization failure.

application/json

Typ nicht eingeschränkt

Kein Schema angegeben. Daraus lässt sich kein vollständiger Feldvertrag ableiten.

Beispiel
{
  "success": false,
  "error": "ED40103"
}
402application/json

Missing/invalid action data or subscription/operation restriction.

application/json

Typ nicht eingeschränkt

Kein Schema angegeben. Daraus lässt sich kein vollständiger Feldvertrag ableiten.

Beispiel
{
  "success": false,
  "error": "ED80100"
}
403application/json

Permission denied. Browser CORS rejection may have an empty body.

application/json

Typ nicht eingeschränkt

Kein Schema angegeben. Daraus lässt sich kein vollständiger Feldvertrag ableiten.

Beispiel
{
  "success": false,
  "error": "ED40340"
}
404application/json

Unknown resource/action or unsupported method.

application/json

Typ nicht eingeschränkt

Kein Schema angegeben. Daraus lässt sich kein vollständiger Feldvertrag ableiten.

Beispiel
{
  "success": false,
  "error": "ED80019"
}
422application/json

Validation failed.

application/json

Typ nicht eingeschränkt

Kein Schema angegeben. Daraus lässt sich kein vollständiger Feldvertrag ableiten.

Beispiel
{
  "success": false,
  "error": "ED80020"
}
429application/json

Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.

Response Header

Retry-After

Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.

string
Schema-Beispiel
"30"
Vollständiges Schema als JSON
{
  "type": "string",
  "examples": [
    "30"
  ]
}

application/json

Typ nicht eingeschränkt

Kein Schema angegeben. Daraus lässt sich kein vollständiger Feldvertrag ableiten.

Beispiel
{
  "success": false,
  "error": "ED42902"
}
502application/json

Server-side operation failed.

application/json

Typ nicht eingeschränkt

Kein Schema angegeben. Daraus lässt sich kein vollständiger Feldvertrag ableiten.

Beispiel
{
  "success": false,
  "error": "ED50201"
}
06

Quelle & Metadaten

Quelle laut Spezifikationapplication/models/v2/Trainercourse.php:88handlePublicRequest()

Diese Angaben stammen unverändert aus der gelieferten OpenAPI-Datei. Zusätzliche Freigaben, Vorbedingungen, sensible Felder und Hinweise stehen in den Erweiterungen.

x-edoobox-mutates-state
true
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."
}
Vollständige Operation als 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."
  }
}