Met de publieke API van FixControl lees en stuur je FixControl aan vanuit je eigen tools: een eigen dashboard, een planningsscript, een interne automatisering of de eigen chatbot van je organisatie. Het basispad is /api/public/v1/ op je FixControl-host.
Voordat je begint
- Rol — voor het aanmaken en intrekken van API-keys heb je de rol admin of manager in de workspace nodig.
- Eén key per workspace — een key hoort bij de workspace waarin hij is aangemaakt en kan alleen bij de data van die workspace.
Een API-key aanmaken
De pagina voor API-keys is nog Engelstalig; de knoppen hieronder staan daarom met hun Engelse naam.
- Open Instellingen → Kanalen. Open in de sectie Webhooks & API de kaart Publieke API (of ga direct naar /settings/integrations/api).
- Klik bovenaan de pagina op Create API key.
- Geef de key een Name (zodat je weet waarvoor hij is) en vink de Scopes aan (zie hieronder).
issues:readstaat standaard aangevinkt; vink er minstens één aan. - Klik Create key.
- De volledige token (begint met
fck_live_) wordt één keer getoond. Kopieer hem naar je secret manager voordat je op I've copied it klikt. Sluit je het venster, dan is de token voorgoed verborgen.
Daarna toont de lijst de naam van de key, de prefix, de scopes, wanneer hij voor het laatst is gebruikt (Last used) en de status (active of revoked). De volledige token is niet terug te halen. Kwijt? Trek de key in en maak een nieuwe aan.
Scopes
Een key heeft één of meer scopes. Kies de kleinste set die het werk doet.
| Scope | Endpoints |
|---|---|
issues:read | GET /api/public/v1/issues, GET /api/public/v1/issues/:key |
issues:write | POST /api/public/v1/issues |
patches:read | GET /api/public/v1/patches, GET /api/public/v1/patches/:id |
patches:write | Gereserveerd voor toekomstige schrijfacties op patches; vandaag geen endpoints. |
integrations:read | GET /api/public/v1/integrations/status |
webhooks:read | GET /api/public/v1/webhooks |
webhooks:write | POST /api/public/v1/webhooks |
hr:questions | POST /api/public/v1/hr/questions — een HR-vraag indienen namens een medewerker die je chatbot al heeft aangemeld. |
comm:stage | POST /api/public/v1/communications, GET /api/public/v1/communications/:id — een klantmail voorstellen ter goedkeuring door een mens en de status ervan teruglezen. |
Twee scopes zijn een vertrouwensbeslissing, niet alleen een recht:
- `hr:questions` laat de key zeggen welke medewerker de vraag stelt. Geef hem alleen aan een chatbot die zijn gebruikers echt authenticeert. Het vergroot nooit wat die medewerker kan bereiken: FixControl controleert nog steeds het lidmaatschap van de medewerker, diens HR-recht en of HR voor het project aanstaat.
- `comm:stage` laat de key een klantbericht voorstellen. Versturen kan hij niet: elk voorstel wacht in Goedkeuringen tot iemand in FixControl het goedkeurt of afwijst.
Een request naar een endpoint waarvan de key de scope niet heeft, geeft 403 met code: insufficient_scope.
Authenticatie
Elk request heeft een Authorization: Bearer <token>-header nodig. De token hoort bij één workspace; er is geen aparte workspace-parameter, de workspace komt altijd uit de key.
curl https://<jouw-host>/api/public/v1/issues \
-H "Authorization: Bearer fck_live_a1b2c3d4..."| Situatie | Antwoord |
|---|---|
| Geen token | 401, code: missing_token |
| Onbekende of ingetrokken token | 401, code: invalid_token |
| Geldige token zonder de juiste scope | 403, code: insufficient_scope |
Rate limits
Elke key heeft een budget van 60 requests per minuut (één per seconde doorlopend, met een piek van 60). Het budget is per key, dus twee keys in dezelfde workspace delen het niet.
POST /api/public/v1/communications heeft daarbovenop een extra, strenger budget van 10 per minuut per key, omdat elke geaccepteerde aanroep een beoordeling bij een mens neerlegt.
Is een budget op, dan krijg je 429 Too Many Requests met een Retry-After-header (in seconden) en code: rate_limited. Heb je voor een legitiem gebruik meer ruimte nodig, neem dan contact op.
Endpoints
Issues
# Issues van de workspace van de key opvragen
GET /api/public/v1/issues
→ { "items": PublicIssue[] }
# Eén issue ophalen op key
GET /api/public/v1/issues/:key
→ PublicIssue
# Een issue aanmaken
POST /api/public/v1/issues
{
"title": "Checkout faalt op mobiel", // verplicht
"kind": "BUG", // verplicht: BUG | FEATURE | SUPPORT
"priority": "high", // verplicht: critical | high | medium | low
"reporter": "alex@acme.com", // verplicht
"summary": "Optionele samenvatting",
"body": "Optionele lange beschrijving",
"customer": "Optionele klantnaam",
"flags": ["payment"], // maximaal 32 tekst-flags
"assignee": "Optionele interne id"
}
→ 201 Created → PublicIssueEr is geen projectveld: een issue dat je via de API aanmaakt, wordt zonder project aangemaakt. Koppel het daarna in FixControl aan een project als het werk dat nodig heeft.
PublicIssue bevat: key (zoals BIZ-29), tenant, kind, title, summary, body, priority, status, readiness, confidence, flags, reporter, assignee, customer, createdAt, updatedAt, externalTracker, externalKey, externalUrl, externalStatus.
Patches
# Patches van de workspace van de key (nieuwste eerst)
GET /api/public/v1/patches?limit=100 # limit: 1..200
→ { "items": PublicPatch[] }
# Eén patch ophalen
GET /api/public/v1/patches/:id
→ PublicPatchPublicPatch bevat de patch-id, workspace-id, versie, status, het aantal bestanden, toegevoegde en verwijderde regels, de bestandslijst en tijdstempels.
Integraties
GET /api/public/v1/integrations/status
→ { "items": [
{ "id": "...", "type": "github", "name": "...", "status": "connected",
"provider": "github", "authKind": "app",
"createdAt": "...", "lastError": null },
...
] }Er komen geen tokens, refresh-tokens, installation-id's of routeringsregels voor ontvangers mee terug, alleen de publiek-veilige samenvatting.
Webhooks
# Uitgaande webhooks opvragen
GET /api/public/v1/webhooks
→ { "items": PublicWebhook[] }
# Aanmaken. Geeft het secret precies één keer terug.
POST /api/public/v1/webhooks
{
"name": "Billing notifier",
"url": "https://hooks.acme.com/fc",
"events": ["issue.created", "patch.approved"],
"enabled": true
}
→ 201 Created → { "webhook": PublicWebhook, "secret": "ws_AB3cD..." }name, url en minstens één geldig event zijn verplicht. Geldige event-waarden: issue.created, issue.updated, patch.ready, patch.approved, patch.failed, pr.created, integration.error. Zie de webhooks-handleiding voor het leveringsformaat en de signature.
HR-vragen
POST /api/public/v1/hr/questions
{
"projectId": "prj_...", // een project met HR aan
"employeeEmail": "jane@acme.nl", // de medewerker die je chatbot aanmeldde
"question": "Hoe vraag ik ouderschapsverlof aan?",
"idempotencyKey": "chat-msg-8f3k2j1a", // je eigen bericht-id, 8–200 tekens
"locale": "nl" // optioneel: en | nl
}
→ 201 Created → { "status": "...", "decision": "...", "userMessage": "...",
"answerText": "...", "sources": [{ "name": "...", "version": "..." }],
"caseReference": "..." }FixControl behandelt de vraag precies zoals vanuit Teams of Slack: het classificeert hem, controleert de bronnen en beslist of het antwoordt, om verduidelijking vraagt of de vraag naar HR doorzet. answerText en sources zijn er alleen als er een antwoord is gegeven. Een gevoelige zaak geeft zijn referentie niet terug. Stuur je dezelfde idempotencyKey opnieuw, dan krijg je 200 met { "status": "ALREADY_PROCESSED" }. Een onbekende medewerker, een niet-lid en een project zonder HR geven allemaal dezelfde 404 not_found; 409 employee_binding_missing betekent dat de koppeling van de medewerker met het HR-systeem ontbreekt, en HR kan dat oplossen. Zie HR-vragen.
Klantcommunicatie
# Eén klantmail voorstellen ter goedkeuring door een mens
POST /api/public/v1/communications
{
"contactEmail": "info@acme.nl", // of "contactId"; het contact moet bestaan
"subject": "Opvolging van je aanvraag",
"body": "Hallo ...",
"idempotencyKey": "growth-lead-1234", // 8–200 tekens
"generatedByAI": true, // optioneel, standaard false
"sourceContext": { "campaign": "q4" } // optioneel, maximaal 20 eenvoudige waarden
}
→ 201 Created → PublicCommunication
# De status teruglezen
GET /api/public/v1/communications/:id
→ PublicCommunicationPublicCommunication bevat de id, status (pending_approval, sent, rejected, failed of discarded), de goedkeurings-id, de contact- en klant-id, de provider en bericht-id zodra verzonden, en tijdstempels. Het onderwerp en de tekst zitten er nooit in.
- Het contact moet al bestaan in Contacten; een onbekend adres geeft
404 contact_not_found. - Stuur je dezelfde
idempotencyKeyopnieuw, dan krijg je200met de bestaande communicatie in plaats van een tweede goedkeuring. generatedByAI: truekan de beoordeling alleen strenger maken.500 stage_failedbetekent dat er niets is blijven openstaan; probeer opnieuw met dezelfdeidempotencyKey.
Foutantwoorden
Elke fout geeft JSON in deze vorm:
{ "error": "Leesbare melding", "code": "stabiele_machine_code" }De HTTP-status geeft de categorie; code is de stabiele identifier voor je client-logica.
| Status | Voorbeelden van code |
|---|---|
| 400 | missing_field, invalid_field, invalid_name, invalid_url, invalid_events, invalid_body |
| 401 | missing_token, invalid_token |
| 403 | insufficient_scope |
| 404 | not_found, contact_not_found |
| 409 | employee_binding_missing |
| 422 | missing_recipient_email |
| 429 | rate_limited (met Retry-After-header) |
| 5xx | internal_error, stage_failed |
OpenAPI
De OpenAPI 3.1-spec is zonder authenticatie beschikbaar op:
GET /api/public/v1/openapi.jsonLaat je codegenerator hiernaar wijzen. Hij beschrijft elk endpoint op deze pagina.
Een key intrekken
- Open Instellingen → Kanalen → Publieke API.
- Klik op het prullenbak-icoon (Revoke) in de rij van de key en bevestig.
Intrekken werkt direct: keys worden bij elk request opnieuw in de database opgezocht, zonder cache, dus het volgende request met die key krijgt 401 invalid_token. De rij blijft in de lijst staan met status revoked. Kijk daarna in de audit-log naar activiteit met die key.
Goede gewoonten
- Behandel de token als een wachtwoord. Nooit committen, nooit in logs zetten.
- Eén key per integratie. Makkelijker te vervangen, te auditen en te scopen.
- Vervang keys regelmatig. Maak de nieuwe key, zet je client om, en trek daarna pas de oude in.
- Liever webhooks dan pollen. Abonneer een uitgaande webhook in plaats van
GET /issueste pollen. - Behandel 429 met backoff. Volg de
Retry-After-header in plaats van direct opnieuw te proberen.
Veelgestelde vragen
Is de API hetzelfde als de interne API van het dashboard? Nee. Het dashboard praat met interne routes die een sessiecookie nodig hebben en niet stabiel zijn. De publieke API op /api/public/v1/... is het geversioneerde contract met OpenAPI-beschrijving.
Kan een key bij meer dan één workspace? Nee. Een key hoort bij één workspace. Werk je in meerdere workspaces, dan heb je per workspace een key nodig.
Kan een API-key een klantbericht goedkeuren of versturen? Nee. Een key met comm:stage kan er alleen een voorstellen. Goedkeuren en versturen doet een mens in FixControl.