Skip to content
JSON API

JSON API

Every resource endpoint negotiates content: send Accept: application/json and the same handler that renders HTML returns JSON — same auth, same permissions, same policies, same validation. There is no separate API layer to keep in sync.

Listing

GET /admin/posts?posts_page=2&posts_per=20&posts_q=go&posts_sort=-ID
Accept: application/json
{ "items": [ { "ID": 21, "Title": "…" } ], "total": 137, "page": 2, "per_page": 20 }

Query parameters are namespaced by resource slug (posts_page, posts_per, posts_sort — prefix - for descending, posts_q for quick search) so several grids can coexist on one URL. Filters use their own namespaced parameters as shown in the filter panel’s form.

Single record

GET /admin/posts/21
Accept: application/json

returns the record as stored (relations included when the grid preloads them).

Schema

GET /admin/posts/_schema describes the form for headless clients:

{
  "slug": "posts", "title": "Posts", "key": "ID",
  "fields": [
    { "name": "Title", "kind": "text", "label": "Title", "required": true, "rules": "required|max:255" }
  ]
}

Mutations

Create, update, and delete return dcat-admin’s envelope — one shape for every mutation:

POST /admin/posts            (form-encoded fields)
PUT  /admin/posts/21
DELETE /admin/posts/21       (comma-separate ids for batch: /posts/1,2,3)
{ "status": true, "data": { "message": "Saved.", "type": "success", "then": { "action": "redirect", "value": "/admin/posts" } } }

then.action is one of redirect, location, download, refresh, script — a hint your client may honor or ignore. Validation failures come back as 422:

{ "status": false, "errors": { "Title": ["Title is required."] } }

Custom actions (POST /admin/posts/_action/{name} with ids=1,2) return the same envelope; your handlers build it with steward.Success, steward.Error, steward.Warning, steward.Info and the chainable Redirect / Refresh / Download / Script / Code helpers.

Authentication & CSRF

JSON requests ride the same session cookie as the browser. State-changing methods need the CSRF token — read it from the csrf-token meta tag (or keep a session per client and reuse the token) and send it as X-CSRF-Token. Unauthenticated JSON requests get 401 with an envelope rather than a redirect.

Responses set Vary: HX-Request, Accept, so caches never mix the HTML, fragment, and JSON representations of one URL.