{"openapi": "3.1.0", "info": {"title": "Locals BI Agent API", "version": "1.0.0", "description": "Alle data en berekeningen van het Locals-dashboard, voor agents.\n\nAuthenticatie: `Authorization: Bearer <token>` op elk endpoint (behalve /health en /openapi.json). Twee scopes: `read` en `write`.\n\nLocaties: locals-coffee, locals-brunch, flow-brunch, locals-to-go, locals-city.\n\nLet op: gebruik `/financials/pnl` voor winstvragen \u2014 losse kostentabellen optellen geeft andere cijfers dan het dashboard."}, "servers": [{"url": "https://dash.localscoffee.nl/api/v1"}], "components": {"securitySchemes": {"bearerAuth": {"type": "http", "scheme": "bearer"}}}, "security": [{"bearerAuth": []}], "tags": [{"name": "reference", "description": "Locaties, kostenmodel, datakwaliteit"}, {"name": "revenue", "description": "Kassaomzet, producten, uren, tafels"}, {"name": "labour", "description": "Loonkosten en roosters"}, {"name": "financials", "description": "P&L, kosten, cash, bank, accountant"}, {"name": "staff", "description": "Personeel en contracten"}, {"name": "ops", "description": "Alerts, managerrapporten, mail, audit"}, {"name": "marketing", "description": "Ads, Google Business, posts, budget"}, {"name": "reviews", "description": "Google-reviews"}, {"name": "reservations", "description": "Zenchef-reserveringen"}, {"name": "forecast", "description": "Omzetforecast en nauwkeurigheid"}, {"name": "targets", "description": "Doelstellingen en resultaten"}, {"name": "kpi", "description": "Kern-KPI's met vergelijking"}, {"name": "query", "description": "Vrije read-only query"}, {"name": "jobs", "description": "Sync-jobs starten"}], "paths": {"/locations": {"get": {"operationId": "locations", "summary": "Alle vestigingen met slug, kleur, Eitje-id en openingsdatum.", "description": "Alle vestigingen met slug, kleur, Eitje-id en openingsdatum.", "tags": ["reference"], "x-scope": "read", "parameters": [{"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/config": {"get": {"operationId": "config", "summary": "Kostenmodel, targets, loonkostdrempels, Exact-divisies en Lightspeed-sites.", "description": "Alle instellingen in \u00e9\u00e9n antwoord. Dit is de bron van waarheid voor elk kostenmodel-antwoord \u2014 het dashboard overschrijft zijn eigen JS-defaults hiermee bij het opstarten.", "tags": ["reference"], "x-scope": "read", "parameters": [], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/data-quality": {"get": {"operationId": "data_quality", "summary": "Versheid per integratie: hoe oud is de data van elke bron?", "description": "Versheid per integratie: hoe oud is de data van elke bron?", "tags": ["reference"], "x-scope": "read", "parameters": [], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/revenue/daily": {"get": {"operationId": "revenue_daily", "summary": "Kassaomzet per dag per locatie (Lightspeed): netto, bruto, orders, covers.", "description": "Kassaomzet per dag per locatie (Lightspeed): netto, bruto, orders, covers.", "tags": ["revenue"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": false, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": false, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "location_id", "in": "query", "required": false, "description": "Locatie, of 'all' voor alle vestigingen.", "schema": {"type": "string", "description": "Locatie, of 'all' voor alle vestigingen.", "enum": ["locals-coffee", "locals-brunch", "flow-brunch", "locals-to-go", "locals-city", "all"], "default": "all"}}, {"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/revenue/monthly": {"get": {"operationId": "revenue_monthly", "summary": "Maandomzet per locatie uit de Lightspeed-bedrijfsrapporten.", "description": "Maandomzet per locatie uit de Lightspeed-bedrijfsrapporten.", "tags": ["revenue"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": false, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": false, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "location_id", "in": "query", "required": false, "description": "Locatie, of 'all' voor alle vestigingen.", "schema": {"type": "string", "description": "Locatie, of 'all' voor alle vestigingen.", "enum": ["locals-coffee", "locals-brunch", "flow-brunch", "locals-to-go", "locals-city", "all"], "default": "all"}}, {"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/revenue/history": {"get": {"operationId": "revenue_history", "summary": "Historische dagomzet (legacy-import, v\u00f3\u00f3r de Lightspeed-koppeling).", "description": "Historische dagomzet (legacy-import, v\u00f3\u00f3r de Lightspeed-koppeling).", "tags": ["revenue"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": false, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": false, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "location_id", "in": "query", "required": false, "description": "Locatie, of 'all' voor alle vestigingen.", "schema": {"type": "string", "description": "Locatie, of 'all' voor alle vestigingen.", "enum": ["locals-coffee", "locals-brunch", "flow-brunch", "locals-to-go", "locals-city", "all"], "default": "all"}}, {"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/revenue/products": {"get": {"operationId": "revenue_products", "summary": "Verkochte producten per dag: aantal en netto-omzet per artikel.", "description": "Verkochte producten per dag: aantal en netto-omzet per artikel.", "tags": ["revenue"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": false, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": false, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "location_id", "in": "query", "required": false, "description": "Locatie, of 'all' voor alle vestigingen.", "schema": {"type": "string", "description": "Locatie, of 'all' voor alle vestigingen.", "enum": ["locals-coffee", "locals-brunch", "flow-brunch", "locals-to-go", "locals-city", "all"], "default": "all"}}, {"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/revenue/hourly": {"get": {"operationId": "revenue_hourly", "summary": "Kassaomzet per uur per dag (Amsterdamse lokale tijd).", "description": "Kassaomzet per uur per dag (Amsterdamse lokale tijd).", "tags": ["revenue"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": false, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": false, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "location_id", "in": "query", "required": false, "description": "Locatie, of 'all' voor alle vestigingen.", "schema": {"type": "string", "description": "Locatie, of 'all' voor alle vestigingen.", "enum": ["locals-coffee", "locals-brunch", "flow-brunch", "locals-to-go", "locals-city", "all"], "default": "all"}}, {"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/revenue/tables": {"get": {"operationId": "revenue_tables", "summary": "Tafelstatistieken: covers, besteding per gast, gemiddelde tafelduur.", "description": "Tafelstatistieken: covers, besteding per gast, gemiddelde tafelduur.", "tags": ["revenue"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": false, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": false, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "location_id", "in": "query", "required": false, "description": "Locatie, of 'all' voor alle vestigingen.", "schema": {"type": "string", "description": "Locatie, of 'all' voor alle vestigingen.", "enum": ["locals-coffee", "locals-brunch", "flow-brunch", "locals-to-go", "locals-city", "all"], "default": "all"}}, {"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/labour/daily": {"get": {"operationId": "labour_daily", "summary": "Loonkost per dag per locatie: geklokt, ingeroosterd, uren, loonkost-% en SPLH.", "description": "Live uit Eitje. Verdeelt het loon van gedeelde medewerkers (zoals de operationeel manager) gelijk over hun vestigingen en telt mensen mee die op dit moment ingeklokt staan.", "tags": ["labour"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": true, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": true, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "location_id", "in": "query", "required": false, "description": "Locatie, of 'all' voor alle vestigingen.", "schema": {"type": "string", "description": "Locatie, of 'all' voor alle vestigingen.", "enum": ["locals-coffee", "locals-brunch", "flow-brunch", "locals-to-go", "locals-city", "all"], "default": "all"}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/labour/hourly": {"get": {"operationId": "labour_hourly", "summary": "Per uur-van-de-dag: omzet, loonkost en resultaat. Beantwoordt 'tot hoe laat open?'.", "description": "De loonkost van elke dienst wordt pro rata over de uren verdeeld die de dienst beslaat. 'result' negatief = dat uur kost geld.", "tags": ["labour"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": true, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": true, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "location_id", "in": "query", "required": false, "description": "Locatie, of 'all' voor alle vestigingen.", "schema": {"type": "string", "description": "Locatie, of 'all' voor alle vestigingen.", "enum": ["locals-coffee", "locals-brunch", "flow-brunch", "locals-to-go", "locals-city", "all"], "default": "all"}}, {"name": "dow", "in": "query", "required": false, "description": "weekday = ma-do, weekend = vr-zo.", "schema": {"type": "string", "description": "weekday = ma-do, weekend = vr-zo.", "enum": ["all", "weekday", "weekend"], "default": "all"}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/labour/clocked-without-roster": {"get": {"operationId": "labour_clocked_without_roster", "summary": "Wie klokte er zonder ingeroosterd te staan? Vangt verkeerde-locatie-inklok.", "description": "Wie klokte er zonder ingeroosterd te staan? Vangt verkeerde-locatie-inklok.", "tags": ["labour"], "x-scope": "read", "parameters": [{"name": "date", "in": "query", "required": false, "description": "Dag, YYYY-MM-DD. Default vandaag.", "schema": {"type": "string", "description": "Dag, YYYY-MM-DD. Default vandaag.", "format": "date"}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/labour/log": {"get": {"operationId": "labour_log", "summary": "Archief van het dagelijkse loonkost-WhatsAppbericht (18:00).", "description": "Archief van het dagelijkse loonkost-WhatsAppbericht (18:00).", "tags": ["labour"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": false, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": false, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/labour/monthly": {"get": {"operationId": "labour_monthly", "summary": "Werkelijke werkgeverslast per maand per locatie, uit het Nmbrs-loonjournaal.", "description": "Werkelijke werkgeverslast per maand per locatie, uit het Nmbrs-loonjournaal.", "tags": ["labour", "staff"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": false, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": false, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "location_id", "in": "query", "required": false, "description": "Locatie, of 'all' voor alle vestigingen.", "schema": {"type": "string", "description": "Locatie, of 'all' voor alle vestigingen.", "enum": ["locals-coffee", "locals-brunch", "flow-brunch", "locals-to-go", "locals-city", "all"], "default": "all"}}, {"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/financials/pnl": {"get": {"operationId": "financials_pnl", "summary": "Winst-en-verliesrekening per locatie \u2014 de cijfers zoals het dashboard ze toont.", "description": "DE endpoint voor winstvragen. Reconcilieert per kalendermaand: afgesloten maanden gebruiken Exact-omzet en Exact-loon (inclusief het terugverdelen van doorbelast loon naar de host-BV), de lopende maand gebruikt live kassaomzet en Eitje-geklokt loon. Tel NOOIT zelf exact_cost_daily op \u2014 dan krijg je andere cijfers dan het dashboard.", "tags": ["financials"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": true, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": true, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "location_id", "in": "query", "required": false, "description": "Locatie, of 'all' voor alle vestigingen.", "schema": {"type": "string", "description": "Locatie, of 'all' voor alle vestigingen.", "enum": ["locals-coffee", "locals-brunch", "flow-brunch", "locals-to-go", "locals-city", "all"], "default": "all"}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/financials/costs-daily": {"get": {"operationId": "financials_costs_daily", "summary": "Geboekte kosten per dag uit Exact: inkoop, vaste kosten, afschrijving, loon, belasting.", "description": "Geboekte kosten per dag uit Exact: inkoop, vaste kosten, afschrijving, loon, belasting.", "tags": ["financials"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": false, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": false, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "location_id", "in": "query", "required": false, "description": "Locatie, of 'all' voor alle vestigingen.", "schema": {"type": "string", "description": "Locatie, of 'all' voor alle vestigingen.", "enum": ["locals-coffee", "locals-brunch", "flow-brunch", "locals-to-go", "locals-city", "all"], "default": "all"}}, {"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/financials/costs-monthly": {"get": {"operationId": "financials_costs_monthly", "summary": "Grootboekmutaties per maand per BV uit Exact.", "description": "Grootboekmutaties per maand per BV uit Exact.", "tags": ["financials"], "x-scope": "read", "parameters": [{"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/financials/cash": {"get": {"operationId": "financials_cash", "summary": "Kaspositie per dag per BV (cash year-to-date).", "description": "Kaspositie per dag per BV (cash year-to-date).", "tags": ["financials"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": false, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": false, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "location_id", "in": "query", "required": false, "description": "Locatie, of 'all' voor alle vestigingen.", "schema": {"type": "string", "description": "Locatie, of 'all' voor alle vestigingen.", "enum": ["locals-coffee", "locals-brunch", "flow-brunch", "locals-to-go", "locals-city", "all"], "default": "all"}}, {"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/financials/bank": {"get": {"operationId": "financials_bank", "summary": "Banksaldi per rekening per BV.", "description": "Banksaldi per rekening per BV.", "tags": ["financials"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": false, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": false, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "location_id", "in": "query", "required": false, "description": "Locatie, of 'all' voor alle vestigingen.", "schema": {"type": "string", "description": "Locatie, of 'all' voor alle vestigingen.", "enum": ["locals-coffee", "locals-brunch", "flow-brunch", "locals-to-go", "locals-city", "all"], "default": "all"}}, {"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/financials/tips": {"get": {"operationId": "financials_tips", "summary": "Nog uit te keren fooi (grootboek 1851) per locatie.", "description": "Nog uit te keren fooi (grootboek 1851) per locatie.", "tags": ["financials"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": false, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": false, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "location_id", "in": "query", "required": false, "description": "Locatie, of 'all' voor alle vestigingen.", "schema": {"type": "string", "description": "Locatie, of 'all' voor alle vestigingen.", "enum": ["locals-coffee", "locals-brunch", "flow-brunch", "locals-to-go", "locals-city", "all"], "default": "all"}}, {"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/financials/rekening-courant": {"get": {"operationId": "financials_rc", "summary": "Rekening-courant DGA per maand: stand bank en schuld.", "description": "Rekening-courant DGA per maand: stand bank en schuld.", "tags": ["financials"], "x-scope": "read", "parameters": [{"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/financials/accountant-pl": {"get": {"operationId": "financials_accountant_pl", "summary": "De P&L zoals de accountant 'm oplevert \u2014 voor reconciliatie met het dashboard.", "description": "De P&L zoals de accountant 'm oplevert \u2014 voor reconciliatie met het dashboard.", "tags": ["financials"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": false, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": false, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "location_id", "in": "query", "required": false, "description": "Locatie, of 'all' voor alle vestigingen.", "schema": {"type": "string", "description": "Locatie, of 'all' voor alle vestigingen.", "enum": ["locals-coffee", "locals-brunch", "flow-brunch", "locals-to-go", "locals-city", "all"], "default": "all"}}, {"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/staff": {"get": {"operationId": "staff", "summary": "Personeelsbestand uit Nmbrs: functie, contractvorm, einddatum, werkgeverslast.", "description": "Bevat contract_type, contract_sequence, contract_end en keten_warning voor de ketenregeling. Privacygevoelige ruwe Nmbrs-payload (kolom 'raw') wordt nooit teruggegeven.", "tags": ["staff"], "x-scope": "read", "parameters": [{"name": "location_id", "in": "query", "required": false, "description": "Locatie, of 'all' voor alle vestigingen.", "schema": {"type": "string", "description": "Locatie, of 'all' voor alle vestigingen.", "enum": ["locals-coffee", "locals-brunch", "flow-brunch", "locals-to-go", "locals-city", "all"], "default": "all"}}, {"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/staff/{employee_id}/contracts": {"get": {"operationId": "staff_contracts", "summary": "Volledige contracthistorie van \u00e9\u00e9n medewerker.", "description": "Volledige contracthistorie van \u00e9\u00e9n medewerker.", "tags": ["staff"], "x-scope": "read", "parameters": [{"name": "employee_id", "in": "path", "required": true, "description": "Nmbrs employee_id.", "schema": {"type": "string", "description": "Nmbrs employee_id."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/alerts": {"get": {"operationId": "alerts", "summary": "Signalen uit de alerts-engine: loonkost, AOV, omzetpace, ads, Exact-staleness.", "description": "Signalen uit de alerts-engine: loonkost, AOV, omzetpace, ads, Exact-staleness.", "tags": ["ops"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": false, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": false, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "location_id", "in": "query", "required": false, "description": "Locatie, of 'all' voor alle vestigingen.", "schema": {"type": "string", "description": "Locatie, of 'all' voor alle vestigingen.", "enum": ["locals-coffee", "locals-brunch", "flow-brunch", "locals-to-go", "locals-city", "all"], "default": "all"}}, {"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/reports/eod": {"get": {"operationId": "reports_eod", "summary": "End-of-day-rapporten van de managers (uit het SOP-workbook).", "description": "End-of-day-rapporten van de managers (uit het SOP-workbook).", "tags": ["ops"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": false, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": false, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "location_id", "in": "query", "required": false, "description": "Locatie, of 'all' voor alle vestigingen.", "schema": {"type": "string", "description": "Locatie, of 'all' voor alle vestigingen.", "enum": ["locals-coffee", "locals-brunch", "flow-brunch", "locals-to-go", "locals-city", "all"], "default": "all"}}, {"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/reports/eod-problems": {"get": {"operationId": "reports_eod_problems", "summary": "Uit de EOD-rapporten ge\u00ebxtraheerde problemen, met thema en ernst.", "description": "Uit de EOD-rapporten ge\u00ebxtraheerde problemen, met thema en ernst.", "tags": ["ops"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": false, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": false, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "location_id", "in": "query", "required": false, "description": "Locatie, of 'all' voor alle vestigingen.", "schema": {"type": "string", "description": "Locatie, of 'all' voor alle vestigingen.", "enum": ["locals-coffee", "locals-brunch", "flow-brunch", "locals-to-go", "locals-city", "all"], "default": "all"}}, {"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/reports/weekly": {"get": {"operationId": "reports_weekly", "summary": "Weekrapporten van de managers.", "description": "Weekrapporten van de managers.", "tags": ["ops"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": false, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": false, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/emails": {"get": {"operationId": "emails", "summary": "Belangrijke mail uit de Primary-inbox, met samenvatting.", "description": "Belangrijke mail uit de Primary-inbox, met samenvatting.", "tags": ["ops"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": false, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": false, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/marketing/google-ads": {"get": {"operationId": "marketing_google_ads", "summary": "Google Ads op campagne-, advertentiegroep- of zoekwoordniveau.", "description": "Google Ads op campagne-, advertentiegroep- of zoekwoordniveau.", "tags": ["marketing"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": false, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": false, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "level", "in": "query", "required": false, "description": "Detailniveau.", "schema": {"type": "string", "description": "Detailniveau.", "enum": ["campaign", "adgroup", "keyword"], "default": "campaign"}}, {"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/marketing/meta-ads": {"get": {"operationId": "marketing_meta_ads", "summary": "Meta/Instagram-advertenties per dag: spend, bereik, clicks.", "description": "Meta/Instagram-advertenties per dag: spend, bereik, clicks.", "tags": ["marketing"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": false, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": false, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/marketing/costs": {"get": {"operationId": "marketing_costs", "summary": "Werkelijk geboekte marketinguitgaven per maand uit Exact.", "description": "Dit is de ECHTE uitgave (netto, uit de boekhouding). Bureaus zijn ongeveer tweederde van het budget en zitten niet in de ad-API's. Tel dit dus nooit op bij google-ads of meta-ads \u2014 dat dubbeltelt.", "tags": ["marketing"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": false, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": false, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "location_id", "in": "query", "required": false, "description": "Locatie, of 'all' voor alle vestigingen.", "schema": {"type": "string", "description": "Locatie, of 'all' voor alle vestigingen.", "enum": ["locals-coffee", "locals-brunch", "flow-brunch", "locals-to-go", "locals-city", "all"], "default": "all"}}, {"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/marketing/google-business": {"get": {"operationId": "marketing_gbp", "summary": "Google Business Profile: vertoningen in Zoeken en Maps, clicks, routes, bellen.", "description": "Google Business Profile: vertoningen in Zoeken en Maps, clicks, routes, bellen.", "tags": ["marketing"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": false, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": false, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "location_id", "in": "query", "required": false, "description": "Locatie, of 'all' voor alle vestigingen.", "schema": {"type": "string", "description": "Locatie, of 'all' voor alle vestigingen.", "enum": ["locals-coffee", "locals-brunch", "flow-brunch", "locals-to-go", "locals-city", "all"], "default": "all"}}, {"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/marketing/posts": {"get": {"operationId": "marketing_posts", "summary": "Geplaatste social posts met onderwerp, CTA en status.", "description": "Geplaatste social posts met onderwerp, CTA en status.", "tags": ["marketing"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": false, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": false, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "location_id", "in": "query", "required": false, "description": "Locatie, of 'all' voor alle vestigingen.", "schema": {"type": "string", "description": "Locatie, of 'all' voor alle vestigingen.", "enum": ["locals-coffee", "locals-brunch", "flow-brunch", "locals-to-go", "locals-city", "all"], "default": "all"}}, {"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/reviews": {"get": {"operationId": "reviews", "summary": "Google-reviews per locatie met score, tekst en datum.", "description": "Google-reviews per locatie met score, tekst en datum.", "tags": ["reviews"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": false, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": false, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "location_id", "in": "query", "required": false, "description": "Locatie, of 'all' voor alle vestigingen.", "schema": {"type": "string", "description": "Locatie, of 'all' voor alle vestigingen.", "enum": ["locals-coffee", "locals-brunch", "flow-brunch", "locals-to-go", "locals-city", "all"], "default": "all"}}, {"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/reviews/stats": {"get": {"operationId": "reviews_stats", "summary": "Actuele gemiddelde score en aantal reviews per locatie.", "description": "Actuele gemiddelde score en aantal reviews per locatie.", "tags": ["reviews"], "x-scope": "read", "parameters": [{"name": "location_id", "in": "query", "required": false, "description": "Locatie, of 'all' voor alle vestigingen.", "schema": {"type": "string", "description": "Locatie, of 'all' voor alle vestigingen.", "enum": ["locals-coffee", "locals-brunch", "flow-brunch", "locals-to-go", "locals-city", "all"], "default": "all"}}, {"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/reservations": {"get": {"operationId": "reservations", "summary": "Zenchef-reserveringen: aantal gasten, tijdslot, status, walk-in of niet.", "description": "Zenchef-reserveringen: aantal gasten, tijdslot, status, walk-in of niet.", "tags": ["reservations"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": false, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": false, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "location_id", "in": "query", "required": false, "description": "Locatie, of 'all' voor alle vestigingen.", "schema": {"type": "string", "description": "Locatie, of 'all' voor alle vestigingen.", "enum": ["locals-coffee", "locals-brunch", "flow-brunch", "locals-to-go", "locals-city", "all"], "default": "all"}}, {"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/forecast/build": {"get": {"operationId": "forecast_build", "summary": "Live omzetforecast per locatie voor de komende dagen.", "description": "Weekdag-baseline over 84 dagen \u00d7 trend \u00d7 weer \u00d7 feestdag \u00d7 event, per archetype (terrace/indoor/grab/office). Zelfde motor als de nachtelijke cron.", "tags": ["forecast"], "x-scope": "read", "parameters": [{"name": "location_id", "in": "query", "required": false, "description": "Locatie, of 'all' voor alle vestigingen.", "schema": {"type": "string", "description": "Locatie, of 'all' voor alle vestigingen.", "enum": ["locals-coffee", "locals-brunch", "flow-brunch", "locals-to-go", "locals-city", "all"], "default": "all"}}, {"name": "days", "in": "query", "required": false, "description": "Horizon in dagen (max 28).", "schema": {"type": "integer", "description": "Horizon in dagen (max 28).", "default": 14, "minimum": 1, "maximum": 28}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/forecast/accuracy": {"get": {"operationId": "forecast_accuracy", "summary": "Hoe goed was de forecast? Model vs baseline vs Eitje, met afwijking in %.", "description": "Hoe goed was de forecast? Model vs baseline vs Eitje, met afwijking in %.", "tags": ["forecast"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": false, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": false, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "location_id", "in": "query", "required": false, "description": "Locatie, of 'all' voor alle vestigingen.", "schema": {"type": "string", "description": "Locatie, of 'all' voor alle vestigingen.", "enum": ["locals-coffee", "locals-brunch", "flow-brunch", "locals-to-go", "locals-city", "all"], "default": "all"}}, {"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/forecast/runs": {"get": {"operationId": "forecast_runs", "summary": "Logboek van forecast-runs met status en duur.", "description": "Logboek van forecast-runs met status en duur.", "tags": ["forecast"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": false, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": false, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "location_id", "in": "query", "required": false, "description": "Locatie, of 'all' voor alle vestigingen.", "schema": {"type": "string", "description": "Locatie, of 'all' voor alle vestigingen.", "enum": ["locals-coffee", "locals-brunch", "flow-brunch", "locals-to-go", "locals-city", "all"], "default": "all"}}, {"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/forecast/inputs": {"get": {"operationId": "forecast_inputs", "summary": "De drivers achter de forecast: weer, feestdagen en evenementen.", "description": "De drivers achter de forecast: weer, feestdagen en evenementen.", "tags": ["forecast"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": false, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": false, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/targets": {"get": {"operationId": "targets", "summary": "Doelstellingen per locatie: omzet/dag, loonkost-%, marge, winst, AOV.", "description": "Doelstellingen per locatie: omzet/dag, loonkost-%, marge, winst, AOV.", "tags": ["targets"], "x-scope": "read", "parameters": [{"name": "location_id", "in": "query", "required": false, "description": "Locatie, of 'all' voor alle vestigingen.", "schema": {"type": "string", "description": "Locatie, of 'all' voor alle vestigingen.", "enum": ["locals-coffee", "locals-brunch", "flow-brunch", "locals-to-go", "locals-city", "all"], "default": "all"}}, {"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/targets/days": {"get": {"operationId": "targets_days", "summary": "Omzetdoel per dag per locatie ('een zaterdag is geen dinsdag').", "description": "Omzetdoel per dag per locatie ('een zaterdag is geen dinsdag').", "tags": ["targets"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": false, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": false, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "location_id", "in": "query", "required": false, "description": "Locatie, of 'all' voor alle vestigingen.", "schema": {"type": "string", "description": "Locatie, of 'all' voor alle vestigingen.", "enum": ["locals-coffee", "locals-brunch", "flow-brunch", "locals-to-go", "locals-city", "all"], "default": "all"}}, {"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}, "put": {"operationId": "target_day_set", "summary": "Omzetdoel voor \u00e9\u00e9n specifieke dag en locatie zetten.", "description": "Omzetdoel voor \u00e9\u00e9n specifieke dag en locatie zetten.", "tags": ["targets"], "x-scope": "write", "parameters": [], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'write'"}}, "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "properties": {"date": {"type": "string", "description": "", "format": "date"}, "location_id": {"type": "string", "description": "", "enum": ["locals-coffee", "locals-brunch", "flow-brunch", "locals-to-go", "locals-city"]}, "target_eur": {"type": "number", "description": "Omzetdoel in euro."}, "stretch_pct": {"type": "number", "description": ""}, "forecast_eur": {"type": "number", "description": ""}, "source": {"type": "string", "description": "Herkomst van het doel. Default 'agent'."}}, "required": ["date", "location_id", "target_eur"]}}}}}}, "/targets/results": {"get": {"operationId": "targets_results", "summary": "Per afgesloten dag: doel vs werkelijk vs gehaald, voor omzet, loonkost en AOV.", "description": "Per afgesloten dag: doel vs werkelijk vs gehaald, voor omzet, loonkost en AOV.", "tags": ["targets"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": false, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": false, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "location_id", "in": "query", "required": false, "description": "Locatie, of 'all' voor alle vestigingen.", "schema": {"type": "string", "description": "Locatie, of 'all' voor alle vestigingen.", "enum": ["locals-coffee", "locals-brunch", "flow-brunch", "locals-to-go", "locals-city", "all"], "default": "all"}}, {"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/kpis": {"get": {"operationId": "kpis", "summary": "De vijf kern-KPI's met vergelijkingsperiode: omzet, loonkost-%, SPLH, AOV, covers.", "description": "De vijf kern-KPI's met vergelijkingsperiode: omzet, loonkost-%, SPLH, AOV, covers.", "tags": ["kpi"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": true, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": true, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "compare", "in": "query", "required": false, "description": "Waarmee vergelijken: 7, 28 of 364 dagen terug.", "schema": {"type": "string", "description": "Waarmee vergelijken: 7, 28 of 364 dagen terug.", "enum": ["week", "month", "year", "none"], "default": "week"}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/query": {"post": {"operationId": "query", "summary": "Vrije read-only query op \u00e9\u00e9n tabel uit de allowlist.", "description": "Voor alles waar geen eigen endpoint voor is. Alleen lezen, alleen tabellen uit de allowlist, maximaal 5000 rijen. Gebruik bij voorkeur een specifiek endpoint \u2014 die bevatten de juiste logica.", "tags": ["query"], "x-scope": "read", "parameters": [], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}, "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "properties": {"table": {"type": "string", "description": "Tabelnaam uit de allowlist.", "enum": ["accountant_pl", "agent_api_log", "alerts", "amsterdam_events", "costs", "daily_briefings", "daily_labour_log", "eitje_cache", "eod_problems", "eod_reports", "exact_bank_balance", "exact_bank_opening", "exact_cash_snapshot", "exact_cost_daily", "exact_cost_monthly", "exact_divisions", "exact_opening_cash", "exact_rc_monthly", "exact_tip_balance", "forecast_accuracy", "forecast_runs", "gbp_insights", "google_ads_adgroup_daily", "google_ads_daily", "google_ads_keyword_daily", "google_place_stats", "google_review_sources", "google_reviews", "holidays", "important_emails", "labor_thresholds", "lightspeed_hourly_revenue", "lightspeed_products_daily", "lightspeed_revenue_daily", "lightspeed_sites", "lightspeed_table_metrics", "locations", "marketing_costs_monthly", "marketing_posts", "meta_ads_daily", "nmbrs_company_map", "nmbrs_contracts", "nmbrs_employees", "nmbrs_labour_monthly", "notification_recipients", "pos_monthly_revenue", "report_runs", "report_schedules", "revenue_daily", "target_days", "target_results", "targets", "weather_forecast", "weekly_reports", "zenchef_bookings", "zenchef_sync_state"]}, "select": {"type": "string", "description": "Kolommen, komma-gescheiden. Default alles."}, "filters": {"type": "object", "description": "PostgREST-filters, bv. {\"date\": {\"gte\": \"2026-07-01\"}, \"location_id\": {\"eq\": \"locals-coffee\"}}. Toegestane operatoren: eq, neq, gt, gte, lt, lte, like, ilike, in, is, cs, cd."}, "order": {"type": "string", "description": "Sortering, bv. 'net_revenue.desc'."}, "limit": {"type": "integer", "description": "", "default": 1000, "minimum": 1, "maximum": 5000}, "offset": {"type": "integer", "description": "", "default": 0, "minimum": 0}}, "required": ["table"]}}}}}}, "/audit-log": {"get": {"operationId": "audit_log", "summary": "Logboek van schrijfacties via deze API.", "description": "Logboek van schrijfacties via deze API.", "tags": ["ops"], "x-scope": "read", "parameters": [{"name": "start", "in": "query", "required": false, "description": "Begindatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Begindatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "end", "in": "query", "required": false, "description": "Einddatum, YYYY-MM-DD (inclusief).", "schema": {"type": "string", "description": "Einddatum, YYYY-MM-DD (inclusief).", "format": "date"}}, {"name": "limit", "in": "query", "required": false, "description": "Maximum aantal rijen (max 5000).", "schema": {"type": "integer", "description": "Maximum aantal rijen (max 5000).", "default": 1000, "minimum": 1, "maximum": 5000}}, {"name": "offset", "in": "query", "required": false, "description": "Rijen overslaan, voor pagineren.", "schema": {"type": "integer", "description": "Rijen overslaan, voor pagineren.", "default": 0, "minimum": 0}}, {"name": "order", "in": "query", "required": false, "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'.", "schema": {"type": "string", "description": "PostgREST-sortering, bv. 'date.desc' of 'net_revenue.desc'."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/alerts/{alert_id}": {"patch": {"operationId": "alert_update", "summary": "Een alert afhandelen: bevestigen, afwijzen of oplossen.", "description": "Een alert afhandelen: bevestigen, afwijzen of oplossen.", "tags": ["ops"], "x-scope": "write", "parameters": [{"name": "alert_id", "in": "path", "required": true, "description": "Id van de alert.", "schema": {"type": "string", "description": "Id van de alert."}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'write'"}}, "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "properties": {"status": {"type": "string", "description": "Nieuwe status.", "enum": ["acknowledged", "dismissed", "open", "resolved"]}}, "required": ["status"]}}}}}}, "/targets/{location_id}": {"put": {"operationId": "target_update", "summary": "Doelstellingen van \u00e9\u00e9n locatie aanpassen.", "description": "Doelstellingen van \u00e9\u00e9n locatie aanpassen.", "tags": ["targets"], "x-scope": "write", "parameters": [{"name": "location_id", "in": "path", "required": true, "description": "", "schema": {"type": "string", "description": "", "enum": ["locals-coffee", "locals-brunch", "flow-brunch", "locals-to-go", "locals-city"]}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'write'"}}, "requestBody": {"required": false, "content": {"application/json": {"schema": {"type": "object", "properties": {"omzet_per_dag": {"type": "number", "description": "Omzetdoel per dag in euro."}, "loonkost_pct": {"type": "number", "description": "Loonkostdoel in procenten."}, "marge_pct": {"type": "number", "description": "Margedoel in procenten."}, "winst_per_dag": {"type": "number", "description": "Winstdoel per dag in euro."}, "aov": {"type": "number", "description": "Doel gemiddelde besteding per order."}, "stretch_pct": {"type": "number", "description": "Stretch-opslag in procenten."}, "omzet_auto": {"type": "boolean", "description": "Omzetdoel automatisch uit de forecast."}, "labour_auto": {"type": "boolean", "description": "Loonkostdoel automatisch."}, "aov_auto": {"type": "boolean", "description": "AOV-doel automatisch."}}}}}}}}, "/recipients": {"get": {"operationId": "recipients_list", "summary": "Ontvangers van alerts en WhatsApp-berichten.", "description": "Ontvangers van alerts en WhatsApp-berichten.", "tags": ["ops"], "x-scope": "read", "parameters": [], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}, "post": {"operationId": "recipient_create", "summary": "Een ontvanger toevoegen.", "description": "Een ontvanger toevoegen.", "tags": ["ops"], "x-scope": "write", "parameters": [], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'write'"}}, "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "properties": {"name": {"type": "string", "description": ""}, "email": {"type": "string", "description": ""}, "phone": {"type": "string", "description": ""}, "whatsapp": {"type": "boolean", "description": ""}, "email_on": {"type": "boolean", "description": ""}, "sort": {"type": "integer", "description": ""}}, "required": ["name"]}}}}}}, "/recipients/{recipient_id}": {"delete": {"operationId": "recipient_delete", "summary": "Een ontvanger verwijderen.", "description": "Een ontvanger verwijderen.", "tags": ["ops"], "x-scope": "write", "parameters": [{"name": "recipient_id", "in": "path", "required": true, "description": "", "schema": {"type": "string", "description": ""}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'write'"}}}}, "/jobs": {"get": {"operationId": "jobs_list", "summary": "Welke sync-jobs kunnen gestart worden.", "description": "Welke sync-jobs kunnen gestart worden.", "tags": ["jobs"], "x-scope": "read", "parameters": [], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'read'"}}}}, "/jobs/{job_name}": {"post": {"operationId": "job_run", "summary": "Een sync-job starten (Lightspeed, Nmbrs, Zenchef, reviews, alerts, forecast).", "description": "Draait de bestaande cron-endpoints server-side. Alleen namen uit de vaste lijst; een job kan tot ~55 seconden duren.", "tags": ["jobs"], "x-scope": "write", "parameters": [{"name": "job_name", "in": "path", "required": true, "description": "Naam van de job.", "schema": {"type": "string", "description": "Naam van de job.", "enum": ["alerts-run", "bank-snapshot", "briefing", "briefing-preview", "cash-snapshot", "daily-labour", "forecast-nightly", "lightspeed-daily", "lightspeed-sync-today", "marketing-costs", "meta-sync", "nmbrs-sync", "reviews-fetch", "reviews-start", "sop-sync", "target-eval", "tip-snapshot", "zenchef-sync"]}}], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "OK", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Ongeldige parameters"}, "401": {"description": "Ontbrekend of ongeldig token"}, "403": {"description": "Token mist de scope 'write'"}}}}}}