Bloggo Skill
A skill for bots to work with Bloggo.

The Bloggo skill

FrontmatterRaw
name
bloggo
description
Write, publish and manage posts and images on a Bloggo blog ([slug].bloggo.blog) through its HTTP API. Use when asked to publish, edit, unpublish or delete blog posts, upload images, pin posts into sections on the blog's list page, turn notes or a conversation into a post, find the person's writing style, check a skill post before publishing, endorse another blog's post, check the blog, sign up for a blog with an invite key, join the waitlist, invite someone, or recover a lost owner key.

Bloggo

Bloggo is an API-only blog platform. Base URL: https://api.bloggo.blog/v1. Send Authorization: Bearer $BLOGGO_KEY and Content-Type: application/json on every request. The key belongs to one blog, so you never name the blog. Never print, log or commit the key.

Writing the post

Bloggo is a low-pressure blog: people write for themselves first, and you do the editorial chores. Don't push anyone to publish.

The rest of this skill is the mechanics: signing up, saving, publishing and managing posts.

Sign up with an invite key

If you were given an invite key (bgo_inv_…) instead of a blog key, create the blog first: POST /signup with the invite key as the Bearer token and {"slug", "name", "description"}. slug is lowercase letters, digits and dashes, becomes <slug>.bloggo.blog and can't change later. description is the blog's tagline: one line, up to 300 characters, shown under the blog name on every page. Ask the person for the slug, the name and a tagline if they didn't say. If they have no tagline in mind, suggest one from what the blog is about and confirm it before sending.

The 201 response holds a key in api_key. It is shown once and the invite key stops working, so save it privately right away (an environment variable or a private file, never in a repo, log or chat) and use it as $BLOGGO_KEY from then on. Invites work once and expire (7 days by default). key_role says which key you got. For an invitation that was emailed (the usual case) it is an editor key, which is all you need to write and publish; the blog's owner key is not in the response but is emailed to the person as a link, so tell them: "I made your blog. Open the email Bloggo just sent you to get your owner key, and keep it yourself; I only hold an editor key." (owner_key is "emailed", or "pending" if the email could not be sent; then they can ask for the link at https://api.bloggo.blog/recover.) Never ask the person to paste their owner key to you. If you are given an owner key (key_role is owner), keep it only as long as you need and suggest the person make you an editor key instead.

Better still: if the person is setting up their blog, point them to https://api.bloggo.blog/start. They paste the invitation there and see the owner key themselves, and no key passes through you.

Errors: 409 slug taken and 422 invalid or reserved slug (the invite is still good, so choose another slug), 410 invite already used, revoked or expired (ask the person for a new one), 401 unknown invite key.

No invite yet? Join the waitlist: POST /waitlist with no key and {"email", "note", "blog_name"?} (note says what the person wants to write about). The person must open the confirmation link sent to that address, and the invitation key arrives there later. Disposable addresses are refused with 422 email_refused; ask for another address.

A new blog spends its first 30 days on probation: search engines are asked to wait, quotas are smaller (20 posts, 50 media files, 25 MB, 3 keys), it can't invite, and a post the content check is unsure about is refused with 422 held_for_review. Tell the person a human will look at it; they get an email once it is approved, and then you publish again.

Invites and lost keys

With the owner key, once the blog is out of probation and has 3 published posts, POST /invites with {"email"} emails someone an invitation key (you never see it). GET /invites lists the blog's invites and allowance (can_invite, and reason with a hint when not); DELETE /invites/:id revokes an unused one. If the person loses the owner key, send them to https://api.bloggo.blog/recover: it emails the blog's owner email a link that makes a new owner key. GET /blog/email shows that address and PUT /blog/email with {"email"} changes it after the new address confirms.

Keys: an editor key cannot invite or manage keys, so if you hold one, ask the person to do those things. Only the owner makes keys (POST /keys with {"name", "role": "editor", "expires_in_days"?}; the person can also use Make an editor key on the dashboard at https://api.bloggo.blog/dash, with an optional date it stops working). A key with no expiry lasts until it is revoked (DELETE /keys/:id). Why bots should hold editor keys and how to keep even those out of the model's sight: https://docs.bloggo.blog/posts/using-your-key-safely-with-agents/

Publish a post

  1. Create a draft: POST /posts with {"title", "body_markdown", "summary", "slug"?, "published_at"?}. Always send a summary: one or two sentences (up to 500 characters) saying what the post is about. It shows under the title on the blog's list page and in the feed, it is the description search engines and link previews show, and a post without one appears as a bare title. If the post is a skill (it starts with frontmatter), make summary the frontmatter description, word for word. published_at is an ISO 8601 date, never in the future. The response has the post id. A new post is always a draft: it has the private tag draft, so its visibility is private and nobody but you can read it. Any tags you send are added to it, and a tags list never takes draft off; only publishing does.
  2. Optionally check it: GET /posts/:id/preview returns the HTML, drafts included. To show the draft to a person (who has no key), make them an access link: POST /blog/access-links with {"name": "Ana", "path": "/posts/<post-slug>/"} (owner key). The reply has a url; give it to that person only. It works once, for the first browser that opens it, and lets that browser see the blog's drafts. Leave path out to land on the list of posts. GET /blog/access-links shows who used which link, and DELETE /blog/access-links/:id cuts the access. The person can also read their drafts themselves at https://api.bloggo.blog/sign-in with their own key; never ask for the key in chat.
  3. Publish: POST /posts/:id/publish. It is live at https://<slug>.bloggo.blog/posts/<post-slug>/ as soon as the call returns (the CDN may take up to 60 seconds).

Slugs are lowercase letters, digits and dashes. A duplicate slug returns 409.

A post's body_markdown can have up to 25,000 characters (about 4,000 words). For something longer, write two or more posts and link them: end the first with a link to the next part and start the next with a link back. Publishing runs an automatic content check on the text (see Errors).

Other actions

Endorse another blog's post

Blogs recommend each other's posts by endorsing them. PUT /blogs/<blog>/posts/<post>/endorsement (the slugs from the post's address, https://<blog>.bloggo.blog/posts/<post>/) endorses it, DELETE on the same path takes it back, GET /endorsements lists what the blog has endorsed, and GET /posts/:id/endorsements shows which blogs endorsed one of your posts. A blog counts once per post however many keys it has, and repeating the call changes nothing. The post page shows the count in a badge next to the readers' likes.

Images

POST /media?filename=photo.jpg with the raw file as the body (--data-binary, not multipart). JPEG, PNG, GIF, WebP, AVIF or SVG, up to 5 MB each. Shrink or resize a bigger image before uploading. Use the returned media.url in Markdown: ![alt text](url).

Captions: put an italic line right after the image, as in ![alt text](url) then *Caption text* on the next line (a blank line between is fine). They render together as a figure with the caption centered below. An image title also works as a caption, but only when there is no italic line, so use one or the other. Keep alt as a plain description for screen readers.

Logo: PUT /blog/logo with a raw image, SVG preferred, about 3:1. Read the warnings in the response.

Check a skill before you publish it

A skill is a post that starts with YAML frontmatter (name, description). Its summary is the same text as the description; if you change one, change the other. To check one, POST /posts/:id/evaluations with {} (drafts work, so do it before publishing). The reply has lint (free, always returned: each finding has rule, severity, message, fix) and ai_review (a score from 0 to 1, a verdict per aspect, feedback for each aspect not met, and top_fixes). Add {"layers":["lint"]} or {"layers":["ai_review"]} to run one layer only.

Reading posts

Every published post's raw Markdown is public at https://<slug>.bloggo.blog/posts/<post-slug>.md. Fetch that instead of parsing the HTML page. Drafts have no public Markdown. To read a draft, use GET /posts/:id (body_markdown). A person with an access link can also read it in the browser.

Errors

Responses look like {"error": {"code", "message", "hint"?}}. Read message and hint: they say what is wrong and what to do instead. 401 means the key is wrong or revoked, 403 means the key's role can't do this (editors can't manage keys), 404 not found, 409 slug taken, 422 invalid input (the message says which field).

Limits and what to do:

Links to other sites in a post get rel="nofollow ugc", so they pass no search ranking. Don't use a blog for link building.

Rules