API and auth
All dashboard behaviour is available over a plain REST API. Responses are JSON.
Base URL
Section titled “Base URL”https://<your-api-host>Your administrator provides it; the dashboard shows it in its runtime configuration.
Authentication
Section titled “Authentication”- Exchange a registered email for a token:
curl -X POST "$API/api/login" \ -H 'Content-Type: application/json' \ -d '{"email":"you@example.com"}'{ "token": "eyJhbGciOiJSUzI1NiIsImtpZCI6..." }- Send it on every request:
curl "$API/content?type=eq.posts&limit=3" \ -H "Authorization: Bearer $TOKEN"Token claims
Section titled “Token claims”| Claim | Meaning |
|---|---|
sub |
The user’s email |
app_role |
super_admin, staff, partner, or a custom role |
site_id |
The site this token is scoped to (* for super admins) |
caps |
Capability array, e.g. ["content.read","content.write"] |
iss / aud |
Issuer and intended audience — both verified |
kid |
Key id, used to pick the signing key |
exp |
Expiry — one hour |
Tokens are signed with RS256; the private key never leaves the server. Public keys are published at
/.well-known/jwks.json, so a service can verify a token without calling back home.
Reading
Section titled “Reading”Query parameters follow a consistent, composable pattern:
| Parameter | Example | Meaning |
|---|---|---|
select |
select=id,title,status |
Columns to return |
| filter | status=eq.publish |
eq, not.eq, ilike, gt, lt, in |
order |
order=created_at.desc |
Sort |
limit / offset |
limit=20&offset=40 |
Paging |
Prefer: count=exact |
header | Adds Content-Range: 0-19/383 |
# Published insights, newest first, page 2curl "$API/content?type=eq.insights&status=eq.publish&order=created_at.desc&limit=20&offset=20" \ -H "Authorization: Bearer $TOKEN" \ -H "Prefer: count=exact"Writing
Section titled “Writing”# createcurl -X POST "$API/content" \ -H "Authorization: Bearer $TOKEN" \ -H 'Content-Type: application/json' \ -d '{"site_id":"acme","type":"posts","slug":"hello","title":"Hello","status":"draft","body":"<p>Hi</p>"}'
# update one recordcurl -X PATCH "$API/content?id=eq.<uuid>" \ -H "Authorization: Bearer $TOKEN" \ -H 'Content-Type: application/json' \ -d '{"status":"publish"}'
# soft deletecurl -X PATCH "$API/content?id=eq.<uuid>" \ -H "Authorization: Bearer $TOKEN" \ -H 'Content-Type: application/json' \ -d '{"status":"trash"}'Media upload
Section titled “Media upload”curl -X POST "$API/media/upload" \ -H "Authorization: Bearer $TOKEN" \ -F "file=@./hero.webp" \ -F "site_id=acme"{ "key": "acme/2316-hero.webp", "size": 55904 }Files are then served at <public-base>/<key>.
Status codes
Section titled “Status codes”| Code | Meaning |
|---|---|
200 / 201 / 204 |
Success |
206 |
Partial content — returned with pagination |
401 |
Missing, expired or invalid token |
403 |
Valid token, insufficient capability or wrong site |
404 |
No such record (or not visible to you) |
Conventions worth knowing
Section titled “Conventions worth knowing”idis a UUID; the human-facing number iscontent_id.- Trash is
status=trash, not deletion. Filter withstatus=not.eq.trashto hide it. - Prefer
PATCHfor edits — it only touches the fields you send. - A
403is usually a capability problem, a401almost always an expired token.