{
  "openapi": "3.1.0",
  "info": {
    "title": "Fokuus public information API",
    "version": "1.0.0",
    "description": "Free anonymous read-only access to published Fokuus information. No customer data, form submissions, scans, quotes or purchases. Developer documentation: https://fokuus.be/developers/. Rate limit: 120 requests per 60 seconds per client IP address, advertised in the RateLimit-Policy header; a 429 carries Retry-After. Versioning: the major version is in the URL path (/api/v1). Additive changes can ship within v1; breaking changes only in a new major version. Deprecation is announced with the Deprecation header (RFC 9745) and the shutdown date with the Sunset header (RFC 8594), at least 90 days apart. Deprecation and sunset policy: https://fokuus.be/docs/versioning/",
    "contact": {
      "url": "https://fokuus.be/contact/"
    }
  },
  "externalDocs": {
    "description": "Versioning and deprecation policy",
    "url": "https://fokuus.be/docs/versioning/"
  },
  "servers": [
    {
      "url": "https://fokuus.be"
    }
  ],
  "security": [],
  "paths": {
    "/api/v1": {
      "get": {
        "operationId": "getApiIndex",
        "summary": "Discover public API endpoints, lifecycle and rate limit",
        "description": "Discover public API endpoints, lifecycle and rate limit",
        "security": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/Link"
              },
              "Deprecation": {
                "$ref": "#/components/headers/Deprecation"
              },
              "Sunset": {
                "$ref": "#/components/headers/Sunset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input; inspect error.hint",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached. Wait for Retry-After seconds before retrying.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/organization": {
      "get": {
        "operationId": "getOrganization",
        "summary": "Read public business and contact information",
        "description": "Read public business and contact information",
        "security": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/Link"
              },
              "Deprecation": {
                "$ref": "#/components/headers/Deprecation"
              },
              "Sunset": {
                "$ref": "#/components/headers/Sunset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Organization"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input; inspect error.hint",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached. Wait for Retry-After seconds before retrying.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/services": {
      "get": {
        "operationId": "listServices",
        "summary": "List services with canonical source URLs",
        "description": "List services with canonical source URLs",
        "security": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/Link"
              },
              "Deprecation": {
                "$ref": "#/components/headers/Deprecation"
              },
              "Sunset": {
                "$ref": "#/components/headers/Sunset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Services"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input; inspect error.hint",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached. Wait for Retry-After seconds before retrying.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/pricing": {
      "get": {
        "operationId": "getPricing",
        "summary": "Read published prices excluding VAT; starting prices are not project quotes",
        "description": "Read published prices excluding VAT; starting prices are not project quotes",
        "security": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/Link"
              },
              "Deprecation": {
                "$ref": "#/components/headers/Deprecation"
              },
              "Sunset": {
                "$ref": "#/components/headers/Sunset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Pricing"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input; inspect error.hint",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached. Wait for Retry-After seconds before retrying.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/search": {
      "get": {
        "operationId": "searchPages",
        "summary": "Search published website pages",
        "description": "Search published website pages",
        "security": [],
        "parameters": [
          {
            "in": "query",
            "name": "q",
            "required": true,
            "description": "Search terms, no personal data",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            }
          },
          {
            "in": "query",
            "name": "limit",
            "description": "Maximum number of results",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 20,
              "default": 5
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/Link"
              },
              "Deprecation": {
                "$ref": "#/components/headers/Deprecation"
              },
              "Sunset": {
                "$ref": "#/components/headers/Sunset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Search"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input; inspect error.hint",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached. Wait for Retry-After seconds before retrying.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/ask": {
      "post": {
        "operationId": "askPublicContent",
        "summary": "Search public pages with NLWeb",
        "description": "Literal keyword search over published pages, not generated advice. NLWeb 0.55 JSON or SSE start/result/complete. No external requests.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "query"
                ],
                "properties": {
                  "query": {
                    "type": "object",
                    "required": [
                      "text"
                    ],
                    "properties": {
                      "text": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 200
                      }
                    }
                  },
                  "prefer": {
                    "type": "object",
                    "properties": {
                      "streaming": {
                        "type": "boolean",
                        "default": false
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "NLWeb answer or NO_RESULTS failure; optional SSE stream",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "Link": {
                "$ref": "#/components/headers/Link"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "_meta"
                  ],
                  "properties": {
                    "_meta": {
                      "type": "object",
                      "required": [
                        "response_type",
                        "version"
                      ],
                      "properties": {
                        "response_type": {
                          "type": "string",
                          "enum": [
                            "answer",
                            "failure"
                          ]
                        },
                        "version": {
                          "type": "string",
                          "const": "0.55"
                        }
                      }
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Invalid query",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached. Wait for Retry-After seconds before retrying.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/pages": {
      "get": {
        "operationId": "getPage",
        "summary": "Read one known public page as Markdown",
        "description": "Read one known public page as Markdown",
        "security": [],
        "parameters": [
          {
            "in": "query",
            "name": "url",
            "required": true,
            "description": "Canonical Fokuus page URL from search results",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Link": {
                "$ref": "#/components/headers/Link"
              },
              "Deprecation": {
                "$ref": "#/components/headers/Deprecation"
              },
              "Sunset": {
                "$ref": "#/components/headers/Sunset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Page"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input; inspect error.hint",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached. Wait for Retry-After seconds before retrying.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Organization": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "contactUrl": {
            "type": "string",
            "format": "uri"
          },
          "address": {
            "type": "object",
            "properties": {
              "streetAddress": {
                "type": "string"
              },
              "addressLocality": {
                "type": "string"
              },
              "postalCode": {
                "type": "string"
              },
              "addressCountry": {
                "type": "string"
              }
            }
          }
        },
        "required": [
          "name",
          "description",
          "url",
          "contactUrl",
          "address"
        ]
      },
      "Services": {
        "type": "object",
        "properties": {
          "services": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "description": {
                  "type": "string"
                },
                "url": {
                  "type": "string",
                  "format": "uri"
                }
              },
              "required": [
                "id",
                "name",
                "description",
                "url"
              ]
            }
          }
        },
        "required": [
          "services"
        ]
      },
      "Pricing": {
        "type": "object",
        "properties": {
          "currency": {
            "type": "string",
            "const": "EUR"
          },
          "vatIncluded": {
            "type": "boolean",
            "const": false
          },
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "amount": {
                  "type": "number"
                },
                "unit": {
                  "type": "string"
                },
                "qualifier": {
                  "type": "string"
                },
                "url": {
                  "type": "string",
                  "format": "uri"
                }
              },
              "required": [
                "id",
                "name",
                "amount",
                "unit",
                "qualifier",
                "url"
              ]
            }
          },
          "notes": {
            "type": "string"
          }
        },
        "required": [
          "currency",
          "vatIncluded",
          "items",
          "notes"
        ]
      },
      "Page": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri"
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "markdown": {
            "type": "string"
          },
          "language": {
            "type": "string",
            "enum": [
              "nl",
              "en"
            ]
          }
        },
        "required": [
          "url",
          "title",
          "description",
          "markdown",
          "language"
        ]
      },
      "Search": {
        "type": "object",
        "properties": {
          "query": {
            "type": "string"
          },
          "results": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "url": {
                  "type": "string",
                  "format": "uri"
                },
                "title": {
                  "type": "string"
                },
                "description": {
                  "type": "string"
                },
                "language": {
                  "type": "string"
                }
              },
              "required": [
                "url",
                "title",
                "description",
                "language"
              ]
            }
          }
        },
        "required": [
          "query",
          "results"
        ]
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "hint": {
                "type": "string"
              }
            },
            "required": [
              "code",
              "message",
              "hint"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "Problem": {
        "type": "object",
        "required": [
          "type",
          "title",
          "status",
          "error"
        ],
        "properties": {
          "type": {
            "type": "string",
            "format": "uri"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer",
            "const": 429
          },
          "detail": {
            "type": "string"
          },
          "violated-policies": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "hint": {
                "type": "string"
              }
            },
            "required": [
              "code",
              "message",
              "hint"
            ]
          }
        }
      }
    },
    "headers": {
      "RateLimit-Policy": {
        "description": "Quota policy on every response: 120 requests per 60 seconds per client IP address.",
        "schema": {
          "type": "string",
          "examples": [
            "\"per-ip\";q=120;w=60"
          ]
        }
      },
      "RateLimit": {
        "description": "Current service limit for the policy (r = remaining requests, t = seconds until the window resets). Sent on non-cacheable responses and on 429; ignore it on responses served from a cache.",
        "schema": {
          "type": "string",
          "examples": [
            "\"per-ip\";r=0;t=42"
          ]
        }
      },
      "Retry-After": {
        "description": "Seconds to wait before retrying after a 429.",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      },
      "Link": {
        "description": "Includes rel=\"service-desc\" (this document) and rel=\"deprecation\" and rel=\"sunset\" pointing to the versioning and deprecation policy at https://fokuus.be/docs/versioning/.",
        "schema": {
          "type": "string"
        }
      },
      "Deprecation": {
        "description": "RFC 9745 deprecation date as a structured-field Date (for example @1798761600). Not sent while API v1 is current; sent on every response of a version once its deprecation is announced.",
        "schema": {
          "type": "string"
        }
      },
      "Sunset": {
        "description": "RFC 8594 HTTP-date after which the resource stops working. Sent only after a sunset date is set, at least 90 days after the deprecation announcement.",
        "schema": {
          "type": "string"
        }
      }
    }
  }
}
