Quickstart
From an empty account to reading your own content over the API.
This walks the shortest path: create a workspace, put a card in it, mint an API
key, read it back. Everything here is done against https://cms.spunto.net; a
self-hosted instance works the same way with its own host.
1. Get an account
Sign in at /login. That page carries both halves — signing in and
signing up — and it knows which one this instance allows.
Whether anyone can register is per instance. The code defaults to
invitation-only; cms.spunto.net is open. If registration is closed, you need
an invitation token minted by an admin of a workspace; the link they send you
carries it.
2. Create a workspace and a list
A workspace is the tenant boundary: members, content, properties, API keys
and the event log all live inside exactly one. Create one from /app.
Inside it, create a collection — a list of cards. Give it a key prefix like
DOC; cards in it will be numbered DOC-1, DOC-2, and so on.
Add a card or two. A card has a title, a body, a status, and whatever properties you declare.
3. Mint an API key
Go to /w/{workspaceId}/settings/api and create a key — you need to be an
admin or the owner of that workspace. You will get a token that starts with
spk_, shown once: the API only stores its SHA-256, so there is no way to
recover it later.
A key carries its own workspace and its own role, both fixed when it is issued. It does not follow the membership of whoever created it: if they leave the workspace, or get demoted, the key keeps doing exactly what it was issued to do until someone revokes it.
Give it the smallest role that works. viewer reads, member writes content,
admin also changes the registry.
4. Read your content back
The public API root is https://cms.spunto.net/api. Health first, to check the
token plumbing before anything else is in the way:
curl https://cms.spunto.net/api/healthNow list your workspaces with the key:
curl https://cms.spunto.net/api/workspaces \
-H "Authorization: Bearer $SPUNTO_TOKEN"Then read a collection — this one call returns the list, its properties, its statuses, its saved views and your role, so a client can render a whole screen without a second round trip:
curl "https://cms.spunto.net/api/workspaces/$WORKSPACE/collections/$COLLECTION" \
-H "Authorization: Bearer $SPUNTO_TOKEN"And query its cards. Reading a view is a POST, because a view definition is a
filter tree and a tree does not fit in a query string:
curl -X POST \
"https://cms.spunto.net/api/workspaces/$WORKSPACE/collections/$COLLECTION/query" \
-H "Authorization: Bearer $SPUNTO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"definition":{"sort":[{"field":{"fieldId":"sys:order"},"direction":"asc"}]},"limit":50}'You get { "rows": [...], "nextCursor": "..." }. Pass nextCursor back as
cursor for the next page; null means you reached the end.
5. Use the typed client instead
Inside a TypeScript project, the generated client is less work and it type-checks your filters against the same specification the server validates them with:
import { createSpuntoCmsClient } from "@spunto-cms/client"
const api = createSpuntoCmsClient({
baseUrl: "https://cms.spunto.net/api",
token: process.env.SPUNTO_CMS_TOKEN,
})
const { data, error } = await api.POST(
"/workspaces/{workspaceId}/collections/{collectionId}/query",
{
params: { path: { workspaceId, collectionId } },
body: { definition: { sort: [] }, limit: 50 },
},
)The client is generated, not hand-written
Types come from the OpenAPI specification via openapi-typescript, and requests
go through openapi-fetch. Nothing in it is maintained by hand, so it cannot
drift from the API.
Where to go next
- Querying content — filters, sorts, grouping, cursors.
- Writing content — creating cards, property validation, bulk edits and replayable imports.
- The property registry — the ten types and what each one guarantees.