Blog JSON:API

v0.0.1

The blog exposes its records through the CMS's JSON:API. Readers use one set of routes, site admins another, and masters a third. This page lists the routes, how to authenticate, and one example that signs in, creates a post and schedules it. Record fields are listed in Blog record fields.

Connect

  • Every route is under /api/json on the site's own host. The host decides which site a request is for.
  • Send content-type: application/vnd.api+json and accept: application/vnd.api+json.
  • Sign in with POST /api/json/v1/auth/user/sign-in. The token is in meta.token of the answer. Send it as authorization: Bearer <token>.
  • A master account has no site. On the master site's address, add the header x-no-tenant: true to leave the request unscoped.
  • The interactive spec is at /api/json/swaggerui and the OpenAPI document at /api/json/open_api. Both need an API permission.

Route groups

Prefix under /api/jsonForNeeds
/v1/user/mishka-blogReadersReads need no token. Writes need a signed-in user.
/v1/tenant/admin/mishka-blogSite admins, on their own siteadmin:api:blog:post or admin:api:blog:comment, as the record asks.
/v1/master/admin/mishka-blogMasters, on any siteThe same names. A create takes a site_id.

The record types are blog_post, collection, tag, blogger, team, comment and auto_posting. Lists are paged by offset, 20 to a page and at most 100, and accept page[limit]. The /post/featured list takes 10 to a page and at most 50.

Reader routes

Route under /v1/user/mishka-blogMethodWhat it does
/post/publishedGETLists published posts the caller may open.
/post/published/:idGETReads one published post.
/collection/active, /collection/active/:idGETLists or reads active collections.
/tag, /tag/:idGETLists or reads tags.
/blogger, /blogger/:id, /blogger/by-slug/:slugGETLists or reads bloggers that are not archived.
/team, /team/:id, /team/by-slug/:slugGETLists or reads active teams.
/comment/approvedGETLists the approved comments of a post. Pass post_id.
/comment/createPOSTWrites a comment as the signed-in user. It starts as pending. Attributes: post_id, content, optional parent_comment_id.
/comment/my-commentsGETLists the caller's comments.
/post-rating, /post-rating/my-ratings, /post-rating/:idPOST, GET, DELETERates a post (post_id, rating from 1 to 5), lists the caller's ratings, removes one.
/bookmark, /bookmark/my-bookmarks, /bookmark/:idPOST, GET, DELETEBookmarks a post (post_id), lists the caller's bookmarks, removes one.
/comment-vote, /comment-vote/my-votes, /comment-vote/:idPOST, GET, DELETEVotes on a comment (comment_id, vote_type of up or down), lists the caller's votes, removes one.
/blogger-follow, /blogger-follow/my-follows, /blogger-follow/followers, /blogger-follow/:idPOST, GET, PATCH, DELETEFollows a blogger (blogger_id), lists the caller's follows, lists a blogger's followers (blogger_id), changes notification_enabled, unfollows.
/team-follow, /team-follow/my-following, /team-follow/:idPOST, GET, PATCH, DELETEThe same for teams, with team_id.

A comment, rating, bookmark or vote on a post that is locked to permissions the caller does not hold is refused as though the post were not there.

Admin routes

These routes exist under both /v1/tenant/admin/mishka-blog and /v1/master/admin/mishka-blog. Replace {record} with post, collection, tag, blogger, team, comment or auto-posting.

RouteMethodRecordsWhat it does
/{record}GET, POSTAllLists records, or creates one. The master prefix has no route to create a comment.
/{record}/:idGET, PATCHAllReads or updates one.
/{record}/language-variantsGETPost, collection, tagLists the editions of a language group. Pass language_group_id.
/{record}/archived, /{record}/archived/:idGETAllLists or reads archived records.
/{record}/:id/archiveDELETEAllArchives a record.
/{record}/:id/unarchivePATCHAllRestores an archived record.
/{record}/:id/permanentDELETEAllDeletes a record for good.
/{record}/:id/with-versionsGETAll but commentReads a record with its versions.
/{record}/:record_id/versionsGETAll but commentLists a record's versions.
/{record}/:id/version/:version_id/restorePATCHAll but commentRestores a version.
/{record}/:record_id/version/:version_id/previewGETAll but commentReturns the record with that version's values, without saving.
/post/:id/publish, /post/:id/unpublishPATCHPostPublishes, or returns to draft.
/post/:id/schedulePATCHPostSets the status to scheduled. Attribute: scheduled_publish_at.
/post/published, /post/drafts, /post/scheduled, /post/featuredGETPost, tenant prefix onlyLists posts by status, or the featured ones.
/comment/:id/approve, /flag, /mark-spam, /pin, /unpinPATCHCommentModerates a comment.
/blogger/by-user/:user_id, /blogger/by-slug/:slugGETBloggerFinds a profile by account or slug.
/team/by-slug/:slugGETTeamFinds a team by slug.
/auto-posting/by-plugin/:plugin, /auto-posting/activeGETAuto postingFinds a configuration by plugin, or lists the active ones.
/auto-posting/:id/activate, /deactivatePATCHAuto postingSwitches a configuration on or off.

A post can include its collections, tags, comments and bloggers. On the admin prefixes the names are tenant_collections and so on, or master_collections and so on, and tenant_comments returns comments in every status. A blogger's account email is not in a normal answer. Ask for it with fields[blogger]=author_email, as a holder of the post area.

Example: sign in, create a post, schedule it

Sign in. Use the site's own host.

bash
curl -X POST https://example.com/api/json/v1/auth/user/sign-in \
  -H "content-type: application/vnd.api+json" \
  -H "accept: application/vnd.api+json" \
  -d '{"data": {"type": "user", "attributes": {"email": "[email protected]", "password": "your password"}}}'

Create a draft with the token from meta.token:

bash
curl -X POST https://example.com/api/json/v1/tenant/admin/mishka-blog/post \
  -H "authorization: Bearer $TOKEN" \
  -H "content-type: application/vnd.api+json" \
  -H "accept: application/vnd.api+json" \
  -d '{"data": {"type": "blog_post", "attributes": {"title": "Hello", "slug": "hello", "content": "<p>First post</p>", "status": "draft"}}}'

Schedule it with a future date. The answer shows status as scheduled:

bash
curl -X PATCH https://example.com/api/json/v1/tenant/admin/mishka-blog/post/$POST_ID/schedule \
  -H "authorization: Bearer $TOKEN" \
  -H "content-type: application/vnd.api+json" \
  -H "accept: application/vnd.api+json" \
  -d '{"data": {"type": "blog_post", "attributes": {"scheduled_publish_at": "2030-01-01T09:00:00Z"}}}'

A scheduled post stays out of the reader routes. Publish it with the /publish route when its time comes.