Modules

Ontwikkelaars

Met Ontwikkelaars koppel je jullie eigen software aan de Suite. Het scherm heeft vier tabbladen: Sleutels voor de developer-API, Query & velden om een query uit te proberen voordat je hem automatiseert, Documentatie met de API-referentie, en Koppelingen om een eigen app aan Spark AI te hangen.

Sleutels

Bovenaan zie je het verbruik van deze maand: het aantal verzoeken en het aantal teruggegeven rijen. Dat is een meter voor de hele organisatie, er zit geen plafond op. Wel geldt er per sleutel een snelheidslimiet: standaard 60 verzoeken per minuut, en 5 per minuut voor vragen in gewone taal. Die vragen gaan bovendien van hetzelfde AI-tegoed af als Spark AI.

Onder Nieuwe sleutel maak je een sleutel aan:

  • Label: een naam waaraan je de sleutel herkent, bijvoorbeeld "WMS-koppeling".
  • E-mailadres: voor wie de sleutel bedoeld is. Bij een softwarepartner vul je diens contactadres in.
  • Rolbeperking: optioneel. Laat je hem leeg, dan is de sleutel onbeperkt. Met een beperking geef je een derde partij minder te zien: Alleen omzet of Alleen inkoop.

Klik op Sleutel aanmaken. De sleutel verschijnt dan één keer, met een knop Kopiëren. Kopieer hem meteen: hij wordt nergens opgeslagen en is daarna niet meer op te vragen.

Ben je geen beheerder en heb je zelf een beperkte rol, dan kun je alleen een sleutel met je eigen rol aanmaken. Voor een andere rol vraag je een beheerder. Onder Sleutels staat elke sleutel met voor wie hij is, de rol, wanneer hij is aangemaakt en laatst gebruikt, en de status.

Sleutels. Het verbruik van deze maand, een nieuwe sleutel aanmaken en de sleutels die er al zijn.
Sleutels. Het verbruik van deze maand, een nieuwe sleutel aanmaken en de sleutels die er al zijn.

Query & velden

Hier probeer je een query uit voordat je hem in een koppeling zet. Typ een SELECT, bijvoorbeeld select * from sales limit 50, en klik op Uitvoeren. Je ziet precies wat jouw rol mag zien.

Onder Veldenreferentie staat per tabel, zoals sales, purchases, stock en auction_prices, wat erin staat. Klap een tabel open voor de kolommen met hun omschrijving, eenheid en synoniemen.

Query & velden. Probeer een query uit, met daaronder per tabel wat erin staat.
Query & velden. Probeer een query uit, met daaronder per tabel wat erin staat.

Documentatie

Hier staat de API-referentie met het basisadres en per endpoint een voorbeeld. Het basisadres is https://api.floraspark.nl. Alle endpoints praten JSON over HTTPS en gebruiken dezelfde sleutel. Die stuur je mee als Bearer-token in de Authorization-header. De volledige technische beschrijving, met voorbeeldantwoorden, foutcodes en grenzen, staat hieronder bij API-referentie.

Documentatie. Per endpoint wat het doet, een voorbeeldaanroep en wat je terugkrijgt.
Documentatie. Per endpoint wat het doet, een voorbeeldaanroep en wat je terugkrijgt.
EndpointWat het doet
POST /v1/query (eigen SQL)Voert je eigen SELECT uit op jullie eigen data
GET /v1/schema (veldenreferentie)Geeft de tabellen en kolommen die je sleutel mag bevragen
POST /v1/ask (vraag in gewone taal)Stelt je vraag aan dezelfde AI-agent als Spark AI, nu vanuit je eigen app
POST /v1/reports (rapportmail versturen)Laat een rapportmail maken en versturen, met een opgeslagen rapport, met blokken of met een eigen opdracht
GET /v1/reports/{id} (status van een rapport)Zegt of het rapport ontvangen, bezig, verstuurd of mislukt is
GET /v1/reports/options (keuzes voor een rapport)Geeft de blokken en opgeslagen rapporten waaruit je kunt kiezen

Een rapportmail via de API bevat alleen wat je sleutel mag zien en gaat af van het AI-tegoed. Je stuurt hoogstens 20 ontvangers per keer en hoogstens 100 rapporten per dag voor de hele organisatie. Het verzoek krijgt meteen een nummer terug; daarmee vraag je daarna de status op. Verstuurd betekent dat de mail de deur uit is, niet dat hij al in de inbox staat. De gebruiker bij de sleutel heeft de module Rapporten nodig.

Koppelingen

Met Koppelingen hang je een eigen app aan Spark AI, bijvoorbeeld jullie CRM of een tool die je met een coding agent hebt gebouwd. Die app biedt een MCP-server aan, en Spark AI kan de tools daarvan daarna in de chat gebruiken. Een lezende tool draait direct, een actie pas nadat je hem in de chat hebt bevestigd.

Koppelingen. De bouwprompt voor je coding agent en het formulier om een server te registreren.
Koppelingen. De bouwprompt voor je coding agent en het formulier om een server te registreren.

Zo leg je een koppeling aan

  1. Klik bij Bouwprompt voor je coding agent op Kopieer prompt en plak hem in de coding agent waarmee je je app bouwt, bijvoorbeeld Claude Code, Cursor of Lovable. De agent voegt een MCP-server toe die aan onze eisen voldoet. Bij de prompt staan ook de redirect-URI, het client-id en het vaste IP-adres waarvandaan Spark AI je server aanroept. De eisen zelf staan ook op deze pagina, bij MCP-server: technische eisen.
  2. Vul onder Server registreren een naam in, een korte naam (1 tot 16 kleine letters of cijfers; Spark AI zet hem voor de namen van de tools) en de URL van het MCP-endpoint. Die begint met https en eindigt meestal op /mcp.
  3. Kies bij Inloggen hoe Spark AI bij je app inlogt (zie hieronder) en klik op Registreren.
  4. Klik bij de server op Test verbinding. Je ziet per stap wat in orde is en wat niet. Een melding kun je letterlijk aan je coding agent teruggeven.
  5. Verbind je account met Verbinden en kies bij Tools voor Spark AI welke tools Spark AI mag gebruiken: per tool lezend of actie, en of hij in de chat mag. Sla op met Goedkeuring opslaan.
InloggenWat het betekent
OAuth per gebruikerElke collega verbindt zijn eigen account met je app. Spark AI kan daardoor nooit meer dan die persoon zelf, en je app beslist wat dat is
Vaste sleutelEén sleutel voor iedereen in de rollen die je aanvinkt. Je app ziet dan alleen FloraSpark, niet wie de vraag stelt. Een vaste sleutel stelt alleen een beheerder in

Tools goedkeuren kan een beheerder of degene die de server registreerde.

Wat je moet weten

  • Je keurt elke tool zelf goed. Verandert je app de naam, beschrijving of invoer van een tool, dan verdwijnt hij uit Spark AI tot je hem opnieuw goedkeurt.
  • Een actie pas na bevestiging. Wil Spark AI een actie uitvoeren, dan toont de chat de exacte gegevens met Uitvoeren en Weigeren. Er gebeurt niets tot de gebruiker kiest, en een besluit geldt één keer en tien minuten.
  • Je server. Hij moet publiek bereikbaar zijn via HTTPS met een geldig certificaat, op poort 443 of een poort vanaf 1024, zonder redirects. Spark AI belt altijd vanaf hetzelfde IP-adres, dus daarop kun je je server afschermen.
  • Grenzen. Een aanroep heeft een maximale duur, een resultaat een maximale omvang, en per gebruiker komt er een maximaal aantal tools in Spark AI. De actuele waarden staan onder Zo werkt een koppeling.
  • Wat er bewaard wordt. Tokens en sleutels staan versleuteld in een eigen kluis en zijn nergens in te zien. Elke aanroep komt 90 dagen in een logboek: wie, welke tool, de uitkomst en de omvang, en bij een actie ook de gegevens en wie bevestigde.
  • Stoppen. Met Verbreken verbreek je je eigen verbinding. Verwijder je de server, dan verdwijnen alle verbindingen, goedkeuringen en sleutels ermee.

API-referentie

Dit deel is voor wie de koppeling bouwt, of voor de coding agent die het voor je doet. Het beschrijft de API zoals hij nu draait. Een interactieve versie staat op api.floraspark.nl/docs, de OpenAPI-specificatie op api.floraspark.nl/openapi.json.

Basisadres en authenticatie

  • Basisadres: https://api.floraspark.nl. Alleen HTTPS.
  • Sleutel: begint met fsk_. Stuur hem bij elk verzoek mee als Authorization: Bearer <sleutel>, nooit in de URL.
  • Formaat: verzoeken en antwoorden zijn JSON (Content-Type: application/json), in UTF-8.
  • Bereik: een sleutel hoort bij één organisatie en één rol. Je ziet altijd alleen de eigen data van je organisatie, en met een rolbeperking alleen wat die rol mag zien.

Zet de sleutel in een omgevingsvariabele, bijvoorbeeld FLORASPARK_API_KEY, en niet in je code of in git.

curl https://api.floraspark.nl/v1/schema \
  -H "Authorization: Bearer $FLORASPARK_API_KEY"

Fouten

Elke fout heeft dezelfde vorm: een JSON-object met error, een leesbare zin in het Nederlands. Bij fouten waar je op kunt sturen staat er ook een code bij.

{ "error": "Snelheidslimiet bereikt voor deze sleutel." }
StatusBetekenisWat je doet
400Het verzoek klopt niet: ongeldige JSON, een ontbrekend veld, of een query die niet magLees error, pas het verzoek aan. Niet opnieuw proberen zonder wijziging
401Geen, een ongeldige of een ingetrokken sleutelControleer de header en de sleutel
402ai_budget_exhausted: het AI-tegoed van deze maand is op (alleen bij /v1/ask en rapporten)Wachten tot volgende maand, of het tegoed laten ophogen
403De sleutel mag dit niet, bijvoorbeeld geen_rapportenVraag een beheerder de module of rol aan te passen
404Niet gevonden, of een functie die voor jullie niet aanstaat (ask_disabled, reports_disabled)Controleer het pad of de id
422De inhoud van een vraag of rapport klopt niet, bijvoorbeeld te veel ontvangersLees error, pas het verzoek aan
429Te veel verzoeken: de minuutlimiet van de sleutel, te veel mislukte pogingen vanaf je IP-adres, of daglimiet_bereikt bij rapportenWacht en probeer het later opnieuw, liefst met oplopende wachttijd
502Een storing aan onze kant of een query die te lang duurdeLater opnieuw proberen

Snelheidslimieten

WatGrens
Verzoeken per sleutel (/v1/query, /v1/schema, rapportstatus en -opties)60 per minuut
Vragen in gewone taal (/v1/ask)5 per minuut per sleutel
Rapporten aanvragen (POST /v1/reports)5 per minuut per sleutel, en 100 per dag voor de hele organisatie
Mislukte pogingen met een ongeldige sleutel20 per minuut per IP-adres, daarna een minuut geblokkeerd (Retry-After: 60)

Het venster is een vaste minuut. Krijg je een 429, wacht dan tot de minuut om is. Er zit geen plafond op het totale aantal verzoeken of rijen per maand; dat is alleen een meter.

POST /v1/query

Voert één SELECT uit op jullie data en geeft de kolommen en rijen terug.

curl https://api.floraspark.nl/v1/query \
  -H "Authorization: Bearer $FLORASPARK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"sql": "select sales_iso_week, sum(revenue_amount) as omzet from sales where sales_iso_year = 2026 and is_revenue_line group by 1 order by 1"}'

Antwoord (200):

{
  "columns": ["sales_iso_week", "omzet"],
  "rows": [[1, 48210.55], [2, 51377.10]],
  "role": "full"
}
  • rows is een lijst van lijsten, in de volgorde van columns. Datums en tijdstippen komen als tekst (ISO 8601), bedragen als getal.
  • role is de rol waarmee de query draaide: full, revenue_only of purchase_only.

Regels voor de query:

  • Precies één statement, en alleen SELECT of WITH ... SELECT. Geen INSERT, UPDATE, DELETE of andere wijzigingen, ook niet in een CTE.
  • Gebruik alleen de tabellen uit GET /v1/schema, zonder schemaprefix: from sales. CTE-namen uit dezelfde query mogen ook. Elke andere tabel wordt geweigerd.
  • Systeemtabellen (pg_catalog, information_schema), functies met een schemaprefix en functies die namen of rechten opzoeken (pg_*, to_reg*, has_*_privilege, casts naar regclass) worden geweigerd.
  • Een query mag hoogstens 3500 rijen opleveren. Komen er meer, dan krijg je een 400 en geen afgekapt resultaat. Tel en groepeer dus in SQL (sum, count, group by), of haal in stukken op met een filter op datum of id.
  • Een query mag hoogstens 30 seconden duren.

Een geweigerde query krijgt een 400 met de reden in error. Die tekst is bedoeld om je query mee te verbeteren.

GET /v1/schema

Geeft de tabellen en kolommen die je sleutel mag bevragen. Dit is dezelfde lijst als onder Query & velden, en de enige bron die telt: hij past zich aan de rol van de sleutel en de modules van je organisatie aan.

{
  "schema": "...",
  "data_role": "full",
  "tables": [
    {
      "name": "sales",
      "description": "...",
      "grain": "Eén rij per verkoopregel (sales_line_key), open of gefactureerd.",
      "columns": [
        { "name": "revenue_amount", "description": "...", "unit": "EUR", "aggregatable": true }
      ]
    }
  ]
}

Lees de description van een kolom voordat je hem gebruikt: daar staat wat je wel en niet kunt optellen. Bij sommige kolommen staan ook synonyms. Gebruik name uit tables in je query; het veld schema heb je niet nodig.

De tabellen

TabelEén rij perZichtbaar voor
salesVerkoopregel, open of gefactureerd (sales_line_key)Alle rollen; bedragen alleen bij full en revenue_only
purchasesInkoopregel (line_id)full en purchase_only
stockVoorraadpakket dat er nu ligt (parcel_key)Alle rollen; kostprijs alleen bij full en purchase_only
auction_pricesVeildag, kanaal, locatie en product, eventueel per kenmerkAlleen met de module Veilingprijzen
auction_prices_intradayWaarneming van een prijsreeks tijdens de veildagAlleen met de module Veilingprijzen

Kolommen die per rol verschillen: margin_amount en commission_amount alleen bij full; omzetbedragen (revenue_amount, revenue_per_stem) bij full en revenue_only; inkoopbedragen (cost_amount, cost_per_stem) bij full en purchase_only. Wat je sleutel niet mag zien, bestaat voor hem niet: de kolom staat niet in het schema en een query erop wordt geweigerd.

Goed om te weten bij het ophalen:

  • Omzet tellen. Filter op is_revenue_line als je omzet wilt optellen; gebruik sales_date als de datum van een verkoop. Prijzen per steel (revenue_per_stem, cost_per_stem) tel je nooit op: gebruik de bedragkolommen.
  • Verversen. De data komt elke 15 minuten opnieuw binnen. Vaker ophalen levert niets nieuws op.
  • Incrementeel ophalen kan op version. Bij sales alleen samen met sales_status, want open en gefactureerde regels hebben elk hun eigen reeks. Bij purchases en stock geldt dat niet.
  • Verwijderingen zie je niet. Verdwijnt een regel uit de bron, dan valt hij weg uit de tabel. Wie alleen incrementeel ophaalt, merkt dat nooit. Haal daarom af en toe alles opnieuw op.
  • Veilingprijzen. Kies in auction_prices altijd precies één price_grain (product of characteristic), anders tel je dubbel. vbn_product_code is hetzelfde nummer als article_id in sales en purchases.

POST /v1/ask

Stelt een vraag in gewone taal aan dezelfde AI-agent als Spark AI. Eén vraag, één antwoord: er is geen gesprekshistorie.

curl https://api.floraspark.nl/v1/ask \
  -H "Authorization: Bearer $FLORASPARK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"question": "Wat was de omzet per week in september?"}'

Antwoord (200):

{
  "answer": "In september was de omzet ...",
  "status": "answered",
  "sql": "select ...",
  "columns": ["week", "omzet"],
  "rows": [[36, 50120.0], [37, 49875.4]]
}
  • answer is de tekst van het antwoord. sql, columns en rows zijn de laatste query die de agent daarvoor draaide, of null als hij geen query nodig had.
  • status is answered bij een gewoon antwoord. Een andere waarde betekent dat er geen volledig antwoord is.
  • De vraag mag hoogstens 8000 tekens zijn. Een antwoord kan een halve minuut duren; zet de timeout van je client ruim.
  • Elke vraag gaat van het AI-tegoed af, net als in Spark AI. Gebruik voor vaste koppelingen liever /v1/query: sneller, voorspelbaar en zonder tegoed.

POST /v1/reports

Laat een rapportmail maken en versturen. Kies één van drie soorten inhoud:

  • template_id: een opgeslagen rapport uit de Suite. Ontvangers en onderwerp komen dan uit dat rapport, tenzij je ze meestuurt.
  • blocks: een lijst met block_id's, eventueel met een extra instruction.
  • instruction: alleen een eigen opdracht in gewone taal.

template_id gaat niet samen met blocks of instruction.

VeldType
template_idtekstEen opgeslagen rapport, zie /v1/reports/options
blockslijst met tekstblock_id's, zie /v1/reports/options
instructiontekstEen eigen opdracht, hoogstens 8000 tekens
recipientslijst met tekst, of één tekst met komma'sHoogstens 20 adressen
subjecttekstOptioneel; anders het onderwerp of de naam van het rapport
curl https://api.floraspark.nl/v1/reports \
  -H "Authorization: Bearer $FLORASPARK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"blocks": ["..."], "recipients": ["inkoop@voorbeeldbedrijf.nl"], "subject": "Weekoverzicht"}'

Het verzoek wordt meteen aangenomen (202) en daarna op de achtergrond gemaakt en verstuurd:

{ "request_id": "3f2c...", "status": "ontvangen" }

Fouten die je meteen terugkrijgt: 402 ai_budget_exhausted, 403 geen_rapporten (de gebruiker bij de sleutel heeft de module Rapporten niet), 404 template_not_found, 422 bij geen inhoud, geen of te veel ontvangers, en 429 daglimiet_bereikt (100 per dag per organisatie, in Nederlandse tijd; mislukte verzoeken tellen mee).

GET /v1/reports/{id}

De status van een rapport dat je met dezelfde sleutel hebt aangevraagd. Een verzoek van een andere sleutel geeft een 404.

{
  "request_id": "3f2c...",
  "status": "verstuurd",
  "foutcode": null,
  "fout": null,
  "recipients": ["inkoop@voorbeeldbedrijf.nl"],
  "subject": "Weekoverzicht",
  "created_at": "2026-10-16T08:00:01+00:00",
  "started_at": "2026-10-16T08:00:02+00:00",
  "finished_at": "2026-10-16T08:00:41+00:00"
}
  • status is ontvangen, bezig, verstuurd of mislukt. Vraag de status op met een pauze van een paar seconden ertussen, en stop bij verstuurd of mislukt.
  • Verstuurd betekent dat de mail de deur uit is, niet dat hij in de inbox staat.
  • Bij mislukt staat er een foutcode: model_fout, mail_fout, onderbroken of onbekend. Het rapport is dan niet verstuurd; vraag het opnieuw aan. Een verzoek dat na 30 minuten nog niet klaar is, krijgt onderbroken.

GET /v1/reports/options

De blokken en opgeslagen rapporten waaruit je kunt kiezen, zoals de gebruiker bij de sleutel ze in de Suite ziet.

{
  "blocks": [
    { "block_id": "...", "label": "...", "description": "..." }
  ],
  "templates": [
    { "template_id": "...", "name": "...", "blocks": ["..."], "recipients": "...", "subject": "..." }
  ]
}

Veranderingen

Tabel- en kolomnamen zijn een belofte: de API leest uit een vaste laag die los staat van onze interne opbouw. Moet er toch iets wegvallen of anders gaan heten, dan kondigen we dat vooraf aan bij het e-mailadres waarvoor de sleutel is aangemaakt. Lees nieuwe kolommen daarom op naam en niet op positie, en negeer velden die je niet kent.

MCP-server: technische eisen

Dit zijn de eisen waaraan je MCP-server moet voldoen om onder Koppelingen te werken. De bouwprompt in de Suite bevat dezelfde eisen, aangevuld met de waarden voor jullie omgeving: de redirect-URI, het client-id en het vaste IP-adres.

Het endpoint

  • MCP via Streamable HTTP, meestal op het pad /mcp. Gebruik de officiële MCP-SDK voor je taal.
  • Publiek bereikbaar via HTTPS met een geldig certificaat, op poort 443 of een poort vanaf 1024. Private, interne en link-local adressen worden geweigerd.
  • Geen redirects op het endpoint en op de metadata-adressen: die worden niet gevolgd.
  • Een verzoek zonder geldige inlog of sleutel krijgt HTTP 401, ook voor initialize.

Inloggen met OAuth per gebruiker

Volgt de autorisatie-eisen van de MCP-specificatie (versie 2026-07-28):

  1. De 401 draagt WWW-Authenticate: Bearer resource_metadata="https://<je app>/.well-known/oauth-protected-resource".
  2. Protected Resource Metadata (RFC 9728) op /.well-known/oauth-protected-resource, met resource exact gelijk aan de URL van het MCP-endpoint, authorization_servers en scopes_supported.
  3. Authorization Server Metadata (RFC 8414 of OpenID Connect Discovery) met issuer, authorization_endpoint, token_endpoint, code_challenge_methods_supported: ["S256"] en authorization_response_iss_parameter_supported: true. Een revocation_endpoint (RFC 7009) is welkom: dan trekken we het token in als iemand de verbinding verbreekt.
  4. Clientregistratie, bij voorkeur via een Client ID Metadata Document (client_id_metadata_document_supported: true); anders Dynamic Client Registration (RFC 7591); anders registreer je FloraSpark met de hand als public client en vul je het client-id in de Suite in.
  5. Authorization code met PKCE S256, verplicht; de methode plain wordt niet geaccepteerd.
  6. iss in de autorisatierespons (RFC 9207), naast code en state.
  7. Audience-binding (RFC 8707): we sturen resource mee; geef een token uit voor precies die resource en weiger tokens voor een andere.
  8. Refresh-tokens via grant_type=refresh_token. Roteren mag.
  9. Rechten per tool: elke tool past de rechten toe van de gebruiker van het token, net als in je eigen app.

Inloggen met een vaste sleutel

  • De sleutel staat in een omgevingsvariabele van je app, nooit in code of git.
  • Standaard komt hij binnen als Authorization: Bearer <sleutel>; een eigen header mag ook. Vergelijk in constante tijd en geef 401 bij een ontbrekende of verkeerde sleutel.
  • Gebruik minstens 32 willekeurige tekens: letters, cijfers, - en _. Een beheerder vult hem in de Suite in.
  • Je server ziet geen gebruiker, alleen FloraSpark.

Tools en resultaten

WatGrens
ToolnaamKleine letters, cijfers en _, hoogstens 40 tekens
BeschrijvingHoogstens 950 tekens; langer wordt afgekapt
Invoerschema (inputSchema)type: object, hoogstens 8192 bytes; groter en de tool verschijnt niet
Tools per gebruiker in Spark AIHoogstens 20
Duur van een aanroepHoogstens 20 seconden
Omvang van een resultaatHoogstens 20.000 tekens; langer wordt afgekapt
  • Geef elk invoerveld een description met formaat en voorbeeld, zet verplichte velden in required, en gebruik enum, minimum, maximum en format waar het kan. Spark AI weet van een argument alleen wat in het schema staat.
  • Zet readOnlyHint: true op een tool die alleen leest, en destructiveHint: true op een tool die iets verwijdert of overschrijft. Dat is een voorstel; de goedkeuring in de Suite beslist.
  • Pagineer lange lijsten met een invoerveld voor de pagina of een cursor.
  • Een fout is een resultaat met isError: true en een leesbare zin die het veld en de juiste vorm noemt, geen stacktrace.
  • Zet nooit geheimen of instructies voor de AI in een resultaat. Alles wat een tool teruggeeft, wordt als gegevens behandeld.
  • Houd naam, beschrijving en schema stabiel: elke wijziging haalt de tool uit Spark AI tot hij opnieuw is goedgekeurd.