Terug naar home

API-documentatie

De read-only REST API ontsluit gepubliceerde EbbenHub-content voor externe afnemers. Hieronder het levende contract.

Levend document. Wordt in dezelfde commit bijgewerkt als elke wijziging aan de API. Afwijking tussen dit contract en de implementatie = bug.

Versie: v1 · Status: Fase 11 · Laatst bijgewerkt: 2026-06-19

De EbbenHub-API ontsluit de gepubliceerde content (bomen, assets, artikelen, taxonomie en relaties) read-only voor externe afnemers, bijv. de Ebben-website. EbbenHub blijft de bron van waarheid; deze API is puur leesbaar.

Basis

  • Base URL: /api/v1
  • Methode: uitsluitend GET
  • Formaat: JSON (Content-Type: application/json)
  • Zichtbaarheid: alleen gepubliceerde bomen en artikelen. Concepten lekken nooit naar buiten — niet als lijstitem, niet als detail (404), en niet als gekoppeld item in een relatie of "waar gebruikt".

Authenticatie

Elke request moet een geldige, actieve API-sleutel meesturen via één van:

  • Authorization: Bearer <sleutel>
  • X-Api-Key: <sleutel>

De sleutel wordt in het admin-panel beheerd (Systeem → API-sleutels), bij aanmaken éénmalig in plaintext getoond en daarna alleen als SHA-256-hash bewaard. Eén toegangsniveau: een geldige sleutel geeft toegang tot alle gepubliceerde data (geen per-sleutel scopes). Bij elk geslaagd verzoek wordt last_used_at bijgewerkt.

Situatie Respons
Geen / ongeldige / ingetrokken 401 Unauthorized{"message": "Ongeldige of ontbrekende API-sleutel."}
Geldige, actieve sleutel 200 OK

Taal (meertaligheid)

Vertaalbare velden (name, description, title, …) worden in één taal geleverd, bepaald door — in volgorde:

  1. de query-parameter ?lang=nl of ?lang=en;
  2. anders de Accept-Language-header (nl / en);
  3. anders de standaardtaal nl.

Paginatie

Lijst-endpoints pagineren via Laravel's standaard paginator:

  • ?page=<n> — paginanummer (default 1)
  • ?per_page=<n> — items per pagina (default 25, max 100)

De respons bevat data, links en meta (met o.a. current_page, per_page, total, last_page).


Endpoints

GET /api/v1/trees

Gepagineerde lijst van gepubliceerde bomen (compacte vorm).

Query-parameters

Parameter Type Omschrijving
search string Zoekt op latijnse naam, sorteernaam en NL/EN naam.
filter[<filter_id>] string Eén of meer value_id's (komma-gescheiden) van een kenmerk; een boom matcht áls hij een van die opties draagt (OR binnen het facet).
range[<filter_id>] string van,tot voor een range-kenmerk (hoogte/breedte/maand/zone); matcht op overlap (lege grens = onbegrensd).
lang, page, per_page Zie hierboven.

filter_id en value_id komen 1-op-1 uit GET /api/v1/taxonomy. Meerdere facetten combineren met AND.

Voorbeeld

GET /api/v1/trees?search=quercus&filter[2]=30,31&range[10]=4,8&lang=nl
Authorization: Bearer ebbh_xxxxxxxx
{
  "data": [
    {
      "id": 12,
      "latin_name": "Quercus robur",
      "keyword": "QUERROB",
      "sort_name": "Quercus robur",
      "synonym": null,
      "type": "Loofboom",
      "name": "Zomereik",
      "description": "…",
      "marketing_text": "…",
      "status": "gepubliceerd",
      "hero": { "type": "asset", "id": 5, "title": "Quercus robur", "url": "https://…", "mime_type": "image/jpeg", "is_image": true }
    }
  ],
  "links": { "first": "…", "last": "…", "prev": null, "next": "…" },
  "meta": { "current_page": 1, "per_page": 25, "total": 1, "last_page": 1 }
}

GET /api/v1/trees/{id}

Volledig boomdetail. Een niet-gepubliceerde boom geeft 404.

Bovenop de lijstvelden:

Veld Omschrijving
attributes Kenmerken gegroepeerd per kenmerkgroep. Per kenmerk: group, kenmerk, filter_id, type, en óf options (value_id + name) óf min/max.
media Gekoppelde DAM-assets (compacte asset-referenties), in de gekozen volgorde.
categories Eigen collecties (namen).
tags Eigen labels (namen).
relations Bidirectioneel relatie-web: per relatie label + een compacte referentie naar het gekoppelde (gepubliceerde) item (type = tree / article / asset).
{
  "data": {
    "id": 12,
    "latin_name": "Quercus robur",
    "name": "Zomereik",
    "status": "gepubliceerd",
    "hero": { "type": "asset", "id": 5, "url": "https://…", "is_image": true },
    "attributes": [
      { "group": "Standplaats", "kenmerk": "Locatie", "filter_id": 2, "type": "enumselect", "options": [ { "value_id": 30, "name": "Park" } ] },
      { "group": "Maatvoering", "kenmerk": "Hoogte", "filter_id": 10, "type": "rangeslider", "min": 18.0, "max": 25.0 }
    ],
    "media": [ { "type": "asset", "id": 5, "url": "https://…" } ],
    "categories": ["Favorieten"],
    "tags": ["Inheems"],
    "relations": [
      { "label": "bestuiver", "type": "tree", "id": 18, "latin_name": "Quercus petraea", "keyword": "QUERPET", "name": "Wintereik" },
      { "label": "gerelateerd", "type": "article", "id": 3, "article_type": "kennisbank", "slug": "eiken-snoeien", "title": "Eiken snoeien" }
    ]
  }
}

GET /api/v1/articles

Gepagineerde lijst van gepubliceerde artikelen (blog + kennisbank).

Query-parameters: type (blog | kennisbank), lang, page, per_page.

{
  "data": [
    {
      "id": 3,
      "type": "kennisbank",
      "slug": "eiken-snoeien",
      "title": "Eiken snoeien",
      "status": "gepubliceerd",
      "featured_image": { "type": "asset", "id": 9, "url": "https://…", "is_image": true }
    }
  ],
  "meta": { "current_page": 1, "per_page": 25, "total": 1, "last_page": 1 }
}

GET /api/v1/articles/{id}

Volledig artikeldetail; niet-gepubliceerd geeft 404. Bovenop de lijstvelden: content (rijke HTML, gekozen taal), media, categories, tags, en relations — inclusief de gekoppelde bomen (type: "tree").

GET /api/v1/assets

Gepagineerde lijst van DAM-assets (URL's + metadata).

Query-parameters: search (titel/bestandsnaam/alt-tekst), page, per_page.

{
  "data": [
    {
      "id": 5,
      "title": "Quercus robur",
      "alt_text": "Zomereik in het park",
      "description": null,
      "mime_type": "image/jpeg",
      "size": 248173,
      "is_image": true,
      "url": "https://…/storage/assets/quercus-robur.jpg",
      "folder": { "id": 2, "name": "Bomen" }
    }
  ],
  "meta": { "current_page": 1, "per_page": 25, "total": 1, "last_page": 1 }
}

GET /api/v1/assets/{id}

Volledig assetdetail. Bovenop de lijstvelden:

Veld Omschrijving
used_by "Waar gebruikt": trees en articles (compacte referenties) die deze asset gebruiken — alleen gepubliceerde items.
relations Polymorf relatie-web rond de asset (bidirectioneel, gepubliceerde items).

GET /api/v1/taxonomy

De volledige facet-taxonomie (groepen → filters → opties) waarop de bomen-lijst gefilterd kan worden. Levert de stabiele filter_id en value_id die je in filter[...] / range[...] op /trees gebruikt.

{
  "data": [
    {
      "group_id": 1,
      "name": "Standplaats",
      "filters": [
        {
          "filter_id": 2,
          "name": "Locatie",
          "type": "enumselect",
          "is_range": false,
          "options": [
            { "value_id": 30, "name": "Park" },
            { "value_id": 31, "name": "Straat" }
          ]
        },
        {
          "filter_id": 10,
          "name": "Hoogte",
          "type": "rangeslider",
          "is_range": true,
          "options": []
        }
      ]
    }
  ]
}

Compacte referenties

Cross-links (hero, media, relations, used_by) gebruiken een korte, type-getagde vorm:

  • tree: { "type": "tree", "id", "latin_name", "keyword", "name" }
  • article: { "type": "article", "id", "article_type", "slug", "title" }
  • asset: { "type": "asset", "id", "title", "url", "mime_type", "is_image" }

In relations staat daarnaast een label (bijv. verwant, alternatief, bestuiver, gerelateerd).