openapi: 3.1.0
info:
  title: PostOmnia API
  description: PostOmnia Multi-Channel Social Publishing. Zum Einbinden als ChatGPT Action / Custom GPT.
  version: 0.1.0
servers:
  - url: 'https://postomnia.de/api'
    description: PostOmnia API
security:
  - ApiKeyAuth: []
  - BearerAuth: []
components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
    BearerAuth:
      type: http
      scheme: bearer
  schemas:
    Post:
      type: object
      properties:
        id: { type: string }
        text: { type: string }
        mediaUrls: { type: array, items: { type: string } }
        accountIds: { type: array, items: { type: string } }
        scheduledAt: { type: string, nullable: true }
        status: { type: string, enum: [draft, scheduled, publishing, published, partial, failed] }
        results: { type: object, additionalProperties: true }
paths:
  /platforms:
    get:
      operationId: listPlatforms
      summary: Unterstützte Plattformen und ihre benötigten Zugangsfelder auflisten.
      responses: { '200': { description: OK } }
  /accounts:
    get:
      operationId: listAccounts
      summary: Verbundene Konten auflisten.
      responses: { '200': { description: OK } }
    post:
      operationId: connectAccount
      summary: Neues Konto verbinden.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [platform]
              properties:
                platform: { type: string }
                label: { type: string }
                credentials: { type: object, additionalProperties: { type: string } }
      responses: { '200': { description: OK } }
  /accounts/{id}:
    delete:
      operationId: disconnectAccount
      summary: Konto trennen.
      parameters:
        - { name: id, in: path, required: true, schema: { type: string } }
      responses: { '200': { description: OK } }
  /posts:
    get:
      operationId: listPosts
      summary: Posts auflisten (optional nach Status filtern).
      parameters:
        - { name: status, in: query, required: false, schema: { type: string } }
      responses: { '200': { description: OK } }
    post:
      operationId: createPost
      summary: Post erstellen (Entwurf, geplant oder sofort veröffentlichen).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [accountIds]
              properties:
                text: { type: string }
                mediaUrls: { type: array, items: { type: string } }
                accountIds: { type: array, items: { type: string } }
                scheduledAt: { type: string, description: "ISO-Zeitpunkt, leer = Entwurf" }
                publishNow: { type: boolean }
      responses: { '200': { description: OK } }
  /posts/{id}:
    get:
      operationId: getPost
      summary: Einzelnen Post abrufen.
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      responses: { '200': { description: OK } }
    patch:
      operationId: updatePost
      summary: Entwurf/geplanten Post ändern.
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      requestBody:
        content:
          application/json:
            schema: { $ref: '#/components/schemas/Post' }
      responses: { '200': { description: OK } }
    delete:
      operationId: deletePost
      summary: Post löschen.
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      responses: { '200': { description: OK } }
  /posts/{id}/publish:
    post:
      operationId: publishPost
      summary: Post sofort auf allen Kanälen veröffentlichen.
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      responses: { '200': { description: OK } }
  /posts/{id}/retry:
    post:
      operationId: retryFailed
      summary: Nur fehlgeschlagene Kanäle erneut versuchen.
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      responses: { '200': { description: OK } }
  /media:
    get:
      operationId: listMedia
      summary: Hochgeladene Medien mit öffentlichen URLs auflisten.
      responses: { '200': { description: OK } }
  /media/import:
    post:
      operationId: importMedia
      summary: Bild/Video von einer URL laden, neu hosten und öffentliche URL zurückgeben.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [url]
              properties:
                url: { type: string }
                filename: { type: string }
      responses: { '200': { description: OK } }
  /media/base64:
    post:
      operationId: uploadMediaBase64
      summary: Bild/Video aus Base64 speichern und öffentliche URL zurückgeben.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [dataBase64]
              properties:
                dataBase64: { type: string, description: "Base64 oder data:-URL" }
                filename: { type: string }
                contentType: { type: string }
      responses: { '200': { description: OK } }
  /media/upload:
    post:
      operationId: uploadMedia
      summary: Rohe Dateibytes hochladen (Body = Datei). Dateiname als Query, Typ als Content-Type-Header.
      parameters:
        - { name: filename, in: query, required: false, schema: { type: string } }
      requestBody:
        required: true
        content:
          application/octet-stream:
            schema: { type: string, format: binary }
      responses: { '200': { description: OK } }
  /media/{id}:
    delete:
      operationId: deleteMedia
      summary: Hochgeladenes Medium löschen.
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      responses: { '200': { description: OK } }
  /media/uploads:
    post:
      operationId: initChunkedUpload
      summary: Chunked-Upload starten (für große Videos). Liefert uploadId und chunkSize.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [filename, contentType]
              properties:
                filename: { type: string }
                contentType: { type: string }
                size: { type: integer }
      responses: { '200': { description: OK } }
  /media/uploads/{id}:
    put:
      operationId: appendChunk
      summary: Nächsten Chunk anhängen (Body = Bytes, offset als Query).
      parameters:
        - { name: id, in: path, required: true, schema: { type: string } }
        - { name: offset, in: query, required: true, schema: { type: integer } }
      requestBody:
        required: true
        content:
          application/octet-stream:
            schema: { type: string, format: binary }
      responses: { '200': { description: OK } }
  /media/uploads/{id}/complete:
    post:
      operationId: completeChunkedUpload
      summary: Chunked-Upload finalisieren, liefert media.url.
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      responses: { '200': { description: OK } }
