{
  "openapi": "3.1.0",
  "info": {
    "title": "Vergabezone Public Feeds",
    "version": "1.1.0",
    "description": "Approved public metadata and original learning content. No API key, workspace data, upstream URL fetching or bulk archive access. Use the live catalogue for current source permissions, hub IDs and available filters. Poll at most once every 15 minutes. Selection counts describe bounded metadata windows, not complete markets."
  },
  "servers": [
    {
      "url": "https://vergabezone.de"
    }
  ],
  "paths": {
    "/api/index.php": {
      "get": {
        "summary": "Read a catalogue or public feed",
        "operationId": "readPublicFeed",
        "description": "action=feed-catalogue returns discovery metadata; action=public-feed returns RSS 2.0, Atom, JSON Feed 1.1, or an events-only iCalendar. Tenders require a hub from the live catalogue. Only use filters advertised for the selected collection. Unknown or unsupported combinations return 422; an unknown or missing tender hub returns 404.",
        "parameters": [
          {
            "name": "action",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "feed-catalogue",
                "public-feed"
              ]
            },
            "example": "public-feed"
          },
          {
            "name": "kind",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "news",
                "events",
                "learning",
                "tenders"
              ],
              "default": "news"
            }
          },
          {
            "name": "format",
            "in": "query",
            "description": "ics is available only for events.",
            "schema": {
              "type": "string",
              "enum": [
                "rss",
                "atom",
                "json",
                "ics"
              ],
              "default": "rss"
            }
          },
          {
            "name": "lang",
            "in": "query",
            "description": "Interface and explanatory language. Original source headlines are not translated.",
            "schema": {
              "type": "string",
              "enum": [
                "de",
                "en"
              ],
              "default": "de"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum 24 for tenders. Maximum 50 for other collections. A bounded candidate window can return fewer items.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            }
          },
          {
            "name": "q",
            "in": "query",
            "description": "Optional literal substring, up to80 Unicode characters. News: own source headline only. Events: own source title/reported city. Tenders: title/buyer/city within the selected bounded public hub. Learning does not support a nonempty q. German umlaut/ss variants are folded. Wildcard characters are literal; no article bodies or private records are searched.",
            "schema": {
              "type": "string",
              "maxLength": 80
            },
            "example": "Digitalisierung"
          },
          {
            "name": "hub",
            "in": "query",
            "description": "Required for tenders only: an exact sector-*, state-* or city-* ID from the catalogue.",
            "schema": {
              "type": "string",
              "maxLength": 100
            },
            "example": "sector-it-dienstleistungen"
          },
          {
            "name": "source",
            "in": "query",
            "description": "Approved source ID or all; news, events and tenders only. Discover valid IDs in the catalogue.",
            "schema": {
              "type": "string",
              "default": "all",
              "maxLength": 64
            }
          },
          {
            "name": "topic",
            "in": "query",
            "description": "Advertised topic ID or all; news and events only.",
            "schema": {
              "type": "string",
              "default": "all",
              "maxLength": 64
            }
          },
          {
            "name": "region",
            "in": "query",
            "description": "Advertised German state, DE or EU code or all. Events permit German state codes only; news supports DE and EU.",
            "schema": {
              "type": "string",
              "default": "all",
              "maxLength": 20
            }
          },
          {
            "name": "If-None-Match",
            "in": "header",
            "description": "Conditional feed request using the ETag returned by a previous response. The catalogue has a request-time generatedAt and no ETag.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Bounded public metadata, with original sources and coverage qualifiers.",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Feed-Status": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Catalogue when action=feed-catalogue."
                }
              },
              "application/feed+json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonFeed"
                }
              },
              "application/rss+xml": {
                "schema": {
                  "type": "string"
                }
              },
              "application/atom+xml": {
                "schema": {
                  "type": "string"
                }
              },
              "text/calendar": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "304": {
            "description": "Feed content unchanged. No response body."
          },
          "404": {
            "description": "Missing or unknown tender hub."
          },
          "405": {
            "description": "Only GET, HEAD and public CORS OPTIONS are supported."
          },
          "422": {
            "description": "Invalid, unknown or unsupported filter/format combination."
          },
          "503": {
            "description": "Stored data unavailable. A valid empty feed explains unavailable status; retry after the Retry-After interval."
          }
        }
      },
      "head": {
        "summary": "Read public feed headers without a response body",
        "description": "Use the same query parameters as GET. Catalogue or feed validation, source permissions, status, MIME and caching rules are identical.",
        "parameters": [
          {
            "name": "action",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "feed-catalogue",
                "public-feed"
              ]
            },
            "example": "public-feed"
          },
          {
            "name": "kind",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "news",
                "events",
                "learning",
                "tenders"
              ],
              "default": "news"
            }
          },
          {
            "name": "format",
            "in": "query",
            "description": "ics is available only for events.",
            "schema": {
              "type": "string",
              "enum": [
                "rss",
                "atom",
                "json",
                "ics"
              ],
              "default": "rss"
            }
          },
          {
            "name": "lang",
            "in": "query",
            "description": "Interface and explanatory language. Original source headlines are not translated.",
            "schema": {
              "type": "string",
              "enum": [
                "de",
                "en"
              ],
              "default": "de"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum 24 for tenders. Maximum 50 for other collections. A bounded candidate window can return fewer items.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            }
          },
          {
            "name": "q",
            "in": "query",
            "description": "Optional literal substring, up to80 Unicode characters. News: own source headline only. Events: own source title/reported city. Tenders: title/buyer/city within the selected bounded public hub. Learning does not support a nonempty q. German umlaut/ss variants are folded. Wildcard characters are literal; no article bodies or private records are searched.",
            "schema": {
              "type": "string",
              "maxLength": 80
            },
            "example": "Digitalisierung"
          },
          {
            "name": "hub",
            "in": "query",
            "description": "Required for tenders only: an exact sector-*, state-* or city-* ID from the catalogue.",
            "schema": {
              "type": "string",
              "maxLength": 100
            },
            "example": "sector-it-dienstleistungen"
          },
          {
            "name": "source",
            "in": "query",
            "description": "Approved source ID or all; news, events and tenders only. Discover valid IDs in the catalogue.",
            "schema": {
              "type": "string",
              "default": "all",
              "maxLength": 64
            }
          },
          {
            "name": "topic",
            "in": "query",
            "description": "Advertised topic ID or all; news and events only.",
            "schema": {
              "type": "string",
              "default": "all",
              "maxLength": 64
            }
          },
          {
            "name": "region",
            "in": "query",
            "description": "Advertised German state, DE or EU code or all. Events permit German state codes only; news supports DE and EU.",
            "schema": {
              "type": "string",
              "default": "all",
              "maxLength": 20
            }
          },
          {
            "name": "If-None-Match",
            "in": "header",
            "description": "Conditional feed request using the ETag returned by a previous response. The catalogue has a request-time generatedAt and no ETag.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Same headers as GET; no response body."
          },
          "304": {
            "description": "Unchanged feed; no body."
          },
          "404": {
            "description": "Unknown hub."
          },
          "422": {
            "description": "Invalid selection."
          },
          "503": {
            "description": "Collection unavailable."
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "JsonFeed": {
        "type": "object",
        "required": [
          "version",
          "title",
          "items"
        ],
        "properties": {
          "version": {
            "const": "https://jsonfeed.org/version/1.1"
          },
          "title": {
            "type": "string"
          },
          "feed_url": {
            "type": "string",
            "format": "uri"
          },
          "home_page_url": {
            "type": "string",
            "format": "uri"
          },
          "language": {
            "type": "string"
          },
          "items": {
            "type": "array",
            "maxItems": 50,
            "items": {
              "type": "object",
              "required": [
                "id",
                "url",
                "title",
                "content_text"
              ],
              "properties": {
                "id": {
                  "type": "string"
                },
                "url": {
                  "type": "string",
                  "format": "uri"
                },
                "title": {
                  "type": "string"
                },
                "content_text": {
                  "type": "string",
                  "description": "Original attribution and metadata explanations, never the publisher article body."
                },
                "date_published": {
                  "type": "string",
                  "format": "date-time",
                  "description": "Only when a source exact publication instant is known."
                },
                "_vergabezone": {
                  "type": "object",
                  "description": "Source attribution, date precision, event dates or reported tender deadline, geography and permission qualifiers."
                }
              }
            }
          },
          "_vergabezone": {
            "type": "object",
            "description": "Selected filters, actual prepared/observed data state, bounded coverage and count unit. Source notices are not unique contracts; events are not publication dates."
          }
        }
      }
    }
  },
  "externalDocs": {
    "description": "Configure feeds and inspect real responses in the public feed centre.",
    "url": "https://vergabezone.de/feeds/"
  }
}
