openapi: "3.1.0"
info:
  title: "Ryh API v1"
  version: "2026-07-01"
  summary: "Anbieter, Connectoren, Fähigkeiten und Verbindungen im Ryh-Netz."
  description: "Die Ryh API wird ausschließlich serverseitig aufgerufen. Zugangsschlüssel bleiben im Backend des Partners. Schreibende Aufrufe brauchen einen Vorgangsschlüssel, damit ein Wiederholungsversuch nie doppelt wirkt."
  contact:
    name: "Ryh Partner Support"
    url: "https://developer.ryh.de"
servers:
  - url: "https://api.ryh.de"
    description: Produktion
  - url: "https://ryh.de"
    description: "Gleiche Schnittstelle über die Hauptdomain"
tags:
  - name: "Übersicht"
    description: "Selbstauskunft der Schnittstelle"
  - name: Netz
    description: "Anbieter, Connectoren und Fähigkeiten"
  - name: Verbindungen
    description: "Verbindungen eines Betriebs"
  - name: "Vorgänge"
    description: "Vorgangsschlüssel und Wiederholsicherheit"
  - name: Live
    description: "Laufende Statusmeldungen"
paths:
  "/api/public/v1":
    get:
      tags:
        - "Übersicht"
      summary: "Übersicht und Fähigkeiten dieser Schnittstelle"
      security: []
      responses:
        "200":
          description: "Selbstauskunft mit Version, Authentifizierung und Routenliste."
          content:
            application/json:
              schema:
                type: object
                required:
                  - ok
                  - data
                  - meta
                properties:
                  ok:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                    properties:
                      service:
                        type: string
                      surface:
                        type: string
                      version:
                        type: string
                      authentication:
                        type: object
                        additionalProperties: true
                      idempotency:
                        type: object
                        additionalProperties: true
                      routes:
                        type: array
                        items:
                          "$ref": "#/components/schemas/RouteInfo"
                  meta:
                    "$ref": "#/components/schemas/Meta"
  "/api/public/v1/providers":
    get:
      tags:
        - Netz
      summary: "Anbieter im Ryh-Netz auflisten"
      security:
        - bearerAuth: []
          tenantHeader: []
      parameters:
        - name: category
          in: query
          required: false
          schema:
            type: string
          description: "Auf eine Kategorie eingrenzen."
      responses:
        "200":
          description: Anbieterliste.
          content:
            application/json:
              schema:
                type: object
                required:
                  - ok
                  - data
                  - meta
                properties:
                  ok:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                    properties:
                      count:
                        type: integer
                      providers:
                        type: array
                        items:
                          "$ref": "#/components/schemas/Provider"
                  meta:
                    "$ref": "#/components/schemas/Meta"
        "400":
          "$ref": "#/components/responses/Error"
        "401":
          "$ref": "#/components/responses/Error"
        "403":
          "$ref": "#/components/responses/Error"
        "404":
          "$ref": "#/components/responses/Error"
        "409":
          "$ref": "#/components/responses/Error"
        "428":
          "$ref": "#/components/responses/Error"
        "429":
          "$ref": "#/components/responses/Error"
        "500":
          "$ref": "#/components/responses/Error"
  "/api/public/v1/connectors":
    get:
      tags:
        - Netz
      summary: "Connectoren auflisten, optional gefiltert"
      security:
        - bearerAuth: []
          tenantHeader: []
      parameters:
        - name: capability
          in: query
          required: false
          schema:
            type: string
        - name: providerId
          in: query
          required: false
          schema:
            type: string
        - name: category
          in: query
          required: false
          schema:
            type: string
      responses:
        "200":
          description: Connectorliste.
          content:
            application/json:
              schema:
                type: object
                required:
                  - ok
                  - data
                  - meta
                properties:
                  ok:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                    properties:
                      count:
                        type: integer
                      connectors:
                        type: array
                        items:
                          "$ref": "#/components/schemas/Connector"
                  meta:
                    "$ref": "#/components/schemas/Meta"
        "400":
          "$ref": "#/components/responses/Error"
        "401":
          "$ref": "#/components/responses/Error"
        "403":
          "$ref": "#/components/responses/Error"
        "404":
          "$ref": "#/components/responses/Error"
        "409":
          "$ref": "#/components/responses/Error"
        "428":
          "$ref": "#/components/responses/Error"
        "429":
          "$ref": "#/components/responses/Error"
        "500":
          "$ref": "#/components/responses/Error"
  "/api/public/v1/capabilities":
    get:
      tags:
        - Netz
      summary: "Katalog aller Fähigkeiten"
      security:
        - bearerAuth: []
          tenantHeader: []
      responses:
        "200":
          description: "Fähigkeitskatalog."
          content:
            application/json:
              schema:
                type: object
                required:
                  - ok
                  - data
                  - meta
                properties:
                  ok:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                    properties:
                      count:
                        type: integer
                      capabilities:
                        type: array
                        items:
                          "$ref": "#/components/schemas/Capability"
                  meta:
                    "$ref": "#/components/schemas/Meta"
        "400":
          "$ref": "#/components/responses/Error"
        "401":
          "$ref": "#/components/responses/Error"
        "403":
          "$ref": "#/components/responses/Error"
        "404":
          "$ref": "#/components/responses/Error"
        "409":
          "$ref": "#/components/responses/Error"
        "428":
          "$ref": "#/components/responses/Error"
        "429":
          "$ref": "#/components/responses/Error"
        "500":
          "$ref": "#/components/responses/Error"
    post:
      tags:
        - Netz
      summary: "Fähigkeit eines Connectors prüfen"
      security:
        - bearerAuth: []
          tenantHeader: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - connectorId
                - capability
              properties:
                connectorId:
                  type: string
                  minLength: 2
                  maxLength: 120
                capability:
                  type: string
                  minLength: 2
                  maxLength: 120
                environment:
                  type: string
                  enum:
                    - development
                    - sandbox
                    - pilot
                    - production
                  default: sandbox
      responses:
        "200":
          description: "Entscheidung inklusive Begründung."
          content:
            application/json:
              schema:
                type: object
                required:
                  - ok
                  - data
                  - meta
                properties:
                  ok:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                    properties:
                      decision:
                        "$ref": "#/components/schemas/CapabilityDecision"
                      environment:
                        type: string
                        enum:
                          - development
                          - sandbox
                          - pilot
                          - production
                  meta:
                    "$ref": "#/components/schemas/Meta"
        "400":
          "$ref": "#/components/responses/Error"
        "401":
          "$ref": "#/components/responses/Error"
        "403":
          "$ref": "#/components/responses/Error"
        "404":
          "$ref": "#/components/responses/Error"
        "409":
          "$ref": "#/components/responses/Error"
        "428":
          "$ref": "#/components/responses/Error"
        "429":
          "$ref": "#/components/responses/Error"
        "500":
          "$ref": "#/components/responses/Error"
  "/api/public/v1/connections":
    get:
      tags:
        - Verbindungen
      summary: "Verbindungen des eigenen Betriebs"
      security:
        - bearerAuth: []
          tenantHeader: []
      responses:
        "200":
          description: "Verbindungsliste des Betriebs."
          content:
            application/json:
              schema:
                type: object
                required:
                  - ok
                  - data
                  - meta
                properties:
                  ok:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                    properties:
                      tenant:
                        type: string
                      count:
                        type: integer
                      connections:
                        type: array
                        items:
                          "$ref": "#/components/schemas/Connection"
                  meta:
                    "$ref": "#/components/schemas/Meta"
        "400":
          "$ref": "#/components/responses/Error"
        "401":
          "$ref": "#/components/responses/Error"
        "403":
          "$ref": "#/components/responses/Error"
        "404":
          "$ref": "#/components/responses/Error"
        "409":
          "$ref": "#/components/responses/Error"
        "428":
          "$ref": "#/components/responses/Error"
        "429":
          "$ref": "#/components/responses/Error"
        "500":
          "$ref": "#/components/responses/Error"
    post:
      tags:
        - Verbindungen
      summary: "Verbindung anlegen (idempotent)"
      security:
        - bearerAuth: []
          tenantHeader: []
      parameters:
        - name: Idempotency-Key
          in: header
          required: true
          schema:
            type: string
            pattern: "^[A-Za-z0-9._:-]{8,128}$"
          description: "Vorgangsschlüssel. Gleicher Schlüssel und gleicher Inhalt liefern dieselbe Antwort erneut."
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - connectorId
                - environment
                - scopeLabel
                - capabilities
              properties:
                connectorId:
                  type: string
                  minLength: 2
                  maxLength: 120
                environment:
                  type: string
                  enum:
                    - development
                    - sandbox
                    - pilot
                    - production
                scopeLabel:
                  type: string
                  minLength: 2
                  maxLength: 120
                  description: "Sprechender Name, z. B. der Name des Lokals."
                capabilities:
                  type: array
                  minItems: 1
                  maxItems: 24
                  items:
                    type: string
                    minLength: 2
                    maxLength: 120
      responses:
        "200":
          description: "Verbindung angelegt oder Wiederholung derselben Antwort."
          content:
            application/json:
              schema:
                type: object
                required:
                  - ok
                  - data
                  - meta
                properties:
                  ok:
                    type: boolean
                    enum:
                      - true
                  data:
                    "$ref": "#/components/schemas/Connection"
                  meta:
                    "$ref": "#/components/schemas/Meta"
        "400":
          "$ref": "#/components/responses/Error"
        "401":
          "$ref": "#/components/responses/Error"
        "403":
          "$ref": "#/components/responses/Error"
        "404":
          "$ref": "#/components/responses/Error"
        "409":
          "$ref": "#/components/responses/Error"
        "428":
          "$ref": "#/components/responses/Error"
        "429":
          "$ref": "#/components/responses/Error"
        "500":
          "$ref": "#/components/responses/Error"
  "/api/public/v1/idempotency-keys":
    post:
      tags:
        - "Vorgänge"
      summary: "Neuen Vorgangsschlüssel erzeugen"
      security:
        - bearerAuth: []
          tenantHeader: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - operation
              properties:
                operation:
                  type: string
                  minLength: 2
                  maxLength: 80
                  description: "Kurzname des Vorgangs, z. B. connection.create."
      responses:
        "200":
          description: "Neuer Schlüssel mit Gültigkeitsdauer."
          content:
            application/json:
              schema:
                type: object
                required:
                  - ok
                  - data
                  - meta
                properties:
                  ok:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                    properties:
                      idempotencyKey:
                        type: string
                      operation:
                        type: string
                      expiresAt:
                        type: string
                        format: date-time
                  meta:
                    "$ref": "#/components/schemas/Meta"
        "400":
          "$ref": "#/components/responses/Error"
        "401":
          "$ref": "#/components/responses/Error"
        "403":
          "$ref": "#/components/responses/Error"
        "404":
          "$ref": "#/components/responses/Error"
        "409":
          "$ref": "#/components/responses/Error"
        "428":
          "$ref": "#/components/responses/Error"
        "429":
          "$ref": "#/components/responses/Error"
        "500":
          "$ref": "#/components/responses/Error"
  "/api/public/v1/stream":
    get:
      tags:
        - Live
      summary: "Laufende Statusmeldungen (Server-Sent Events)"
      description: "Liefert Zustandsänderungen fortlaufend. Nach einem Abbruch wird mit 'Last-Event-ID' oder '?cursor=' lückenlos weitergelesen; jede Meldung trägt eine fortlaufende Nummer und kommt genau einmal an."
      security: []
      parameters:
        - name: cursor
          in: query
          required: false
          schema:
            type: integer
            minimum: 0
          description: "Zuletzt gelesene Nummer."
        - name: Last-Event-ID
          in: header
          required: false
          schema:
            type: string
      responses:
        "200":
          description: Ereignisstrom.
          content:
            text/event-stream:
              schema:
                type: string
  "/api/public/v1/openapi.json":
    get:
      tags:
        - "Übersicht"
      summary: "Diese Beschreibung als OpenAPI-Dokument"
      security: []
      responses:
        "200":
          description: "OpenAPI 3.1 Dokument."
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: "Zugangsschlüssel des Partners. Nur serverseitig senden, nie im Browser."
    tenantHeader:
      type: apiKey
      in: header
      name: x-ryh-tenant
      description: "Kennung des Betriebs."
  responses:
    Error:
      description: "Fehlermeldung mit klarer Ursache und nächstem Schritt."
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/Failure"
  schemas:
    Meta:
      type: object
      required:
        - requestId
        - version
        - at
      properties:
        requestId:
          type: string
          description: "Kennung dieses Aufrufs für Rückfragen."
        version:
          type: string
          example: "2026-07-01"
        at:
          type: string
          format: date-time
        idempotencyKey:
          type: string
        replayed:
          type: boolean
          description: "Antwort stammt aus einem früheren, gleichen Vorgang."
    Failure:
      type: object
      required:
        - ok
        - error
        - meta
      properties:
        ok:
          type: boolean
          enum:
            - false
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              enum:
                - unauthorized
                - forbidden
                - not_found
                - invalid_request
                - capability_denied
                - idempotency_conflict
                - idempotency_required
                - rate_limited
                - internal
            message:
              type: string
            detail:
              type: string
            hint:
              type: string
        meta:
          "$ref": "#/components/schemas/Meta"
    RouteInfo:
      type: object
      properties:
        method:
          type: string
        path:
          type: string
        purpose:
          type: string
        auth:
          type: boolean
    Provider:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        category:
          type: string
        summary:
          type: string
      additionalProperties: true
    Connector:
      type: object
      properties:
        id:
          type: string
        providerId:
          type: string
        name:
          type: string
        category:
          type: string
        capabilities:
          type: array
          items:
            type: string
      additionalProperties: true
    Capability:
      type: object
      properties:
        id:
          type: string
        label:
          type: string
        critical:
          type: boolean
        idempotencyRequired:
          type: boolean
      additionalProperties: true
    CapabilityDecision:
      type: object
      required:
        - connectorId
        - capability
        - allowed
        - critical
        - idempotencyRequired
        - reason
      properties:
        connectorId:
          type: string
        capability:
          type: string
        allowed:
          type: boolean
        critical:
          type: boolean
        idempotencyRequired:
          type: boolean
        reason:
          type: string
    Connection:
      type: object
      properties:
        id:
          type: string
        connectorId:
          type: string
        environment:
          type: string
          enum:
            - development
            - sandbox
            - pilot
            - production
        scopeLabel:
          type: string
        state:
          type: string
        capabilities:
          type: array
          items:
            type: object
            properties:
              capability:
                type: string
              critical:
                type: boolean
              idempotencyRequired:
                type: boolean
        nextStep:
          type: string
        createdAt:
          type: string
          format: date-time
