Cadmus
v0.1.2

Guides

The REST API

The JSON endpoints every model gets - listing, searching, filtering, sorting, creating, changing, deleting, and errors.

Everything in the admin can also be done by a program, over HTTP with JSON. Every model gets these endpoints under /api, with the model's table name in the path:

RequestWhat it does
GET /api/postlist posts
POST /api/postcreate one; answers 201 with the new record and a Location header
GET /api/post/1read one
PUT /api/post/1replace one: every required field must be sent
PATCH /api/post/1change only the fields sent
DELETE /api/post/1delete one; answers 204

Every request needs a login token or an API key; see Logging in. The API follows the same permissions as the admin.

#Listing

curl -H "Authorization: Bearer $TOKEN" "http://localhost:3000/api/post?q=rust&sort=-id&page=2&per_page=50"
{
  "data": [{ "id": 12, "title": "Rust tips", "published": true, "views": 40 }],
  "meta": { "total": 31, "page": 2, "per_page": 50 }
}
ParameterMeaning
qsearch in the model's search_fields (ignores case)
sorta field to sort by; - in front for highest first (sort=-views)
pagewhich page, from 1
per_pagehow many per page, at most 100

#Filtering

Any visible field can be filtered on, with the same choices as the admin's filters:

ExampleMatches
?published=truean exact value
?author_id=3&published=falseseveral at once (all must match)
?views__gt=100, __gte, __lt, __ltecomparisons
?created_at__gte=2026-01-01dates and date-times compare too
?summary__isnull=trueempty (or false: not empty)
?created_at__range=7dtoday, 7d, 30d, month or year (UTC)

An unknown field, or a value the field cannot hold, is refused with 422. A field named page, per_page, q or sort cannot be filtered on.

#Creating and changing

curl -X POST http://localhost:3000/api/post \
     -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
     -d '{"title": "Hello", "body": "First post", "published": false, "views": 0}'
  • Dates and times are ISO 8601: "2026-03-04", "2026-03-04T05:06:07".
  • Links to other models are sent as the id: "author_id": 3.
  • Fields that are unknown, hidden or read-only are refused rather than silently ignored.
  • PUT needs every required field; PATCH changes only what is sent.

#Errors

Errors come back as JSON:

{ "error": { "code": "invalid_field", "message": "title is required", "field": "title" } }
StatusCodeMeaning
400bad_requestthe request is malformed
401unauthorizedno valid token or key was sent
403forbiddenthe account may not do this
404not_foundno such record or model
409conflictconflicts with other data: a duplicate, or a record others point to
413too_largean upload over the size limit
422invalid_fielda field is missing, unknown, read-only or of the wrong type
429rate_limitedtoo many login attempts; wait for the Retry-After seconds

#Calling it from a browser app

A web app on another domain needs permission to call the API: set server.cors_origins: vec!["https://app.example.com".into()] in the settings. Such apps send a token or an API key; the admin's login cookie is never accepted by the API.

#Documentation

Every project documents its own API at /api/docs (Swagger UI), /api/redoc and /api/openapi.json. See API documentation.