Graph RAG Backend

Minimal developer portal summarising every public endpoint, request contract and sample payload so you can integrate quickly.

GET /health Test this endpoint →

Lightweight probe used by load balancers and monitoring systems to confirm the service is reachable.

Request Schema
{
  "type": "object",
  "properties": {},
  "required": []
}
Response Schema
{
  "type": "object",
  "required": [
    "status",
    "timestamp",
    "deployment"
  ],
  "properties": {
    "status": {
      "type": "string",
      "enum": [
        "healthy"
      ]
    },
    "timestamp": {
      "type": "string",
      "format": "date-time",
      "description": "ISO 8601 timestamp"
    },
    "deployment": {
      "type": "object",
      "required": [
        "date",
        "environment",
        "version"
      ],
      "properties": {
        "date": {
          "type": "string",
          "description": "Deployment timestamp (YYYY-MM-DD HH:mm:ss)"
        },
        "environment": {
          "type": "string",
          "enum": [
            "prod",
            "acc",
            "local"
          ],
          "description": "Deployment environment"
        },
        "version": {
          "type": "string",
          "pattern": "^v\\d{8}$",
          "description": "Deployment version (vMMddHHmm format)"
        },
        "platform": {
          "type": "string",
          "enum": [
            "azure",
            "docker",
            "kubernetes",
            "aws",
            "local"
          ],
          "description": "Runtime platform"
        },
        "instance": {
          "type": "string",
          "description": "Azure instance ID or local identifier"
        },
        "commit": {
          "type": "string",
          "description": "Git commit hash (short)"
        },
        "build": {
          "type": "string",
          "description": "Build number"
        }
      }
    }
  }
}
Example Request
GET https://your-api.example.com/health
Example Response
{
  "status": "healthy",
  "timestamp": "2025-12-17T10:52:27.112Z",
  "deployment": {
    "date": "2025-12-17 11:45:00",
    "environment": "acc",
    "version": "v12171145",
    "platform": "azure",
    "instance": "0-webapp-1234",
    "commit": "a3f9c821",
    "build": "unknown"
  }
}

GET /query-stream/ Test this endpoint →

Progressive query endpoint using Server-Sent Events (SSE) for real-time result streaming. Returns results in 4 stages: search → reranking → answer → done. Each stage is delivered as soon as it completes, providing faster time-to-first-result compared to the standard /query/ endpoint.

Request Schema
{
  "type": "object",
  "properties": {
    "question": {
      "type": "string",
      "description": "User's question (query parameter)"
    },
    "api_key": {
      "type": "string",
      "description": "Client API key (query parameter)"
    },
    "requestID": {
      "type": "string",
      "description": "Optional request ID for tracking (query parameter)"
    }
  },
  "required": [
    "question",
    "api_key"
  ]
}
Response Schema
{
  "type": "object",
  "title": "Server-Sent Events Stream",
  "description": "EventSource stream with 4 progressive events",
  "properties": {
    "event_1_search_results": {
      "type": "object",
      "description": "First event (~500ms): Initial search results",
      "properties": {
        "event": {
          "type": "string",
          "enum": [
            "search_results"
          ]
        },
        "data": {
          "type": "object",
          "properties": {
            "request_id": {
              "type": "string"
            },
            "query_id": {
              "type": "integer",
              "description": "Query log ID for feedback"
            },
            "stage": {
              "type": "string",
              "enum": [
                "search"
              ]
            },
            "timestamp": {
              "type": "string",
              "format": "date-time"
            },
            "pages": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "title": {
                    "type": "string"
                  },
                  "url": {
                    "type": "string"
                  },
                  "score": {
                    "type": "number"
                  },
                  "snippet": {
                    "type": "string"
                  },
                  "ai_page_purpose": {
                    "type": "string"
                  }
                }
              }
            },
            "total_found": {
              "type": "integer"
            },
            "search_time_ms": {
              "type": "number"
            },
            "search_method": {
              "type": "string",
              "enum": [
                "vector",
                "keyword"
              ]
            }
          }
        }
      }
    },
    "event_2_reranked_results": {
      "type": "object",
      "description": "Second event (~2s): LLM-reranked results",
      "properties": {
        "event": {
          "type": "string",
          "enum": [
            "reranked_results"
          ]
        },
        "data": {
          "type": "object",
          "properties": {
            "request_id": {
              "type": "string"
            },
            "query_id": {
              "type": "integer"
            },
            "stage": {
              "type": "string",
              "enum": [
                "reranking"
              ]
            },
            "timestamp": {
              "type": "string"
            },
            "rankings": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "page_id": {
                    "type": "string"
                  },
                  "url": {
                    "type": "string"
                  },
                  "old_rank": {
                    "type": "integer"
                  },
                  "new_rank": {
                    "type": "integer"
                  },
                  "llm_score": {
                    "type": "number"
                  },
                  "is_relevant": {
                    "type": "boolean"
                  }
                }
              }
            },
            "reranking_time_ms": {
              "type": "number"
            },
            "model_used": {
              "type": "string"
            }
          }
        }
      }
    },
    "event_3_final_answer": {
      "type": "object",
      "description": "Third event (~3-5s): Final answer with safety checks",
      "properties": {
        "event": {
          "type": "string",
          "enum": [
            "final_answer"
          ]
        },
        "data": {
          "type": "object",
          "properties": {
            "request_id": {
              "type": "string"
            },
            "query_id": {
              "type": "integer"
            },
            "stage": {
              "type": "string",
              "enum": [
                "complete"
              ]
            },
            "timestamp": {
              "type": "string"
            },
            "answer": {
              "type": "object",
              "properties": {
                "text": {
                  "type": "string"
                },
                "sources": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "confidence": {
                  "type": "number"
                }
              }
            },
            "safety_checks": {
              "type": "object",
              "properties": {
                "content_safe": {
                  "type": "boolean"
                },
                "relevant": {
                  "type": "boolean"
                },
                "question_type": {
                  "type": "string"
                },
                "requires_human": {
                  "type": "boolean"
                },
                "is_bot_inquiry": {
                  "type": "boolean"
                }
              }
            },
            "modification_info": {
              "type": "object",
              "description": "Answer modification metadata",
              "properties": {
                "llm_answer_used": {
                  "type": "boolean"
                },
                "modification_reason": {
                  "type": "string",
                  "nullable": true
                },
                "preanswer_used": {
                  "type": "string",
                  "nullable": true
                }
              }
            },
            "total_time_ms": {
              "type": "number"
            },
            "tokens_used": {
              "type": "object",
              "properties": {
                "prompt": {
                  "type": "integer"
                },
                "completion": {
                  "type": "integer"
                },
                "total": {
                  "type": "integer"
                }
              }
            }
          }
        }
      }
    },
    "event_4_done": {
      "type": "object",
      "description": "Fourth event: Completion signal",
      "properties": {
        "event": {
          "type": "string",
          "enum": [
            "done"
          ]
        },
        "data": {
          "type": "object",
          "properties": {
            "request_id": {
              "type": "string"
            },
            "query_id": {
              "type": "integer"
            },
            "status": {
              "type": "string",
              "enum": [
                "completed"
              ]
            },
            "timestamp": {
              "type": "string"
            },
            "total_time_ms": {
              "type": "number"
            }
          }
        }
      }
    }
  }
}
Example Request
GET https://your-api.example.com/query-stream/?question=Wat+moet+ik+meenemen+voor+een+knieoperatie&api_key=YOUR_CLIENT_API_KEY&requestID=stream-001
Example Response
{
  "event_1": {
    "event": "search_results",
    "data": {
      "request_id": "stream-001",
      "query_id": 12890,
      "stage": "search",
      "pages": [
        {
          "id": "page-1",
          "title": "Voorbereiding opname",
          "url": "https://rivierenland.nl/opname/voorbereiding",
          "score": 0.82,
          "snippet": "Informatie over voorbereiding op uw operatie"
        }
      ],
      "total_found": 5,
      "search_time_ms": 487.32,
      "search_method": "vector"
    }
  },
  "event_2": {
    "event": "reranked_results",
    "data": {
      "request_id": "stream-001",
      "query_id": 12890,
      "stage": "reranking",
      "rankings": [
        {
          "page_id": "page-1",
          "url": "https://rivierenland.nl/opname/voorbereiding",
          "old_rank": 1,
          "new_rank": 1,
          "llm_score": 0.95,
          "is_relevant": true
        }
      ],
      "reranking_time_ms": 1845.67,
      "model_used": "gpt-4o-mini"
    }
  },
  "event_3": {
    "event": "final_answer",
    "data": {
      "request_id": "stream-001",
      "query_id": 12890,
      "stage": "complete",
      "answer": {
        "text": "Neem uw legitimatiebewijs, medicatieoverzicht en afsprakenkaart mee...",
        "sources": [
          "https://rivierenland.nl/opname/voorbereiding"
        ],
        "confidence": 0.85
      },
      "safety_checks": {
        "content_safe": true,
        "relevant": true,
        "question_type": "INFORMATION",
        "requires_human": false,
        "is_bot_inquiry": false
      },
      "modification_info": {
        "llm_answer_used": true,
        "modification_reason": null,
        "preanswer_used": null
      },
      "total_time_ms": 3245.89,
      "tokens_used": {
        "prompt": 1250,
        "completion": 180,
        "total": 1430
      }
    }
  },
  "event_4": {
    "event": "done",
    "data": {
      "request_id": "stream-001",
      "query_id": 12890,
      "status": "completed",
      "total_time_ms": 3245.89
    }
  }
}

POST /upload/

Ingest a single page into the vector store. Existing embeddings are overwritten unless `force_update` is false.

Request Schema
{
  "$defs": {
    "ContentBlock": {
      "description": "Model for a content block within a page.\n\nCRITICAL: The 'embed' field has TWO DISTINCT PURPOSES:\n\n1. INDEXING PHASE (importer.py):\n   - embed=True:  Create vector embedding in langchain_pg_embedding table\n   - embed=False: Store in documents table ONLY (no vector embedding)\n\n2. RETRIEVAL PHASE (keyword.py, vector.py, prompt_generator.py):\n   - The embed field MUST BE IGNORED completely during:\n     * BM25 keyword search content extraction\n     * Vector search result processing\n     * LLM prompt generation\n   - ALL blocks should be included regardless of embed value\n\nNEVER skip blocks based on embed=False during retrieval/search operations!",
      "properties": {
        "index": {
          "default": 0,
          "description": "Order index of the content block",
          "title": "Index",
          "type": "integer"
        },
        "titel": {
          "default": "De titel van de content block",
          "description": "Title of the content block",
          "title": "Titel",
          "type": "string"
        },
        "content": {
          "default": "De inhoud van de content block",
          "description": "Content text",
          "title": "Content",
          "type": "string"
        },
        "embed": {
          "default": true,
          "description": "Controls vector embedding creation during INDEXING only. True=create embedding, False=no embedding (context-only). MUST BE IGNORED during retrieval/search/prompt generation!",
          "title": "Embed",
          "type": "boolean"
        },
        "property_name": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Source property name from Umbraco JSON for context labeling",
          "title": "Property Name"
        }
      },
      "title": "ContentBlock",
      "type": "object"
    }
  },
  "description": "Request model for uploading/updating page content.\n\nSupports both legacy URL-based identification and modern Umbraco GUID-based identification.\nThe umbraco_id field is preferred when available as it provides stable, unique identification\nacross URL changes and redirects.",
  "properties": {
    "api_key": {
      "default": "PAgOD9hk1uOsifLzz5XuTzLRIgfDCSf3",
      "description": "API key for authentication",
      "title": "Api Key",
      "type": "string"
    },
    "umbraco_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Stable Umbraco node GUID (preferred identifier). Use this for reliable page identification.",
      "examples": [
        "f4762b01-e4b3-47bf-b030-67e5fb8dc08f"
      ],
      "title": "Umbraco Id"
    },
    "url": {
      "description": "URL path of the page (required for display, fallback for legacy)",
      "example": "/behandelingen/operatie-aan-de-stembanden/",
      "title": "Url",
      "type": "string"
    },
    "title": {
      "description": "Page title",
      "example": "Operatie aan de stembanden",
      "title": "Title",
      "type": "string"
    },
    "summary": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Page summary",
      "title": "Summary"
    },
    "content": {
      "description": "List of content blocks",
      "items": {
        "$ref": "#/$defs/ContentBlock"
      },
      "title": "Content",
      "type": "array"
    },
    "categories": {
      "description": "Page categories",
      "items": {
        "type": "string"
      },
      "title": "Categories",
      "type": "array"
    },
    "force_update": {
      "default": true,
      "description": "Force update even if page exists",
      "title": "Force Update",
      "type": "boolean"
    },
    "seoIndex": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "SEO index value",
      "title": "Seoindex"
    },
    "contentType": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Content type",
      "title": "Contenttype"
    },
    "createDate": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Creation date",
      "title": "Createdate"
    },
    "updateDate": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Last update date",
      "title": "Updatedate"
    },
    "publicationDate": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Publication date",
      "title": "Publicationdate"
    },
    "snippet": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Short snippet",
      "title": "Snippet"
    },
    "status": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Page status",
      "title": "Status"
    },
    "pageSummary": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Page summary",
      "title": "Pagesummary"
    },
    "ai_content_description": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "AI-generated content inventory (200-300 chars)",
      "title": "Ai Content Description"
    },
    "ai_page_purpose": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "AI-generated page purpose/utility (50-100 chars)",
      "title": "Ai Page Purpose"
    },
    "ai_data_extraction": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "AI-extracted structured data from page content",
      "title": "Ai Data Extraction"
    },
    "full_page_content": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Clean combined page content without markdown titles or duplicates (for LLM prompts)",
      "title": "Full Page Content"
    },
    "keywords": {
      "anyOf": [
        {
          "additionalProperties": true,
          "type": "object"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Unified keywords structure: {'primary': [...], 'secondary': [...], 'hidden_intent': [...], 'umbraco': [...]}",
      "title": "Keywords"
    },
    "u_facets": {
      "anyOf": [
        {
          "items": {
            "type": "string"
          },
          "type": "array"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Umbraco facets",
      "title": "U Facets"
    },
    "u_boost": {
      "anyOf": [
        {
          "type": "number"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Search boost value",
      "title": "U Boost"
    },
    "u_indexable": {
      "anyOf": [
        {
          "type": "boolean"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Whether page is indexable and visible in search results",
      "title": "U Indexable"
    },
    "u_use_for_llm_answer": {
      "anyOf": [
        {
          "type": "boolean"
        },
        {
          "type": "null"
        }
      ],
      "default": true,
      "description": "Whether page content is used in LLM answer generation (defaults to True)",
      "title": "U Use For Llm Answer"
    },
    "is_nested_content": {
      "default": false,
      "description": "Whether this is nested content from a parent document",
      "title": "Is Nested Content",
      "type": "boolean"
    },
    "parent_umbraco_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Parent document's Umbraco GUID if this is nested content",
      "title": "Parent Umbraco Id"
    },
    "nested_property_name": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Property name that contains this nested content",
      "title": "Nested Property Name"
    },
    "metadata": {
      "additionalProperties": true,
      "description": "Auto-extracted metadata fields from content properties",
      "title": "Metadata",
      "type": "object"
    },
    "website": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Website identifier from route.startItem.path (e.g., 'rioned', 'rioolinfo')",
      "title": "Website"
    }
  },
  "required": [
    "url",
    "title",
    "content",
    "categories"
  ],
  "title": "UploadRequest",
  "type": "object"
}
Response Schema
{
  "type": "object",
  "required": [
    "status",
    "chunks"
  ],
  "properties": {
    "status": {
      "type": "string"
    },
    "chunks": {
      "type": "integer"
    }
  }
}
Example Request
{
  "api_key": "YOUR_CLIENT_API_KEY",
  "url": "/behandelingen/stembanden/",
  "title": "Operatie aan de stembanden",
  "categories": [
    "behandeling"
  ],
  "force_update": true,
  "content": [
    {
      "index": 0,
      "titel": "Intro",
      "content": "Deze pagina begeleidt pati\u00ebnten bij een stembandenoperatie."
    },
    {
      "index": 1,
      "titel": "Voorbereiding",
      "content": "Meld medicatiegebruik en nuchter blijven tot de operatie."
    }
  ]
}
Example Response
{
  "status": "uploaded",
  "chunks": 12
}

POST /add-extra-chunks/

Add supplemental chunks to an existing URL without touching previously embedded content.

Request Schema
{
  "$defs": {
    "ContentBlock": {
      "description": "Model for a content block within a page.\n\nCRITICAL: The 'embed' field has TWO DISTINCT PURPOSES:\n\n1. INDEXING PHASE (importer.py):\n   - embed=True:  Create vector embedding in langchain_pg_embedding table\n   - embed=False: Store in documents table ONLY (no vector embedding)\n\n2. RETRIEVAL PHASE (keyword.py, vector.py, prompt_generator.py):\n   - The embed field MUST BE IGNORED completely during:\n     * BM25 keyword search content extraction\n     * Vector search result processing\n     * LLM prompt generation\n   - ALL blocks should be included regardless of embed value\n\nNEVER skip blocks based on embed=False during retrieval/search operations!",
      "properties": {
        "index": {
          "default": 0,
          "description": "Order index of the content block",
          "title": "Index",
          "type": "integer"
        },
        "titel": {
          "default": "De titel van de content block",
          "description": "Title of the content block",
          "title": "Titel",
          "type": "string"
        },
        "content": {
          "default": "De inhoud van de content block",
          "description": "Content text",
          "title": "Content",
          "type": "string"
        },
        "embed": {
          "default": true,
          "description": "Controls vector embedding creation during INDEXING only. True=create embedding, False=no embedding (context-only). MUST BE IGNORED during retrieval/search/prompt generation!",
          "title": "Embed",
          "type": "boolean"
        },
        "property_name": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Source property name from Umbraco JSON for context labeling",
          "title": "Property Name"
        }
      },
      "title": "ContentBlock",
      "type": "object"
    }
  },
  "description": "Request model for uploading/updating page content.\n\nSupports both legacy URL-based identification and modern Umbraco GUID-based identification.\nThe umbraco_id field is preferred when available as it provides stable, unique identification\nacross URL changes and redirects.",
  "properties": {
    "api_key": {
      "default": "PAgOD9hk1uOsifLzz5XuTzLRIgfDCSf3",
      "description": "API key for authentication",
      "title": "Api Key",
      "type": "string"
    },
    "umbraco_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Stable Umbraco node GUID (preferred identifier). Use this for reliable page identification.",
      "examples": [
        "f4762b01-e4b3-47bf-b030-67e5fb8dc08f"
      ],
      "title": "Umbraco Id"
    },
    "url": {
      "description": "URL path of the page (required for display, fallback for legacy)",
      "example": "/behandelingen/operatie-aan-de-stembanden/",
      "title": "Url",
      "type": "string"
    },
    "title": {
      "description": "Page title",
      "example": "Operatie aan de stembanden",
      "title": "Title",
      "type": "string"
    },
    "summary": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Page summary",
      "title": "Summary"
    },
    "content": {
      "description": "List of content blocks",
      "items": {
        "$ref": "#/$defs/ContentBlock"
      },
      "title": "Content",
      "type": "array"
    },
    "categories": {
      "description": "Page categories",
      "items": {
        "type": "string"
      },
      "title": "Categories",
      "type": "array"
    },
    "force_update": {
      "default": true,
      "description": "Force update even if page exists",
      "title": "Force Update",
      "type": "boolean"
    },
    "seoIndex": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "SEO index value",
      "title": "Seoindex"
    },
    "contentType": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Content type",
      "title": "Contenttype"
    },
    "createDate": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Creation date",
      "title": "Createdate"
    },
    "updateDate": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Last update date",
      "title": "Updatedate"
    },
    "publicationDate": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Publication date",
      "title": "Publicationdate"
    },
    "snippet": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Short snippet",
      "title": "Snippet"
    },
    "status": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Page status",
      "title": "Status"
    },
    "pageSummary": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Page summary",
      "title": "Pagesummary"
    },
    "ai_content_description": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "AI-generated content inventory (200-300 chars)",
      "title": "Ai Content Description"
    },
    "ai_page_purpose": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "AI-generated page purpose/utility (50-100 chars)",
      "title": "Ai Page Purpose"
    },
    "ai_data_extraction": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "AI-extracted structured data from page content",
      "title": "Ai Data Extraction"
    },
    "full_page_content": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Clean combined page content without markdown titles or duplicates (for LLM prompts)",
      "title": "Full Page Content"
    },
    "keywords": {
      "anyOf": [
        {
          "additionalProperties": true,
          "type": "object"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Unified keywords structure: {'primary': [...], 'secondary': [...], 'hidden_intent': [...], 'umbraco': [...]}",
      "title": "Keywords"
    },
    "u_facets": {
      "anyOf": [
        {
          "items": {
            "type": "string"
          },
          "type": "array"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Umbraco facets",
      "title": "U Facets"
    },
    "u_boost": {
      "anyOf": [
        {
          "type": "number"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Search boost value",
      "title": "U Boost"
    },
    "u_indexable": {
      "anyOf": [
        {
          "type": "boolean"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Whether page is indexable and visible in search results",
      "title": "U Indexable"
    },
    "u_use_for_llm_answer": {
      "anyOf": [
        {
          "type": "boolean"
        },
        {
          "type": "null"
        }
      ],
      "default": true,
      "description": "Whether page content is used in LLM answer generation (defaults to True)",
      "title": "U Use For Llm Answer"
    },
    "is_nested_content": {
      "default": false,
      "description": "Whether this is nested content from a parent document",
      "title": "Is Nested Content",
      "type": "boolean"
    },
    "parent_umbraco_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Parent document's Umbraco GUID if this is nested content",
      "title": "Parent Umbraco Id"
    },
    "nested_property_name": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Property name that contains this nested content",
      "title": "Nested Property Name"
    },
    "metadata": {
      "additionalProperties": true,
      "description": "Auto-extracted metadata fields from content properties",
      "title": "Metadata",
      "type": "object"
    },
    "website": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Website identifier from route.startItem.path (e.g., 'rioned', 'rioolinfo')",
      "title": "Website"
    }
  },
  "required": [
    "url",
    "title",
    "content",
    "categories"
  ],
  "title": "UploadRequest",
  "type": "object"
}
Response Schema
{
  "type": "object",
  "required": [
    "success",
    "message",
    "chunks_added",
    "documents_update"
  ],
  "properties": {
    "success": {
      "type": "boolean"
    },
    "message": {
      "type": "string"
    },
    "chunks_added": {
      "type": "integer"
    },
    "documents_update": {
      "type": "object",
      "properties": {
        "success": {
          "type": "boolean"
        },
        "content_blocks_added": {
          "type": "integer"
        },
        "total_content_blocks": {
          "type": "integer"
        },
        "message": {
          "type": "string"
        }
      },
      "required": [
        "success",
        "content_blocks_added",
        "total_content_blocks"
      ]
    }
  }
}
Example Request
{
  "api_key": "YOUR_CLIENT_API_KEY",
  "url": "/specialismen/anesthesie/",
  "title": "Anesthesiologie",
  "categories": [
    "specialisme"
  ],
  "content": [
    {
      "index": 99,
      "titel": "Team",
      "content": "Maak kennis met het anesthesieteam en hun expertises."
    }
  ]
}
Example Response
{
  "success": true,
  "message": "Added 3 extra chunks",
  "chunks_added": 3,
  "documents_update": {
    "success": true,
    "content_blocks_added": 1,
    "total_content_blocks": 14
  }
}

POST /batch-upload/

Process every `batch_*.json` file mounted under `/app/data/rivierenland`. Intended for bulk re-index jobs.

Request Schema
{
  "type": "object",
  "properties": {
    "api_key": {
      "type": "string",
      "description": "Client API key passed as query parameter."
    }
  },
  "required": [
    "api_key"
  ]
}
Response Schema
{
  "type": "object",
  "required": [
    "total_files",
    "results"
  ],
  "properties": {
    "total_files": {
      "type": "integer"
    },
    "results": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "file": {
            "type": "string"
          },
          "processed": {
            "type": "integer"
          },
          "results": {
            "type": "array",
            "items": {
              "type": "object"
            }
          }
        },
        "required": [
          "file",
          "processed"
        ]
      }
    }
  }
}
Example Request
POST https://your-api.example.com/batch-upload/?api_key=YOUR_CLIENT_API_KEY
Example Response
{
  "total_files": 5,
  "results": [
    {
      "file": "/app/data/rivierenland/batch_0001.json",
      "processed": 42
    }
  ]
}

POST /create-keywords/

Runs keyword extraction for documents missing `keywords_primary` or `keywords_secondary`. Only processes the first 500 documents per call.

Request Schema
{
  "type": "object",
  "properties": {
    "api_key": {
      "type": "string",
      "description": "Client API key passed as query parameter."
    }
  },
  "required": [
    "api_key"
  ]
}
Response Schema
{
  "type": "object",
  "properties": {
    "message": {
      "type": "string"
    },
    "processed": {
      "type": "integer"
    },
    "failed": {
      "type": "integer"
    },
    "total_tokens_used": {
      "type": "number"
    },
    "cost_estimation": {
      "type": "number"
    },
    "statistics": {
      "type": "object",
      "additionalProperties": {
        "type": "integer"
      }
    },
    "remaining_documents": {
      "type": "integer"
    },
    "note": {
      "type": "string"
    }
  }
}
Example Request
POST https://your-api.example.com/create-keywords/?api_key=YOUR_CLIENT_API_KEY
Example Response
{
  "message": "Keyword generation completed (limited to 500 documents for testing)",
  "processed": 87,
  "failed": 3,
  "total_tokens_used": 12890,
  "statistics": {
    "total_documents": 540,
    "documents_with_both": 470
  }
}

POST /feedback/

Capture end-user feedback for previously answered queries so we can monitor quality.

Request Schema
{
  "description": "Request model for submitting feedback on a query response.",
  "properties": {
    "query_id": {
      "description": "ID van de query waarop feedback wordt gegeven",
      "title": "Query Id",
      "type": "integer"
    },
    "answer_quality": {
      "description": "Kwaliteit van het antwoord (0-5)",
      "maximum": 5,
      "minimum": 0,
      "title": "Answer Quality",
      "type": "integer"
    },
    "unwarranted_warning": {
      "default": false,
      "description": "Onterechte waarschuwing aanwezig",
      "title": "Unwarranted Warning",
      "type": "boolean"
    },
    "missing_warning": {
      "default": false,
      "description": "Missende waarschuwing",
      "title": "Missing Warning",
      "type": "boolean"
    },
    "comments": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Optionele opmerkingen",
      "title": "Comments"
    },
    "name": {
      "anyOf": [
        {
          "maxLength": 255,
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Naam van de feedback-gever (optioneel)",
      "title": "Name"
    },
    "api_key": {
      "description": "API key voor authenticatie",
      "title": "Api Key",
      "type": "string"
    }
  },
  "required": [
    "query_id",
    "answer_quality",
    "api_key"
  ],
  "title": "FeedbackRequest",
  "type": "object"
}
Response Schema
{
  "description": "Response model for feedback submission.",
  "properties": {
    "success": {
      "description": "Whether feedback was recorded successfully",
      "title": "Success",
      "type": "boolean"
    },
    "feedback_id": {
      "description": "ID of the created feedback record",
      "title": "Feedback Id",
      "type": "integer"
    },
    "message": {
      "description": "Status message",
      "title": "Message",
      "type": "string"
    }
  },
  "required": [
    "success",
    "feedback_id",
    "message"
  ],
  "title": "FeedbackResponse",
  "type": "object"
}
Example Request
{
  "query_id": 12873,
  "answer_quality": 4,
  "unwarranted_warning": false,
  "missing_warning": false,
  "comments": "Goed antwoord, graag iets meer over hersteltijd.",
  "api_key": "YOUR_CLIENT_API_KEY"
}
Example Response
{
  "success": true,
  "feedback_id": 987,
  "message": "Feedback successfully submitted"
}

POST /umbraco/batch-update/

Primary webhook that accepts batched content updates from Umbraco CMS and enqueues them for processing.

Request Schema
{
  "$defs": {
    "UmbracoItem": {
      "description": "Single item in the Umbraco update request.",
      "properties": {
        "UniqueIdentifier": {
          "description": "Unique identifier (Umbraco node key/GUID)",
          "example": "f4762b01-e4b3-47bf-b030-67e5fb8dc08f",
          "title": "Uniqueidentifier",
          "type": "string"
        },
        "Operation": {
          "default": "AddOrUpdate",
          "description": "Operation to perform on this item",
          "enum": [
            "AddOrUpdate",
            "Delete"
          ],
          "title": "Operation",
          "type": "string"
        },
        "Variants": {
          "description": "List of language variants for this item",
          "items": {
            "$ref": "#/$defs/UmbracoVariant"
          },
          "title": "Variants",
          "type": "array"
        }
      },
      "required": [
        "UniqueIdentifier",
        "Variants"
      ],
      "title": "UmbracoItem",
      "type": "object"
    },
    "UmbracoVariant": {
      "description": "Single variant within an Umbraco item (represents one language version).",
      "properties": {
        "Language": {
          "description": "Language in ISO format (e.g., 'en-us', 'nl-nl')",
          "example": "nl-nl",
          "title": "Language",
          "type": "string"
        },
        "Operation": {
          "default": "AddOrUpdate",
          "description": "Operation to perform on this variant",
          "enum": [
            "AddOrUpdate",
            "Delete"
          ],
          "title": "Operation",
          "type": "string"
        },
        "Facets": {
          "description": "Facets for categorization",
          "example": [
            "product",
            "treatment"
          ],
          "items": {
            "type": "string"
          },
          "title": "Facets",
          "type": "array"
        },
        "Url": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Full URL to the page (optional for Delete operations)",
          "example": "https://rivierenland.nl/behandelingen/gescheurde-achillespees/",
          "title": "Url"
        },
        "Path": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Path portion of the URL (required for Delete, optional for AddOrUpdate)",
          "example": "/behandelingen/gescheurde-achillespees/",
          "title": "Path"
        },
        "Keywords": {
          "description": "Keywords associated with the page",
          "example": [
            "achillespees",
            "orthopedics",
            "treatment"
          ],
          "items": {
            "type": "string"
          },
          "title": "Keywords",
          "type": "array"
        },
        "Boost": {
          "default": 0.0,
          "description": "Search boost value (0.0-10.0)",
          "example": 1.5,
          "maximum": 10.0,
          "minimum": 0.0,
          "title": "Boost",
          "type": "number"
        },
        "Indexable": {
          "default": true,
          "description": "Whether the page should be indexed and visible in search results",
          "example": true,
          "title": "Indexable",
          "type": "boolean"
        },
        "UseForLLMAnswer": {
          "anyOf": [
            {
              "type": "boolean"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Whether page content can be used in LLM answer generation. If not provided (None), inherits the value from Indexable field. Example: (Indexable=False, UseForLLMAnswer=None) \u2192 both become False",
          "example": true,
          "title": "Useforllmanswer"
        },
        "TimeRelevanceType": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "How should this content's time-based relevance be handled? Options: 'ai_decide' (let AI determine), 'decay_180d' (gradual fade over 180 days), 'relevancy_window' (hard expiry between fromDate and expireDate), 'always_relevant' (never expires, always shown). Defaults to 'ai_decide' if not provided.",
          "example": "decay_180d",
          "title": "Timerelevancetype"
        },
        "TimeRelevanceFromDate": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Start date for content relevance (ISO 8601 format: YYYY-MM-DD). Only used with 'relevancy_window' type. If null, content is active from publication.",
          "example": "2026-02-01",
          "title": "Timerelevancefromdate"
        },
        "TimeRelevanceExpireDate": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Expiry date for content relevance (ISO 8601 format: YYYY-MM-DD). Used with 'relevancy_window' type for hard cutoff. If null with 'decay_180d', content fades gradually with no hard expiry. Ignored with 'always_relevant' type.",
          "example": "2026-05-01",
          "title": "Timerelevanceexpiredate"
        }
      },
      "required": [
        "Language"
      ],
      "title": "UmbracoVariant",
      "type": "object"
    }
  },
  "description": "Request model for updating pages from Umbraco.\n\nBased on the exact Umbraco indexing schema.",
  "example": {
    "ApiKey": "PAgOD9hk1uOsifLzz5XuTzLRIgfDCSf3",
    "Items": [
      {
        "Operation": "AddOrUpdate",
        "UniqueIdentifier": "f4762b01-e4b3-47bf-b030-67e5fb8dc08f",
        "Variants": [
          {
            "Boost": 1.5,
            "Facets": [
              "treatment",
              "orthopedics"
            ],
            "Indexable": true,
            "Keywords": [
              "achillespees",
              "orthopedics"
            ],
            "Language": "nl-nl",
            "Operation": "AddOrUpdate",
            "Path": "/behandelingen/gescheurde-achillespees/",
            "TimeRelevanceExpireDate": null,
            "TimeRelevanceFromDate": "2026-02-01",
            "TimeRelevanceType": "decay_180d",
            "Url": "https://rivierenland.nl/behandelingen/gescheurde-achillespees/"
          }
        ]
      }
    ],
    "RequiresFullRebuild": false
  },
  "properties": {
    "Items": {
      "description": "List of items to process",
      "items": {
        "$ref": "#/$defs/UmbracoItem"
      },
      "title": "Items",
      "type": "array"
    },
    "RequiresFullRebuild": {
      "default": false,
      "description": "Whether a full index rebuild is required",
      "title": "Requiresfullrebuild",
      "type": "boolean"
    },
    "ApiKey": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Client API key for authentication (will be added by developer)",
      "example": "PAgOD9hk1uOsifLzz5XuTzLRIgfDCSf3",
      "title": "Apikey"
    }
  },
  "required": [
    "Items"
  ],
  "title": "UmbracoUpdateRequest",
  "type": "object"
}
Response Schema
{
  "$defs": {
    "UmbracoItemResult": {
      "description": "Result for a single processed item.",
      "properties": {
        "unique_identifier": {
          "description": "Umbraco unique identifier",
          "title": "Unique Identifier",
          "type": "string"
        },
        "language": {
          "description": "Language code",
          "title": "Language",
          "type": "string"
        },
        "url": {
          "description": "Page URL",
          "title": "Url",
          "type": "string"
        },
        "status": {
          "description": "Processing status (success/failed/skipped)",
          "title": "Status",
          "type": "string"
        },
        "message": {
          "description": "Status message",
          "title": "Message",
          "type": "string"
        },
        "chunks_created": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Number of chunks created",
          "title": "Chunks Created"
        },
        "error": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Error message if failed",
          "title": "Error"
        }
      },
      "required": [
        "unique_identifier",
        "language",
        "url",
        "status",
        "message"
      ],
      "title": "UmbracoItemResult",
      "type": "object"
    }
  },
  "description": "Response model for Umbraco batch update.",
  "properties": {
    "success": {
      "description": "Overall operation success",
      "title": "Success",
      "type": "boolean"
    },
    "total_items": {
      "description": "Total items in request",
      "title": "Total Items",
      "type": "integer"
    },
    "processed": {
      "description": "Successfully processed items",
      "title": "Processed",
      "type": "integer"
    },
    "skipped": {
      "description": "Skipped items",
      "title": "Skipped",
      "type": "integer"
    },
    "failed": {
      "description": "Failed items",
      "title": "Failed",
      "type": "integer"
    },
    "results": {
      "description": "Detailed results per item",
      "items": {
        "$ref": "#/$defs/UmbracoItemResult"
      },
      "title": "Results",
      "type": "array"
    },
    "message": {
      "description": "Overall status message",
      "title": "Message",
      "type": "string"
    }
  },
  "required": [
    "success",
    "total_items",
    "processed",
    "skipped",
    "failed",
    "message"
  ],
  "title": "UmbracoUpdateResponse",
  "type": "object"
}
Example Request
{
  "ApiKey": "PAgOD9hk1uOsifLzz5XuTzLRIgfDCSf3",
  "Items": [
    {
      "Operation": "AddOrUpdate",
      "UniqueIdentifier": "f4762b01-e4b3-47bf-b030-67e5fb8dc08f",
      "Variants": [
        {
          "Boost": 1.5,
          "Facets": [
            "treatment",
            "orthopedics"
          ],
          "Indexable": true,
          "Keywords": [
            "achillespees",
            "orthopedics"
          ],
          "Language": "nl-nl",
          "Operation": "AddOrUpdate",
          "Path": "/behandelingen/gescheurde-achillespees/",
          "TimeRelevanceExpireDate": null,
          "TimeRelevanceFromDate": "2026-02-01",
          "TimeRelevanceType": "decay_180d",
          "Url": "https://rivierenland.nl/behandelingen/gescheurde-achillespees/"
        }
      ]
    }
  ],
  "RequiresFullRebuild": false
}
Example Response
{
  "success": true,
  "total_items": 2,
  "processed": 0,
  "failed": 0,
  "message": "Queued 2 items for processing"
}

GET /umbraco/queue-stats/ Test this endpoint →

Retrieve a breakdown of items waiting in the Umbraco processing queue.

Request Schema
{
  "type": "object",
  "properties": {},
  "required": []
}
Response Schema
{
  "type": "object",
  "required": [
    "status",
    "queue",
    "total"
  ],
  "properties": {
    "status": {
      "type": "string"
    },
    "queue": {
      "type": "object",
      "description": "Counts per queue status",
      "additionalProperties": {
        "type": "integer"
      }
    },
    "total": {
      "type": "integer"
    }
  }
}
Example Request
GET https://your-api.example.com/umbraco/queue-stats/
Example Response
{
  "status": "success",
  "queue": {
    "pending": 5,
    "processing": 1,
    "failed": 0,
    "superseded": 2
  },
  "total": 8
}

GET /umbraco/health/ Test this endpoint →

Simple heartbeat endpoint confirming the Umbraco integration module is active.

Request Schema
{
  "type": "object",
  "properties": {},
  "required": []
}
Response Schema
{
  "type": "object",
  "required": [
    "status",
    "service",
    "endpoints"
  ],
  "properties": {
    "status": {
      "type": "string"
    },
    "service": {
      "type": "string"
    },
    "endpoints": {
      "type": "array",
      "items": {
        "type": "string"
      }
    }
  }
}
Example Request
GET https://your-api.example.com/umbraco/health/
Example Response
{
  "status": "healthy",
  "service": "umbraco-integration",
  "endpoints": [
    "/umbraco/batch-update/ (POST)",
    "/umbraco/queue-stats/ (GET)"
  ]
}

POST /admin/clear-cache/ Clear all caches →

Leeg de caches van de applicatie. Kies welke cache je wilt legen met de boolean parameters hieronder. Met all=true leeg je alles in één keer.

Request Schema
{
  "type": "object",
  "properties": {
    "prompts": {
      "type": "boolean",
      "default": false,
      "description": "Leeg de cache met LLM prompts (wat we aan de AI vragen)"
    },
    "field_detection": {
      "type": "boolean",
      "default": false,
      "description": "Leeg de cache met veldnamen die we herkennen in Umbraco"
    },
    "pricing": {
      "type": "boolean",
      "default": false,
      "description": "Leeg de cache met OpenAI prijzen per model"
    },
    "search_settings": {
      "type": "boolean",
      "default": false,
      "description": "Leeg de cache met zoekinstellingen per client (BM25, vector search, etc.)"
    },
    "client_lookup": {
      "type": "boolean",
      "default": false,
      "description": "Leeg de cache met client info (API keys, database namen, Umbraco URLs)"
    },
    "content_types": {
      "type": "boolean",
      "default": false,
      "description": "Leeg de cache met Umbraco content types per client (leegt BEIDE caches: lijst voor dropdowns + settings voor search results)"
    },
    "all": {
      "type": "boolean",
      "default": false,
      "description": "Leeg ALLE caches tegelijk"
    },
    "cache_type": {
      "type": "string",
      "enum": [
        "prompts",
        "all"
      ],
      "description": "DEPRECATED - gebruik de boolean parameters hierboven"
    }
  },
  "required": []
}
Response Schema
{
  "type": "object",
  "required": [
    "status",
    "message",
    "cleared_caches",
    "timing",
    "timestamp"
  ],
  "properties": {
    "status": {
      "type": "string"
    },
    "message": {
      "type": "string"
    },
    "cleared_caches": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "timing": {
      "type": "object",
      "description": "Execution time in milliseconds for each cache type",
      "properties": {
        "prompts_ms": {
          "type": "number"
        },
        "field_detection_ms": {
          "type": "number"
        },
        "pricing_ms": {
          "type": "number"
        },
        "search_settings_ms": {
          "type": "number"
        },
        "client_lookup_ms": {
          "type": "number"
        },
        "content_types_ms": {
          "type": "number"
        }
      }
    },
    "timestamp": {
      "type": "string",
      "format": "date-time"
    }
  }
}
Example Request
POST https://your-api.example.com/admin/clear-cache/?all=true
Example Response
{
  "status": "success",
  "message": "Cache cleared: prompts, field_detection, pricing, search_settings, client_lookup, content_types",
  "cleared_caches": [
    "prompts",
    "field_detection",
    "pricing",
    "search_settings",
    "client_lookup",
    "content_types"
  ],
  "timing": {
    "prompts_ms": 0.12,
    "field_detection_ms": 0.08,
    "pricing_ms": 1.45,
    "search_settings_ms": 0.05,
    "client_lookup_ms": 0.03,
    "content_types_ms": 0.02
  },
  "timestamp": "2025-12-08T16:42:00.123456"
}

GET /clients/ Test this endpoint →

Retrieve all active clients from the master database. Returns client configuration including display names, API keys, environments, and database names. Used by the frontend dropdown for client selection in advanced mode.

Request Schema
{
  "type": "object",
  "properties": {},
  "required": []
}
Response Schema
{
  "type": "array",
  "items": {
    "type": "object",
    "properties": {
      "name": {
        "type": "string",
        "description": "Internal client name (e.g., 'rivierenland', 'rioned')"
      },
      "displayName": {
        "type": "string",
        "description": "User-friendly display name with environment suffix"
      },
      "apiKey": {
        "type": "string",
        "description": "Client API key for authentication"
      },
      "environment": {
        "type": "string",
        "enum": [
          "prod",
          "acc",
          "test",
          "dev"
        ],
        "description": "Deployment environment"
      },
      "dbName": {
        "type": "string",
        "description": "PostgreSQL database name"
      },
      "websiteDomain": {
        "type": "string",
        "nullable": true,
        "description": "Client website domain (e.g., 'https://rivierenland.nl')"
      }
    },
    "required": [
      "name",
      "displayName",
      "apiKey",
      "environment",
      "dbName"
    ]
  }
}
Example Request
GET https://your-api.example.com/clients/
Example Response
[
  {
    "name": "rivierenland",
    "displayName": "Ziekenhuis Rivierenland",
    "apiKey": "PAgOD9hk1uOsifLzz5XuTzLRIgfDCSf3",
    "environment": "prod",
    "dbName": "aisearch-rivierenland-prod",
    "websiteDomain": "https://rivierenland.nl"
  },
  {
    "name": "rivierenland",
    "displayName": "Ziekenhuis Rivierenland (Acceptatie)",
    "apiKey": "test-acc-key-123",
    "environment": "acc",
    "dbName": "aisearch-rivierenland-acc",
    "websiteDomain": "https://acc.rivierenland.nl"
  },
  {
    "name": "rioned",
    "displayName": "RIOneD (Riolering)",
    "apiKey": "rioned-api-key-456",
    "environment": "prod",
    "dbName": "aisearch-rioned-prod",
    "websiteDomain": "https://rioned.org"
  }
]