Aan de slag
Authenticatie
Tenant-API-keys, de bearer-header, scopes, het /v1/api/me endpoint en tenant-suspensie.
Bekijk als MarkdownElke request naar een /v1/api-route wordt geauthenticeerd met een tenant-API-key. De key is een bearer token dat bij één winkel (tenant) hoort: hij authenticeert de request én selecteert de winkel, dus een aparte tenant-identifier of header is nooit nodig. Deze pagina behandelt hoe keys werken, welke scopes er zijn, het identity-endpoint en wat er gebeurt wanneer een winkel wordt geschorst.
Elke request naar een /v1/api-route wordt geauthenticeerd met een tenant-API-key. De key is een bearer token dat bij één winkel (tenant) hoort: hij authenticeert de request én selecteert de winkel, dus een aparte tenant-identifier of header is nooit nodig. Deze pagina behandelt hoe keys werken, welke scopes er zijn, het identity-endpoint en wat er gebeurt wanneer een winkel wordt geschorst.
API keys
Een API-key is een langlevend geheim token dat voor één winkel wordt uitgegeven. Keys verlopen niet; ze blijven geldig tot de merchant ze intrekt. De plaintext key wordt precies één keer getoond, bij het aanmaken. Sla hem direct op in een secret manager; hij kan niet opnieuw worden opgevraagd.
Merchants maken keys aan en trekken ze in via het client-dashboard (app.borkol.com) onder Settings > API keys. Bij het aanmaken kiest de merchant een naam en een set scopes voor de key. Het dashboard toont per key de naam, scopes, het laatste gebruiksmoment en het aanmaakmoment, en kan een key op elk moment intrekken. Een ingetrokken key werkt onmiddellijk niet meer: requests ermee geven 401 {"message": "Unauthenticated."} terug.
Stuur de key bij elke request mee in de Authorization-header:
Er is geen X-Tenant-Id-header op /v1/api-routes en er wordt er geen gelezen. De winkelcontext wordt volledig afgeleid uit de key.
Authorization: Bearer {api_key}API keys
Een API-key is een langlevend geheim token dat voor één winkel wordt uitgegeven. Keys verlopen niet; ze blijven geldig tot de merchant ze intrekt. De plaintext key wordt precies één keer getoond, bij het aanmaken. Sla hem direct op in een secret manager; hij kan niet opnieuw worden opgevraagd.
Merchants maken keys aan en trekken ze in via het client-dashboard (app.borkol.com) onder Settings > API keys. Bij het aanmaken kiest de merchant een naam en een set scopes voor de key. Het dashboard toont per key de naam, scopes, het laatste gebruiksmoment en het aanmaakmoment, en kan een key op elk moment intrekken. Een ingetrokken key werkt onmiddellijk niet meer: requests ermee geven 401 {"message": "Unauthenticated."} terug.
Stuur de key bij elke request mee in de Authorization-header:
Authorization: Bearer {api_key}Er is geen X-Tenant-Id-header op /v1/api-routes en er wordt er geen gelezen. De winkelcontext wordt volledig afgeleid uit de key.
Scopes
Elke key draagt een set scopes die bij het aanmaken zijn toegekend. Scopes bewaken endpoints: een route die een scope vereist die de key niet draagt, geeft 403 {"message": "Invalid ability provided."} terug.
Scopenamen volgen het patroon {module}:{read|write}. De volledige set:
| Scope | Geeft toegang tot |
|---|---|
tenant:read | Basale toegang tot de winkelidentiteit (GET /v1/api/business, de redirect- en sitemap-endpoints). Altijd toekenbaar. |
catalog:read | Producten, categorieën en de configurator-endpoints. commerce:read wordt op dezelfde routes geaccepteerd. |
catalog:write | Toekenbaar; nog geen /v1/api-route gebruikt hem. |
commerce:read | Winkelwagens (lezen), bestellingen, en overal waar catalog:read of sales:read wordt geaccepteerd. |
commerce:write | Winkelwagens (schrijven), checkout, offerteaanvragen, en overal waar sales:write wordt geaccepteerd. |
configurator:read / configurator:write | Toekenbaar; de configurator-endpoints controleren catalog:read of commerce:read, niet deze. |
contacts:read | GET-endpoints van klantaccounts (/v1/api/contacts/*). |
contacts:write | Registratie, inloggen, uitloggen, wachtwoordherstel, profiel- en adreswijzigingen van klanten. |
customers:read / customers:write | De vroegere namen van contacts:read / contacts:write. Nog steeds toekenbaar en geaccepteerd op dezelfde routes, zodat keys van vóór de hernoeming blijven werken. |
sales:read | Een offerte lezen via token (GET /v1/api/sales/quotes/by-token/{token}). |
sales:write | Een offerte accepteren of afwijzen via token. |
stock:read | Voorraadniveaus (GET /v1/api/stock/*). |
stock:write | Toekenbaar; nog geen /v1/api-route gebruikt hem. |
objects:read | Objecttypes en -records (GET /v1/api/objects/*), en de sitemap. |
objects:write | Toekenbaar; nog geen /v1/api-route gebruikt hem. |
forms:read | Gepubliceerde formulieren laden (GET /v1/api/forms/{key}) en de adreslookup. |
forms:write | Bestanden uploaden voor bestandsvelden van formulieren (POST /v1/api/forms/{key}/uploads) en formulierinzendingen versturen (POST /v1/api/forms/{key}/submissions). Alleen op de server; zie hieronder. |
Een module zonder publieke surface (payroll) biedt haar permissienamen ook als scopes aan; geen enkele /v1/api-route gebruikt ze. Waar een route twee scopes noemt, volstaat een key met één van beide: de oudere commerce:*-familie dateert van vóór de splitsing van de commerce-module in catalog, commerce en sales, en wordt op alle drie geaccepteerd.
Een merchant kan alleen scopes toekennen voor modules die op dat moment voor zijn winkel zijn ingeschakeld: de toekenbare set is altijd tenant:read plus het {module}:read- en {module}:write-paar van elke ingeschakelde module. Het aanvragen van een niet-beschikbare scope bij het aanmaken van een key faalt met een 422 in de dashboardflow.
Houd forms:write-sleutels op je server. Met een sleutel met forms:write verstuur je inzendingen, en elke inzending verstuurt mail namens de merchant. Lever hem nooit uit aan een browser of een mobiele app: laad en verstuur formulieren vanaf je eigen server, en gebruik aparte sleutels voor het laden (forms:read) en het versturen (forms:write). Zie Formulieren.
Scopes en module-inschakeling worden onafhankelijk van elkaar afgedwongen. Zelfs wanneer een key commerce:read draagt, geeft het aanroepen van een commerce-route terwijl de commerce-module voor de winkel is uitgeschakeld 403 {"error": "Module not enabled: commerce"} terug. Lees-endpoints vereisen de read-scope van de module; create-, update- en delete-endpoints vereisen de write-scope van de module. De referentiepagina van elk endpoint vermeldt de vereiste scope.
Scopes
Elke key draagt een set scopes die bij het aanmaken zijn toegekend. Scopes bewaken endpoints: een route die een scope vereist die de key niet draagt, geeft 403 {"message": "Invalid ability provided."} terug.
Scopenamen volgen het patroon {module}:{read|write}. De volledige set:
| Scope | Geeft toegang tot |
|---|---|
tenant:read | Basale toegang tot de winkelidentiteit (GET /v1/api/business, de redirect- en sitemap-endpoints). Altijd toekenbaar. |
catalog:read | Producten, categorieën en de configurator-endpoints. commerce:read wordt op dezelfde routes geaccepteerd. |
catalog:write | Toekenbaar; nog geen /v1/api-route gebruikt hem. |
commerce:read | Winkelwagens (lezen), bestellingen, en overal waar catalog:read of sales:read wordt geaccepteerd. |
commerce:write | Winkelwagens (schrijven), checkout, offerteaanvragen, en overal waar sales:write wordt geaccepteerd. |
configurator:read / configurator:write | Toekenbaar; de configurator-endpoints controleren catalog:read of commerce:read, niet deze. |
contacts:read | GET-endpoints van klantaccounts (/v1/api/contacts/*). |
contacts:write | Registratie, inloggen, uitloggen, wachtwoordherstel, profiel- en adreswijzigingen van klanten. |
customers:read / customers:write | De vroegere namen van contacts:read / contacts:write. Nog steeds toekenbaar en geaccepteerd op dezelfde routes, zodat keys van vóór de hernoeming blijven werken. |
sales:read | Een offerte lezen via token (GET /v1/api/sales/quotes/by-token/{token}). |
sales:write | Een offerte accepteren of afwijzen via token. |
stock:read | Voorraadniveaus (GET /v1/api/stock/*). |
stock:write | Toekenbaar; nog geen /v1/api-route gebruikt hem. |
objects:read | Objecttypes en -records (GET /v1/api/objects/*), en de sitemap. |
objects:write | Toekenbaar; nog geen /v1/api-route gebruikt hem. |
forms:read | Gepubliceerde formulieren laden (GET /v1/api/forms/{key}) en de adreslookup. |
forms:write | Bestanden uploaden voor bestandsvelden van formulieren (POST /v1/api/forms/{key}/uploads) en formulierinzendingen versturen (POST /v1/api/forms/{key}/submissions). Alleen op de server; zie hieronder. |
Een module zonder publieke surface (payroll) biedt haar permissienamen ook als scopes aan; geen enkele /v1/api-route gebruikt ze. Waar een route twee scopes noemt, volstaat een key met één van beide: de oudere commerce:*-familie dateert van vóór de splitsing van de commerce-module in catalog, commerce en sales, en wordt op alle drie geaccepteerd.
Een merchant kan alleen scopes toekennen voor modules die op dat moment voor zijn winkel zijn ingeschakeld: de toekenbare set is altijd tenant:read plus het {module}:read- en {module}:write-paar van elke ingeschakelde module. Het aanvragen van een niet-beschikbare scope bij het aanmaken van een key faalt met een 422 in de dashboardflow.
Houd forms:write-sleutels op je server. Met een sleutel met forms:write verstuur je inzendingen, en elke inzending verstuurt mail namens de merchant. Lever hem nooit uit aan een browser of een mobiele app: laad en verstuur formulieren vanaf je eigen server, en gebruik aparte sleutels voor het laden (forms:read) en het versturen (forms:write). Zie Formulieren.
Scopes en module-inschakeling worden onafhankelijk van elkaar afgedwongen. Zelfs wanneer een key commerce:read draagt, geeft het aanroepen van een commerce-route terwijl de commerce-module voor de winkel is uitgeschakeld 403 {"error": "Module not enabled: commerce"} terug. Lees-endpoints vereisen de read-scope van de module; create-, update- en delete-endpoints vereisen de write-scope van de module. De referentiepagina van elk endpoint vermeldt de vereiste scope.
Verwerkingsvolgorde van requests
Middleware draait in een vaste volgorde op elke /v1/api-request. De volgorde kennen helpt bij het interpreteren van error-responses:
- Authenticatie: het bearer token wordt herleid tot een winkel. Ontbrekend, misvormd of ingetrokken token:
401 {"message": "Unauthenticated."}. - Tenancy en suspensie: de winkelcontext wordt geïnitialiseerd vanuit de key. Geschorste winkel:
403 {"message": "Tenant suspended.", "code": "tenant_suspended"}. - Rate limiting: 120 requests per minuut per winkel. Overschreden:
429 {"message": "Too Many Attempts."}. - Module-gate en scope-check (alleen module-routes): beide draaien na de bovenstaande stappen, maar hun onderlinge volgorde verschilt per route. Uitgeschakelde module:
403 {"error": "Module not enabled: {key}"}. Ontbrekende ability:403 {"message": "Invalid ability provided."}. De checks zijn onafhankelijk; wanneer beide zouden falen, kan elk van de twee errors als eerste optreden.
Verwerkingsvolgorde van requests
Middleware draait in een vaste volgorde op elke /v1/api-request. De volgorde kennen helpt bij het interpreteren van error-responses:
- Authenticatie: het bearer token wordt herleid tot een winkel. Ontbrekend, misvormd of ingetrokken token:
401 {"message": "Unauthenticated."}. - Tenancy en suspensie: de winkelcontext wordt geïnitialiseerd vanuit de key. Geschorste winkel:
403 {"message": "Tenant suspended.", "code": "tenant_suspended"}. - Rate limiting: 120 requests per minuut per winkel. Overschreden:
429 {"message": "Too Many Attempts."}. - Module-gate en scope-check (alleen module-routes): beide draaien na de bovenstaande stappen, maar hun onderlinge volgorde verschilt per route. Uitgeschakelde module:
403 {"error": "Module not enabled: {key}"}. Ontbrekende ability:403 {"message": "Invalid ability provided."}. De checks zijn onafhankelijk; wanneer beide zouden falen, kan elk van de twee errors als eerste optreden.
Tenant-suspensie
Borkol kan een winkel schorsen. Zolang de winkel geschorst is, geeft elke /v1/api-request met de keys van die winkel, ongeacht scope of endpoint, dit terug:
met HTTP-status 403. Het code-veld is stabiel en machine-leesbaar; controleer op tenant_suspended om deze 403 te onderscheiden van scope- en module-errors. Suspensie maakt keys niet ongeldig: wanneer de schorsing wordt opgeheven, werken bestaande keys ongewijzigd weer.
{
"message": "Tenant suspended.",
"code": "tenant_suspended"
}Tenant-suspensie
Borkol kan een winkel schorsen. Zolang de winkel geschorst is, geeft elke /v1/api-request met de keys van die winkel, ongeacht scope of endpoint, dit terug:
{
"message": "Tenant suspended.",
"code": "tenant_suspended"
}met HTTP-status 403. Het code-veld is stabiel en machine-leesbaar; controleer op tenant_suspended om deze 403 te onderscheiden van scope- en module-errors. Suspensie maakt keys niet ongeldig: wanneer de schorsing wordt opgeheven, werken bestaande keys ongewijzigd weer.
GET /v1/api/me
Geeft de identiteit terug van de winkel waar de aangeboden API-key bij hoort, plus de scopes die aan die key zijn toegekend. Dit is de canonieke eerste aanroep om te verifiëren dat een key werkt en om zijn abilities te inspecteren. Het endpoint vereist geen scope; elke geldige key kan het aanroepen.
Parameters
Geen. Alleen de header Authorization: Bearer {api_key} is vereist.
Responsevelden
| Naam | Type | Verplicht | Beschrijving |
|---|---|---|---|
tenant | object | altijd | De winkel waar de key bij hoort. |
tenant.id | string (UUID) | altijd | Winkel-ID. |
tenant.name | string | altijd | Weergavenaam van de winkel. |
tenant.slug | string | altijd | URL-veilige winkelidentifier. |
scopes | array van strings | altijd | De scopes die aan deze key zijn toegekend, bijvoorbeeld ["tenant:read", "commerce:read"]. |
Errors
| Status | Body | Wanneer |
|---|---|---|
| 401 | {"message": "Unauthenticated."} | Bearer token ontbreekt, is misvormd of ingetrokken. |
| 403 | {"message": "Tenant suspended.", "code": "tenant_suspended"} | De winkel is geschorst. |
| 429 | {"message": "Too Many Attempts."} | Meer dan 120 requests in de huidige minuut. |
curl https://api.borkol.com/v1/api/me \
-H "Authorization: Bearer {api_key}"const res = await fetch("https://api.borkol.com/v1/api/me", {
headers: { Authorization: `Bearer ${apiKey}` },
});
const me = await res.json();{
"tenant": {
"id": "9c4e7f0a-2b31-4d6e-8f0a-1d2c3b4a5e6f",
"name": "Example Store",
"slug": "example-store"
},
"scopes": ["tenant:read", "commerce:read", "commerce:write", "stock:read"]
}GET /v1/api/me
Geeft de identiteit terug van de winkel waar de aangeboden API-key bij hoort, plus de scopes die aan die key zijn toegekend. Dit is de canonieke eerste aanroep om te verifiëren dat een key werkt en om zijn abilities te inspecteren. Het endpoint vereist geen scope; elke geldige key kan het aanroepen.
Parameters
Geen. Alleen de header Authorization: Bearer {api_key} is vereist.
Responsevelden
| Naam | Type | Verplicht | Beschrijving |
|---|---|---|---|
tenant | object | altijd | De winkel waar de key bij hoort. |
tenant.id | string (UUID) | altijd | Winkel-ID. |
tenant.name | string | altijd | Weergavenaam van de winkel. |
tenant.slug | string | altijd | URL-veilige winkelidentifier. |
scopes | array van strings | altijd | De scopes die aan deze key zijn toegekend, bijvoorbeeld ["tenant:read", "commerce:read"]. |
curl https://api.borkol.com/v1/api/me \
-H "Authorization: Bearer {api_key}"const res = await fetch("https://api.borkol.com/v1/api/me", {
headers: { Authorization: `Bearer ${apiKey}` },
});
const me = await res.json();{
"tenant": {
"id": "9c4e7f0a-2b31-4d6e-8f0a-1d2c3b4a5e6f",
"name": "Example Store",
"slug": "example-store"
},
"scopes": ["tenant:read", "commerce:read", "commerce:write", "stock:read"]
}Errors
| Status | Body | Wanneer |
|---|---|---|
| 401 | {"message": "Unauthenticated."} | Bearer token ontbreekt, is misvormd of ingetrokken. |
| 403 | {"message": "Tenant suspended.", "code": "tenant_suspended"} | De winkel is geschorst. |
| 429 | {"message": "Too Many Attempts."} | Meer dan 120 requests in de huidige minuut. |
GET /v1/ping
Ongeauthenticeerde health check voor API v1. Er gelden geen credentials, geen winkelcontext, geen scopes en geen tenant-rate-limit. Gebruik het voor uptime-monitoring en om connectiviteit en de base URL te verifiëren voordat je credentials aansluit.
Parameters
Geen.
Responsevelden
| Naam | Type | Verplicht | Beschrijving |
|---|---|---|---|
status | string | altijd | Altijd "ok". |
version | string | altijd | Altijd "v1". |
Errors
Geen die specifiek zijn voor deze route.
curl https://api.borkol.com/v1/ping{
"status": "ok",
"version": "v1"
}GET /v1/ping
Ongeauthenticeerde health check voor API v1. Er gelden geen credentials, geen winkelcontext, geen scopes en geen tenant-rate-limit. Gebruik het voor uptime-monitoring en om connectiviteit en de base URL te verifiëren voordat je credentials aansluit.
Parameters
Geen.
Responsevelden
| Naam | Type | Verplicht | Beschrijving |
|---|---|---|---|
status | string | altijd | Altijd "ok". |
version | string | altijd | Altijd "v1". |
curl https://api.borkol.com/v1/ping{
"status": "ok",
"version": "v1"
}Errors
Geen die specifiek zijn voor deze route.