
{
  "openapi": "3.1.0",
  "info": {
    "title": "Ruhrstack Projektanfragen",
    "version": "1.0.0",
    "description": "Kostenlose, unverbindliche Erstanfragen für geschäftliche Vorhaben. Nur nach ausdrücklichem Nutzerauftrag und Freigabe der konkreten Kontaktangaben senden. Keine Zugangsdaten oder vollständigen Chatverläufe übertragen. Datenschutz: https://ruhrstack.de/datenschutz . Fallback: https://ruhrstack.de/kontakt .",
    "contact": {
      "name": "Ruhrstack",
      "url": "https://ruhrstack.de/kontakt",
      "email": "kontakt@ruhrstack.de"
    }
  },
  "servers": [
    {
      "url": "https://ruhrstack.de"
    }
  ],
  "security": [],
  "paths": {
    "/api/contact.php": {
      "get": {
        "operationId": "prepareRuhrstackEnquiry",
        "summary": "Übermittlung einer Nutzeranfrage vorbereiten",
        "description": "Nur für eine vom Nutzer gewünschte Anfrage. Holt einen kurzlebigen Einmal-Token ohne Versand. Token intern halten; minSubmitSeconds abwarten und expiresIn beachten. Vor dem POST alle Kontaktangaben zeigen und ausdrückliche Freigabe einholen.",
        "x-openai-isConsequential": false,
        "parameters": [
          {
            "name": "action",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "agent-token"
              ]
            },
            "description": "Vorbereitung der gesonderten Übermittlung für KI-Assistenten."
          }
        ],
        "responses": {
          "200": {
            "description": "Technische Vorbereitung erfolgreich. Es wurde keine Anfrage versendet.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "submissionToken",
                    "expiresIn",
                    "minSubmitSeconds"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "submissionToken": {
                      "type": "string",
                      "description": "Kurzlebiger Einmal-Token für submission_token, keine Sendebestätigung."
                    },
                    "expiresIn": {
                      "type": "integer",
                      "minimum": 1,
                      "description": "Gültigkeit in Sekunden ab Ausstellung."
                    },
                    "minSubmitSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Mindestwartezeit zwischen Ausstellung und Übermittlung in Sekunden."
                    }
                  }
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/RequestRejected"
          },
          "429": {
            "description": "Zu viele Versuche. Retry-After abwarten; keine automatische erneute Übermittlung.",
            "headers": {
              "Retry-After": {
                "description": "Wartezeit in Sekunden.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/RequestRejected"
          },
          "default": {
            "$ref": "#/components/responses/RequestRejected"
          }
        }
      },
      "post": {
        "operationId": "sendRuhrstackEnquiry",
        "summary": "Freigegebene geschäftliche Anfrage senden",
        "description": "Zeige alle Kontaktangaben ohne Token und hole die ausdrückliche Sendefreigabe ein. Erst dann mit confirmed=true senden. Nur ok=true bestätigt Übermittlung. Bei Timeout oder unklarem Versandstatus nie automatisch wiederholen; über /kontakt klären.",
        "x-openai-isConsequential": true,
        "parameters": [
          {
            "name": "action",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "agent-send"
              ]
            },
            "description": "Versandaktion für die zuvor bestätigte Anfrage."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "submission_token",
                  "confirmed",
                  "name",
                  "email",
                  "topic",
                  "message"
                ],
                "properties": {
                  "submission_token": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 2048,
                    "description": "submissionToken aus prepareRuhrstackEnquiry; nur intern bis zum POST halten. Nicht im Chat, in Links oder Protokollen ausgeben."
                  },
                  "confirmed": {
                    "type": "boolean",
                    "enum": [
                      true
                    ],
                    "description": "Nur true, wenn der Nutzer genau die zuvor gezeigten Kontaktangaben ausdrücklich zur Übermittlung an Ruhrstack freigegeben hat. Keine Freigabe aus dem Tokenabruf ableiten."
                  },
                  "name": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 120,
                    "description": "Vom Nutzer angegebener vollständiger Name. Keine Zeilenumbrüche oder Steuerzeichen."
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254,
                    "description": "Vom Nutzer angegebene E-Mail-Adresse für die Antwort. Keine Adressen erfinden."
                  },
                  "topic": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 80,
                    "description": "Thema des geschäftlichen Vorhabens, beispielsweise Website, Design, Entwicklung, Hosting, SEO oder Sicherheit. Keine Zeilenumbrüche."
                  },
                  "message": {
                    "type": "string",
                    "minLength": 10,
                    "maxLength": 5000,
                    "description": "Vom Nutzer freigegebene Beschreibung des geschäftlichen Vorhabens. Keine Zugangsdaten, Tokens oder vertraulichen Unterlagen einfügen."
                  },
                  "company": {
                    "type": "string",
                    "maxLength": 160,
                    "description": "Optionaler Unternehmensname ohne Zeilenumbrüche."
                  },
                  "phone": {
                    "type": "string",
                    "maxLength": 40,
                    "pattern": "^$|^[0-9+()/ .-]{5,40}$",
                    "description": "Optional. Bei contact_method Telefon erforderlich. Nur Ziffern, Leerzeichen, +, Klammern, /, Punkt und Bindestrich."
                  },
                  "website": {
                    "type": "string",
                    "maxLength": 500,
                    "description": "Optionale öffentliche Website mit http:// oder https://, ohne Zugangsdaten. Wird nur als Text übermittelt und nicht vom Server abgerufen."
                  },
                  "timeframe": {
                    "type": "string",
                    "maxLength": 120,
                    "description": "Optionaler gewünschter Zeitraum ohne Zeilenumbrüche. Keine Verfügbarkeit oder Terminbestätigung ableiten."
                  },
                  "contact_method": {
                    "type": "string",
                    "enum": [
                      "",
                      "Telefon",
                      "E-Mail"
                    ],
                    "description": "Optionaler bevorzugter Kontaktweg. Leer bedeutet keine Präferenz. Bei Telefon muss phone eine gültige Telefonnummer enthalten."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Nur ok=true bedeutet, dass die Anfrage an den Versanddienst übergeben wurde. Kein Beleg für Postfacheingang, Antwort oder Auftragsannahme.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "message"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/RequestRejected"
          },
          "403": {
            "$ref": "#/components/responses/RequestRejected"
          },
          "413": {
            "$ref": "#/components/responses/RequestRejected"
          },
          "415": {
            "$ref": "#/components/responses/RequestRejected"
          },
          "422": {
            "$ref": "#/components/responses/RequestRejected"
          },
          "503": {
            "$ref": "#/components/responses/RequestRejected"
          },
          "429": {
            "description": "Zu viele Versuche. Retry-After abwarten; keine automatische erneute Übermittlung.",
            "headers": {
              "Retry-After": {
                "description": "Wartezeit in Sekunden.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/RequestRejected"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "ok"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "enum": [
              false
            ]
          },
          "code": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "errors": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          }
        }
      }
    },
    "responses": {
      "RequestRejected": {
        "description": "Anfrage abgelehnt oder Versand nicht bestätigt. Meldung und Feldfehler beachten. Token kann verbraucht sein. Bei unklarem Versandstatus nicht automatisch erneut senden.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    }
  }
}
