FixControl/Documentatie

Gebruikershandleiding

Publieke API

Tenant-gescopete REST-API voor issues, patches, integraties en webhooks. Bearer-token-authenticatie.

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.

  1. Open Instellingen → Kanalen. Open in de sectie Webhooks & API de kaart Publieke API (of ga direct naar /settings/integrations/api).
  2. Klik bovenaan de pagina op Create API key.
  3. Geef de key een Name (zodat je weet waarvoor hij is) en vink de Scopes aan (zie hieronder). issues:read staat standaard aangevinkt; vink er minstens één aan.
  4. Klik Create key.
  5. 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.

ScopeEndpoints
issues:readGET /api/public/v1/issues, GET /api/public/v1/issues/:key
issues:writePOST /api/public/v1/issues
patches:readGET /api/public/v1/patches, GET /api/public/v1/patches/:id
patches:writeGereserveerd voor toekomstige schrijfacties op patches; vandaag geen endpoints.
integrations:readGET /api/public/v1/integrations/status
webhooks:readGET /api/public/v1/webhooks
webhooks:writePOST /api/public/v1/webhooks
hr:questionsPOST /api/public/v1/hr/questions — een HR-vraag indienen namens een medewerker die je chatbot al heeft aangemeld.
comm:stagePOST /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..."
SituatieAntwoord
Geen token401, code: missing_token
Onbekende of ingetrokken token401, code: invalid_token
Geldige token zonder de juiste scope403, 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 → PublicIssue

Er 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
→ PublicPatch

PublicPatch 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
→ PublicCommunication

PublicCommunication 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 idempotencyKey opnieuw, dan krijg je 200 met de bestaande communicatie in plaats van een tweede goedkeuring.
  • generatedByAI: true kan de beoordeling alleen strenger maken.
  • 500 stage_failed betekent dat er niets is blijven openstaan; probeer opnieuw met dezelfde idempotencyKey.

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.

StatusVoorbeelden van code
400missing_field, invalid_field, invalid_name, invalid_url, invalid_events, invalid_body
401missing_token, invalid_token
403insufficient_scope
404not_found, contact_not_found
409employee_binding_missing
422missing_recipient_email
429rate_limited (met Retry-After-header)
5xxinternal_error, stage_failed

OpenAPI

De OpenAPI 3.1-spec is zonder authenticatie beschikbaar op:

GET /api/public/v1/openapi.json

Laat je codegenerator hiernaar wijzen. Hij beschrijft elk endpoint op deze pagina.

Een key intrekken

  1. Open Instellingen → Kanalen → Publieke API.
  2. 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 /issues te 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.

Iets onduidelijk of fout?Laat het ons weten →

FixControl is een handelsnaam van FixControl B.V.