{
 "openapi": "3.1.0",
 "info": {
  "title": "Friedrich & Clement — European public tenders API",
  "version": "beta-2026-10",
  "summary": "Open public tenders from 27 EU countries, including below-threshold notices from national portals that are not on TED.",
  "description": "The same data behind friedrich-clement.com: open procurement procedures from ~100 national and EU sources, deduplicated across portals, with English titles, CPV codes, deadlines, buyers and estimated values. Refreshed daily. Beta: GET /api/browse works without a key (anonymous results are capped at 20 per page); keys with higher limits are issued personally. Plans and terms: https://friedrich-clement.com/developers. Attribution required on the free tier.",
  "contact": {
   "name": "Friedrich & Clement",
   "email": "edition@friedrich-clement.com",
   "url": "https://friedrich-clement.com/developers"
  },
  "termsOfService": "https://friedrich-clement.com/legal/terms"
 },
 "servers": [
  {
   "url": "https://friedrich-clement.com"
  }
 ],
 "externalDocs": {
  "description": "Developer page, plans and key requests",
  "url": "https://friedrich-clement.com/developers"
 },
 "paths": {
  "/api/browse": {
   "get": {
    "operationId": "searchOpenTenders",
    "summary": "Search open tenders",
    "description": "Full-text search in any language plus filters. Returns open procedures, newest/most relevant first, with country facets.",
    "parameters": [
     {
      "name": "q",
      "in": "query",
      "schema": {
       "type": "string"
      },
      "description": "Free text in any EU language (matched against original and translated titles and descriptions)."
     },
     {
      "name": "country",
      "in": "query",
      "schema": {
       "type": "string"
      },
      "description": "Comma-separated ISO country codes, e.g. `DE,AT`."
     },
     {
      "name": "cpv",
      "in": "query",
      "schema": {
       "type": "string"
      },
      "description": "Comma-separated CPV codes or prefixes, e.g. `45,72`."
     },
     {
      "name": "sub",
      "in": "query",
      "schema": {
       "type": "string",
       "enum": [
        "all",
        "only",
        "no"
       ],
       "default": "all"
      },
      "description": "`only` = below-threshold only, `no` = above-threshold only."
     },
     {
      "name": "days",
      "in": "query",
      "schema": {
       "type": "integer"
      },
      "description": "Deadline within N days."
     },
     {
      "name": "minVal",
      "in": "query",
      "schema": {
       "type": "integer"
      },
      "description": "Minimum estimated value (EUR)."
     },
     {
      "name": "sort",
      "in": "query",
      "schema": {
       "type": "string",
       "enum": [
        "rang",
        "newest",
        "value",
        "wettbewerb"
       ],
       "default": "rang"
      },
      "description": "`rang` relevance, `newest` publication date, `value` estimated value, `wettbewerb` least competition first."
     },
     {
      "name": "lang",
      "in": "query",
      "schema": {
       "type": "string",
       "enum": [
        "de",
        "en",
        "fr",
        "pl",
        "es"
       ],
       "default": "de"
      },
      "description": "Language of `displayTitle`."
     },
     {
      "name": "plz",
      "in": "query",
      "schema": {
       "type": "string"
      },
      "description": "Postal code or place for a radius search (with `km`)."
     },
     {
      "name": "km",
      "in": "query",
      "schema": {
       "type": "integer"
      },
      "description": "Radius in km around `plz`."
     },
     {
      "name": "limit",
      "in": "query",
      "schema": {
       "type": "integer",
       "default": 50,
       "maximum": 100
      },
      "description": "Page size (anonymous requests are capped at 20)."
     },
     {
      "name": "offset",
      "in": "query",
      "schema": {
       "type": "integer",
       "default": 0
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Search results",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "total": {
           "type": "integer",
           "description": "Number of matching open procedures"
          },
          "capped": {
           "type": "boolean",
           "description": "True when the anonymous page cap applied"
          },
          "results": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Tender"
           }
          },
          "countryFacets": {
           "type": "array",
           "items": {
            "type": "object"
           }
          }
         }
        }
       }
      }
     }
    }
   }
  },
  "/api/tenders/{id}/eligibility": {
   "post": {
    "operationId": "checkEligibility",
    "summary": "Check a company profile against a tender's selection criteria",
    "description": "Send a company profile; receive a criterion-by-criterion verdict (met / unmet / unknown) against the tender's structured eForms selection criteria, with the buyer's own wording. National below-threshold notices often publish criteria only in documents; then status is `no-requirements-data`.",
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/CompanyProfile"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Eligibility verdict",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "status": {
           "type": "string",
           "enum": [
            "eligible",
            "blocked",
            "insufficient-data",
            "no-requirements-data"
           ]
          },
          "checks": {
           "type": "array",
           "items": {
            "type": "object",
            "properties": {
             "code": {
              "type": "string",
              "description": "Criterion code (e.g. eForms selection-criterion type)."
             },
             "label": {
              "type": "string"
             },
             "buyerText": {
              "type": [
               "string",
               "null"
              ],
              "description": "The buyer's own wording of the requirement, for verification."
             },
             "verdict": {
              "type": "string",
              "enum": [
               "met",
               "unmet",
               "unknown"
              ]
             },
             "reason": {
              "type": [
               "string",
               "null"
              ]
             }
            }
           }
          },
          "blockers": {
           "type": "array",
           "items": {
            "type": "object",
            "properties": {
             "code": {
              "type": "string",
              "description": "Criterion code (e.g. eForms selection-criterion type)."
             },
             "label": {
              "type": "string"
             },
             "buyerText": {
              "type": [
               "string",
               "null"
              ],
              "description": "The buyer's own wording of the requirement, for verification."
             },
             "verdict": {
              "type": "string",
              "enum": [
               "met",
               "unmet",
               "unknown"
              ]
             },
             "reason": {
              "type": [
               "string",
               "null"
              ]
             }
            }
           }
          },
          "unknowns": {
           "type": "array",
           "items": {
            "type": "object",
            "properties": {
             "code": {
              "type": "string",
              "description": "Criterion code (e.g. eForms selection-criterion type)."
             },
             "label": {
              "type": "string"
             },
             "buyerText": {
              "type": [
               "string",
               "null"
              ],
              "description": "The buyer's own wording of the requirement, for verification."
             },
             "verdict": {
              "type": "string",
              "enum": [
               "met",
               "unmet",
               "unknown"
              ]
             },
             "reason": {
              "type": [
               "string",
               "null"
              ]
             }
            }
           }
          },
          "guaranteeRequired": {
           "type": [
            "boolean",
            "null"
           ]
          },
          "guaranteeDescription": {
           "type": [
            "string",
            "null"
           ]
          },
          "submissionLanguages": {
           "type": [
            "array",
            "null"
           ],
           "items": {
            "type": "string"
           }
          },
          "questionDeadline": {
           "type": [
            "string",
            "null"
           ],
           "format": "date-time"
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Body is not a JSON company profile"
     }
    }
   }
  },
  "/api/feed": {
   "get": {
    "operationId": "sourceFreshness",
    "summary": "Source freshness and volumes",
    "description": "Per-source ingest status: last run, notices in the last 24/48 hours, live/paused.",
    "responses": {
     "200": {
      "description": "Feed status",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "at": {
           "type": "string",
           "format": "date-time"
          },
          "open": {
           "type": "integer",
           "description": "Open procedures in the index"
          },
          "feeds": {
           "type": "array",
           "items": {
            "type": "object",
            "properties": {
             "source": {
              "type": "string"
             },
             "label": {
              "type": "string"
             },
             "cc": {
              "type": "string"
             },
             "h24": {
              "type": "integer"
             },
             "h48": {
              "type": "integer"
             },
             "last": {
              "type": [
               "string",
               "null"
              ],
              "format": "date-time"
             },
             "status": {
              "type": "string"
             }
            }
           }
          }
         }
        }
       }
      }
     }
    }
   }
  }
 },
 "components": {
  "schemas": {
   "Tender": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string",
      "description": "Stable Friedrich & Clement tender id; detail page at https://friedrich-clement.com/tender/{id}"
     },
     "title": {
      "type": "string",
      "description": "Title in the original language"
     },
     "titleEn": {
      "type": [
       "string",
       "null"
      ],
      "description": "English title (machine translated where the source has none)"
     },
     "displayTitle": {
      "type": "string",
      "description": "Title in the requested `lang`"
     },
     "country": {
      "type": "string",
      "description": "ISO 3166-1 alpha-2"
     },
     "buyerName": {
      "type": [
       "string",
       "null"
      ]
     },
     "contractType": {
      "type": [
       "string",
       "null"
      ],
      "enum": [
       "WORKS",
       "SERVICES",
       "SUPPLIES",
       null
      ]
     },
     "publicationDate": {
      "type": [
       "string",
       "null"
      ],
      "format": "date-time"
     },
     "submissionDeadline": {
      "type": [
       "string",
       "null"
      ],
      "format": "date-time"
     },
     "isSubThreshold": {
      "type": [
       "boolean",
       "null"
      ],
      "description": "Below the EU publication threshold — usually not on TED"
     },
     "estimatedValue": {
      "type": [
       "string",
       "null"
      ],
      "description": "Decimal string in `currency`"
     },
     "currency": {
      "type": [
       "string",
       "null"
      ]
     },
     "source": {
      "type": "string",
      "description": "Source portal key (e.g. ted-de, placsp-es, pvl-it)"
     },
     "tedUrl": {
      "type": [
       "string",
       "null"
      ],
      "format": "uri"
     },
     "bieterMedian": {
      "type": [
       "number",
       "null"
      ],
      "description": "Median number of bids in comparable past procedures of this buyer/sector"
     },
     "bieterN": {
      "type": [
       "integer",
       "null"
      ],
      "description": "Number of past awards behind bieterMedian"
     }
    },
    "required": [
     "id",
     "title",
     "country",
     "source"
    ]
   },
   "CompanyProfile": {
    "type": "object",
    "required": [
     "certifications"
    ],
    "properties": {
     "certifications": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "e.g. `ISO 9001`, `ISO 27001`"
     },
     "turnoverEur": {
      "type": [
       "number",
       "null"
      ],
      "description": "Annual turnover in EUR"
     },
     "hasProfessionalInsurance": {
      "type": [
       "boolean",
       "null"
      ]
     },
     "insuranceCoverPersonEur": {
      "type": [
       "number",
       "null"
      ]
     },
     "insuranceCoverPropertyEur": {
      "type": [
       "number",
       "null"
      ]
     },
     "registeredTrade": {
      "type": [
       "boolean",
       "null"
      ]
     },
     "registeredProfessional": {
      "type": [
       "boolean",
       "null"
      ]
     },
     "references": {
      "type": "object",
      "description": "Comparable contracts in the last 3 years",
      "properties": {
       "works": {
        "type": [
         "integer",
         "null"
        ]
       },
       "services": {
        "type": [
         "integer",
         "null"
        ]
       },
       "supplies": {
        "type": [
         "integer",
         "null"
        ]
       }
      }
     },
     "headcount": {
      "type": [
       "integer",
       "null"
      ]
     },
     "canPostGuarantee": {
      "type": [
       "boolean",
       "null"
      ]
     }
    }
   }
  }
 }
}
