Skip to content

Posts API

Posts are the product. Reading them is what your website does with a project key; writing them is a dashboard action performed by a person or by an agent token acting for one.

Last updated

Endpoints

MethodPathWhat it doesCredential
GET/v1/blog/postsList postsKey or session
GET/v1/blog/posts/{id}One post by idKey or session
GET/v1/blog/posts/slug/{slug}One post by slugKey or session
POST/v1/blog/postsCreate a postSession or agent
PATCH/v1/blog/posts/{id}Update a postSession or agent
DELETE/v1/blog/posts/{id}Delete a postSession or agent

What a post is

FieldTypeNotes
idstringpost_ + 32 hex
titlestringRequired, 1–300 characters, trimmed
slugstringGenerated from the title if you do not send one; unique per project
excerptstring | nullUp to 1,000 characters
bodystringUp to 1,000,000 characters
bodyFormatmarkdown | html | plainDefaults to markdown; we store it, we do not render it
statusdraft | scheduled | published | archivedDefaults to draft
publishedAtISO 8601 | nullSet automatically when you publish without one
scheduledAtISO 8601 | nullRequired when status is scheduled
authorobject | nullExpanded on reads, set with authorId on writes
categoryobject | nullExpanded on reads, set with categoryId on writes
tagsarrayExpanded on reads; on writes, names or ids — unknown names are created
featuredMediaIdstring | nullA media id
seoobjecttitle, description, canonicalUrl, ogImage, noIndex, keywords
createdAt / updatedAtISO 8601Set by the platform

Listing and filtering

ParameterExampleWhat it does
limit501–100, default 25
cursoreyJ2Ijox…From the previous response
statuspublishedIgnored for a public key, which only ever sees published
categorylogisticsBy slug
categoryIdcat_0193…By id
tagcold-chainBy slug
authorIdauth_0193…By id
qcongestionSubstring match on the listing; see Search for real full-text
publishedAfter2026-01-01T00:00:00ZInclusive
publishedBefore2026-12-31T23:59:59ZInclusive
sort-publishedAtSee Pagination for the closed list
Combining filtersbash
curl -G 'https://api.cmskite.com/v1/blog/posts' \
  -H "authorization: Bearer $KEY" \
  --data-urlencode 'category=logistics' \
  --data-urlencode 'tag=cold-chain' \
  --data-urlencode 'sort=-publishedAt' \
  --data-urlencode 'limit=50'

Creating and updating

Createbash
curl -X POST https://api.cmskite.com/v1/blog/posts \
  -H "authorization: Bearer $ACCESS_TOKEN" \
  -H "x-tenant-id: $WORKSPACE_ID" \
  -H "x-project-id: $PROJECT_ID" \
  -H 'content-type: application/json' \
  -H "idempotency-key: $(uuidgen)" \
  -d '{
    "title": "Six weeks of port congestion, in one chart",
    "body": "# Rotterdam, week by week\n\nThe queue peaked on a Tuesday.",
    "status": "published",
    "categoryId": "cat_0193...",
    "tags": ["ports", "data"],
    "seo": { "description": "Six weeks of Rotterdam queue data." }
  }'

A PATCH takes the same fields and changes only what you send. Sending null clears a nullable field; leaving it out leaves it alone.

  • Fields that decide ownership — tenantId, projectId, id — are not part of the request surface at all. Sending one is a 400, not a silently ignored key.
  • Two people creating the same title at the same moment both succeed, with different slugs. Nobody gets a 500.
  • Tags are created on demand. Sending a tag name nobody has used yet makes it; sending one that exists reuses it.

Publishing and scheduling

  1. Set status: "published" to publish now. If you send no publishedAt, it is set to now — a published post with no timestamp would sit at the top of a newest-first listing forever.
  2. Set status: "scheduled" with a scheduledAt in the future. It is refused without one.
  3. A scheduled post becomes published on its own. Until then it is invisible to a project key, exactly like a draft.
  4. Set status: "archived" to take it off the site without deleting it.

Deleting

Deletes are soft. The row is retained so support can restore it, and it stops being reachable immediately — a project key, the dashboard and a listing all stop seeing it at once.

The slug frees straight away, so delete-and-recreate gives you the same URL back rather than my-post-2 forever.