Naslag

REST API

Eén API voor elke resource: sessie-, token- en publieke toegang, lezen, schrijven, export, import en bestanden.

Elke resource heeft dezelfde REST API. Definieer een resource en hij antwoordt op /api/v1/<resource>: tonen, lezen, aanmaken, wijzigen, verwijderen, exporteren en importeren, met de rechten van wie de aanroep doet. Leer het voor één resource en je kent het voor allemaal. De voorbeelden op deze pagina gebruiken projects.

Request Doet Recht
GET /api/v1/projects Records tonen, met filteren, sorteren, zoeken en pagineren view-projects
GET /api/v1/projects/<uuid> Eén record lezen view-projects
POST /api/v1/projects Een record aanmaken create-projects
POST /api/v1/projects/<uuid> Een record aanmaken met een id die je zelf kiest create-projects
PUT of PATCH /api/v1/projects/<uuid> De velden wijzigen die je stuurt edit-projects
PUT of PATCH /api/v1/projects Meerdere records in één request wijzigen edit-projects
DELETE /api/v1/projects/<uuid> Eén record verwijderen delete-projects
DELETE /api/v1/projects Alle records verwijderen api_delete_resource_projects
GET /api/v1/projects/export Alle records downloaden als JSON, CSV of TSV export-projects
POST /api/v1/projects/import Records aanmaken en wijzigen vanuit een bestand import-projects

manage-projects omvat elke handeling behalve alle records in één keer verwijderen; dat staat alleen het eigen recht daarvoor toe.

Wie de aanroep doet

Er zijn drie manieren om binnen te komen, en de rechten zijn voor elk hetzelfde.

Aanroeper Logt in met Gebruikelijk voor
Je eigen pagina's en scripts De sessie van de ingelogde gebruiker Formulieren, inline bewerken, een applicatie op een route
Een ander systeem Een token Een app, een server, een integratie
Een anonieme bezoeker Een uitdrukkelijke toestemming per handeling Een contact- of aanmeldformulier

Vanaf je eigen pagina's: nb.api

Code op de site zelf heeft geen token nodig. De browser stuurt de sessie van de ingelogde gebruiker mee, en de API past de rechten van die gebruiker toe, precies zoals in de admin. De pagina-shell levert een kleine client, nb.api, en het basisadres van de site in nb.base_url:

const list = await nb.api.get(`${nb.base_url}/api/v1/projects?filter=published:1&limit=12`);

const created = await nb.api.post(`${nb.base_url}/api/v1/projects`, {
    title: 'Harbour Atlas',
    sector: 'public',
});

await nb.api.put(`${nb.base_url}/api/v1/projects/${uuid}`, { published: true });
await nb.api.delete(`${nb.base_url}/api/v1/projects/${uuid}`);

Elke aanroep geeft het ingelezen JSON-antwoord terug, ook bij een fout, dus controleer success:

const answer = await nb.api.post(`${nb.base_url}/api/v1/leads`, form);
if (!answer.success) {
    show_error(answer.message);
}

Elke andere client werkt op dezelfde manier, zolang hij de sessiecookie meestuurt: fetch(url, { credentials: 'same-origin' }) vanuit React, Vue of gewoon JavaScript. Een bezoeker die niet is ingelogd, heeft de rechten van een anonieme bezoeker, en dat zijn er standaard geen.

Vanuit een ander systeem: tokens

Een externe aanroeper logt in als Nimbly-gebruiker en krijgt een token.

1. Geef de aanroeper een eigen gebruiker. Maak een rol met alleen de rechten die nodig zijn, bijvoorbeeld view-projects voor een feed, en een gebruiker met die rol. Zet API access aan in het actiepaneel van de gebruiker in de admin, naast het bewerkformulier op /nb-admin/users/<uuid>; daarvoor is een rol nodig die gebruikers mag bewerken. Zet je het uit, dan werkt het token van die gebruiker meteen niet meer.

2. Vraag een token aan:

curl -X POST https://example.com/api/v1/auth/token \
  -H "Content-Type: application/json" \
  -d '{"email": "feed@example.com", "password": "…"}'
{
  "token": "anSojGGKOveDuIypVGbx…",
  "token_created": 1791940697,
  "token_expires": 1792075420,
  "code": 200,
  "success": true,
  "status": "ok"
}

3. Stuur het mee met elk request:

curl https://example.com/api/v1/projects \
  -H "Authorization: Bearer $TOKEN"

Een token is 10 minuten geldig na de laatste vernieuwing. Vernieuw het voor die tijd met een GET op hetzelfde adres:

curl https://example.com/api/v1/auth/token -H "Authorization: Bearer $TOKEN"

Een fout wachtwoord geeft 401 INVALID_CREDENTIALS; een gebruiker zonder API-toegang geeft 403 ACCESS_DENIED. Bewaar de inloggegevens in de eigen geheimenkluis van de aanroeper, nooit in een repository.

Vanaf anonieme bezoekers: één handeling tegelijk

Een publiek formulier moet records kunnen aanmaken zonder dat iemand inlogt. Sta precies die handeling toe, en verder niets, met een route op ext/uri/api/v1/<resource>/index.tpl:

[#api-allow post leads#]

Elke andere handeling op de resource blijft dicht. Formulieren die met [#build-form#] zijn gemaakt, voegen een formuliersleutel en een honeypotveld toe, die de API controleert. Zie Formulieren.

Lezen

Een lijst

GET /api/v1/projects?filter=published:1&sort=year|numeric|desc&limit=12&offset=0
{
  "projects": {
    "4f1c9a2e7b": {"uuid": "4f1c9a2e7b", "title": "Harbour Atlas", "sector": "public", "year": 2026, "published": true, "_created": 1792074820, "_modified": 1792074830},
    "9d03b6e1c4": {"uuid": "9d03b6e1c4", "title": "Northlight Library", "sector": "culture", "year": 2026, "published": true, "_created": 1792074821, "_modified": 1792074821}
  },
  "count": 2,
  "total": 9,
  "code": 200,
  "success": true,
  "status": "ok"
}

Records zijn een object met de UUID als sleutel. count is het aantal dat terugkomt; total, aanwezig als je filtert, sorteert of pagineert, is het aantal vóór het afknippen.

Parameter Voorbeeld Doet
filter status:open, status:!draft, status:new||todo, published:1,sector:public Houdt passende records over. ! keert om, || is of, een komma is en
sort year|numeric|desc, title|text|asc, date|desc,title|asc Sorteert op één of meer velden
limit, offset limit=20&offset=40 Eén pagina: de derde pagina van 20
search search=harbour Records waarvan de titel de tekst bevat; in een resource zonder titelveld, elk veld

Een gefilterde, gesorteerde of gepagineerde lijst leest alleen de records op die pagina, ook bij een grote resource. Filteren of sorteren op een veld dat de API nooit teruggeeft, wordt geweigerd met 400.

Eén record

GET /api/v1/projects/4f1c9a2e7b

Het antwoord heeft dezelfde vorm als een lijst, met één record.

Ongewijzigde data

Leesacties antwoorden met een header Last-Modified. Stuur die terug als If-Modified-Since, dan antwoordt een ongewijzigd record of een ongewijzigde lijst met 304 Not Modified, zonder inhoud.

Schrijven

Stuur JSON met Content-Type: application/json. Waarden worden gevalideerd tegen de definitie van de resource, opgemaakte tekst wordt opgeschoond tot de toegestane markup, en elke wijziging komt in de geschiedenis van het record, met de gebruiker die hem deed.

Aanmaken

POST /api/v1/projects
{"title": "Harbour Atlas", "sector": "public", "year": 2026}
{
  "projects": {"1ztuvo56o5vze6t0": {"uuid": "1ztuvo56o5vze6t0", "title": "Harbour Atlas", "sector": "public", "year": 2026}},
  "count": 1,
  "message": "RESOURCE_CREATED",
  "code": 201,
  "success": true,
  "status": "ok"
}

Nimbly maakt de UUID. Wil je een id uit een ander systeem gebruiken, zet die dan in het adres, POST /api/v1/projects/erp-1042, of stuur hem mee als uuid in de inhoud. Een id die al bestaat, geeft 409 RESOURCE_EXISTS; een ontbrekend verplicht veld of een ongeldige waarde geeft 422 INVALID_DATA.

Wijzigen

PUT /api/v1/projects/1ztuvo56o5vze6t0
{"published": true}

Alleen de velden die je stuurt, veranderen. PATCH doet precies hetzelfde; gebruik wat je client verwacht. Het antwoord bevat het hele record zoals het nu is, met RESOURCE_UPDATED. Een resource met "upsert": true in de .meta maakt het record aan als het nog niet bestaat.

Meerdere records in één request, met de UUID als sleutel:

PUT /api/v1/projects
{
  "1ztuvo56o5vze6t0": {"published": true},
  "9d03b6e1c4": {"published": false}
}

Verwijderen

DELETE /api/v1/projects/1ztuvo56o5vze6t0

geeft RESOURCE_DELETED. Het record is nog 90 dagen terug te zetten vanuit de geschiedenis in de admin. DELETE /api/v1/projects verwijdert alle records en heeft een eigen recht nodig, api_delete_resource_projects, dat in geen enkel ander recht zit.

Exporteren en importeren

Exporteren

GET /api/v1/projects/export?format=csv

Downloadt alle records als een bestand met de naam van de resource en de datum. format is json (de standaard), csv of tsv. De JSON-export bevat de definitie van de resource als _meta naast de records, zodat je de resource ergens anders opnieuw kunt maken. Velden die de API nooit teruggeeft, blijven weg.

Importeren

curl -X POST https://example.com/api/v1/projects/import \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@projects.csv"
{"stats": {"imported": 2, "errors": 0, "created": 1, "updated": 1, "total": 2}, "code": 200, "success": true, "status": "ok"}

Het bestand is JSON (een export, een object met records, of een lijst), CSV of TSV met een kopregel met veldnamen. Een rij wijzigt een bestaand record als de uuid past, en anders de id, name of title; andere rijen maken nieuwe records. Een JSON-export die wordt geïmporteerd in een site zonder die resource, maakt de resource aan vanuit de _meta.

Een document importeren

POST /api/v1/projects/import-document met een .docx-bestand stelt veldwaarden voor een nieuw record voor, door een AI-model uit het document gelezen. Dit is wat de actie Import document in de admin gebruikt. Het heeft create-projects en een OpenAI-sleutel in .env nodig, en maakt zelf niets aan.

Bestanden

Uploads gaan via .files naar de mediabibliotheek:

curl -X POST https://example.com/api/v1/.files \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@cover.jpg"
{
  "files": {"uuid": "415efabde63801837fd88c62078f5db0", "name": "cover.jpg", "type": "image/jpeg", "size": 248113, "width": 2400, "height": 1600, "orientation": "landscape"},
  "message": "RESOURCE_CREATED",
  "code": 201
}

De UUID is de checksum van het bestand, dus hetzelfde bestand twee keer uploaden geeft dezelfde UUID. Bewaar die in een veld image, gallery of file, net als elke andere waarde.

Request Doet
GET /api/v1/.files Alle bestanden tonen met hun metadata
GET /api/v1/.files/<uuid> De metadata van één bestand lezen
PUT /api/v1/.files/<uuid> Metadata wijzigen, zoals title
DELETE /api/v1/.files/<uuid> Het bestand, de metadata en de verkleinde kopieën verwijderen
GET /img/<uuid>/<size> De afbeelding, verkleind: 800w, 400h, 400x300c (uitsnede), 400x300f (passend), als WebP
GET /download/<uuid> Het bestand zoals geüpload, met naam en type

Rechten voor bestanden stel je in op de rij Files van het rollenscherm. Een bestand dat groter is dan de uploadlimiet van de server, geeft 413 UPLOAD_TOO_LARGE.

Antwoorden en fouten

Elk antwoord is JSON met code, success en status naast de data:

{
  "message": "ACCESS_DENIED",
  "needs": "api_get_projects,api_(any)_projects,api_get_(any),api_(any),view-projects,manage-projects",
  "code": 403,
  "success": false,
  "status": "error"
}
Code Melding Betekent
200 RESOURCE_UPDATED, RESOURCE_DELETED Gedaan
201 RESOURCE_CREATED Aangemaakt
304 Niet gewijzigd sinds If-Modified-Since
400 INVALID_DATA Het request klopt niet, of filtert op een verborgen veld
401 INVALID_CREDENTIALS Fout e-mailadres of wachtwoord bij een tokenaanvraag
403 ACCESS_DENIED Niet toegestaan; needs noemt de rechten die het zouden toestaan
404 RESOURCE_NOT_FOUND Die resource of dat record bestaat niet
405 METHOD_NOT_ALLOWED De resource ondersteunt deze methode niet
409 RESOURCE_EXISTS Er bestaat al een record met deze id
413 UPLOAD_TOO_LARGE Het bestand is groter dan de uploadlimiet
422 INVALID_DATA Een waarde voldoet niet aan de validatie van de resource
500 RESOURCE_CREATE_FAILED, RESOURCE_UPDATE_FAILED De server kon niet schrijven

Controleer success en code in plaats van één bepaalde melding.

Wat de API nooit teruggeeft

Velden in hidden in de .meta, en eenzijdig versleutelde velden zoals wachtwoorden, worden bewaard en gebruikt, maar verschijnen nooit in een lijst, een record, een antwoord op een schrijfactie of een export. Bij gebruikers zijn het wachtwoord, de salt, het API-token en de hersteltokens altijd verborgen.

Eigen endpoints

Heeft een integratie een antwoord nodig dat de resource-API niet geeft, schrijf dan een eigen route. api_access('<permission>') accepteert alle drie soorten aanroepers, dus één endpoint bedient je eigen pagina's en andere systemen. Zie Integraties en Wat een route teruggeeft.

OpenAPI

Elke Nimbly-site beschrijft zijn eigen API volgens de standaard OpenAPI 3, op /api/v1/openapi.json. Het document noemt de resources van de site met hun velden, typen, verplichte velden en keuzeopties, en de beschikbare handelingen. Het volgt de rechten van de aanroeper, precies zoals de API: een token of sessie ziet alleen de resources en handelingen die hij mag gebruiken, velden die de API nooit teruggeeft, blijven weg, en een anoniem request wordt geweigerd.

curl https://example.com/api/v1/openapi.json -H "Authorization: Bearer $TOKEN" > openapi.json

Laad het in Postman, Insomnia of Swagger UI, of maak er met een OpenAPI-generator een getypeerde client van. Geef een integratie een eigen gebruiker, dan laat het document precies zien wat die integratie kan.

Een algemene beschrijving van de handelingen die elke resource deelt, met export, import en bestanden, staat hier.

Interne endpoints

Een paar adressen onder /api/v1/ dienen de admin en de bewerktools: .resources, .pages, .files-usage, .files-unused, .system-messages, session, openai/complete, openai/translate, git-status, git-pull, empty-resource, agent-chat en agent-remote. Ze controleren rechten zoals elk ander endpoint, maar hun vorm kan meeveranderen met de admin; bouw liever op de resource-API.

Bekijk deze pagina als Markdown