Skip to content
TheShopMarketing Guys

Your blog

Write posts from a short brief. Nothing goes live until you say so.

Overview

A good blog post answers a question your customers really ask, in the words they'd use. Your assistant can write those posts with you, and nothing goes live until you say so.

Give it a short brief, like the question and what you want to say. Or paste in a post you've already written. It checks the draft against the same rules as the rest of your site. Then it saves the draft and gives you a preview link to open on your phone. Ask for changes and it makes them.

When you're happy, tell it to publish. It publishes the exact version that passed the check. If anything changed since then, it checks again first. You can edit a live post the same way, like fixing a typo or swapping the photo. A change to a live post gets the same check before it saves.

To take a post down, your assistant sends us a request. There's also a daily limit on how many drafts it writes from a brief.

Changes here need an active account. If your account is on hold or cancelled, editing pauses until it's back.

You can also do this in your workspace. Look in Site, under Blog (/workspace).

The tools on this page

Your blog: what each tool reads, writes, and asks
ToolReadsWritesAsks you first
See your blogYesNoNo
Write a draftNoYesYes
Check a postYesNoNo
Publish a postNoYesAlways
Edit a postNoYesAlways

See your blog

get_blog_contextReads only

What you can ask

  • What have we posted on the blog lately?
  • What should we write about next?

What it does

Your blog posts, live and draft, and the services you can link to.

What your assistant sends

FieldRequiredDescription
limitintegerNoRecent posts to include, default 20.
Example: what your assistant sends
{
  "limit": 10
}
Example: what comes back
{
  "posts": [
    {
      "id": "55555555-5555-4555-8555-555555555555",
      "title": "When to replace brake pads",
      "slug": "when-to-replace-brake-pads",
      "status": "published",
      "published_at": "2026-09-12T14:00:00.000Z",
      "category": "Brake pads"
    }
  ],
  "services": [
    {
      "id": "66666666-6666-4666-8666-666666666666",
      "name": "Brake repair",
      "path": "/services/brake-repair"
    }
  ],
  "post_shape": "post = { title (8-90 chars, the question the post answers), excerpt, tldr, body (markdown), faqs, category, tags[], service_ids[] }.",
  "steps": [
    "draft_blog_post with a brief or a whole post.",
    "publish_blog_post with the post id, the latest validation_id and confirm.intent."
  ],
  "voice_notes": {
    "note": "Reference only, never checked.",
    "data": [
      "The owner prefers plain words over trade terms."
    ]
  }
}

Write a draft

draft_blog_postChanges your site or sends something

What you can ask

  • Write a blog post about when to replace brake pads.
  • Make the intro of that draft shorter.

What it does

Write a blog draft from a short brief or a post you wrote, or change a draft. Never publishes.

Your assistant asks you to confirm this first.

What it won't do

It writes drafts and never publishes. There's a daily limit on drafts from a brief, usually 10. Past that, your assistant is told when it can try again.

What your assistant sends

FieldRequiredDescription
briefobjectNoHave the writer draft it. Or send post, not both.
postobjectNoA whole post. get_blog_context describes the shape.
revise_post_idstringNoA draft to revise, with post (replaces it) or notes (the writer revises it).
notesstringNoRevision instructions for the writer.
owner_requeststringNoWhat the owner asked for, in their words, one sentence. Optional. Stored with the audit record so their team can see what owners ask for.
Example: what your assistant sends
{
  "brief": {
    "topic": "When to replace brake pads",
    "angle": "When to call us"
  }
}
Example: what comes back
{
  "post": {
    "id": "55555555-5555-4555-8555-555555555555",
    "slug": "when-to-replace-brake-pads",
    "title": "When to replace brake pads",
    "excerpt": "Age, leaks and rising bills are the signs. Here is how to tell a repair from a replacement.",
    "tldr": "Past 10 years with a leak, replace it.",
    "body": "Most brake pads last 30,000 to 70,000 miles.\n\n## When to call us\n\nIf you are not sure, call and we will take a look.",
    "faqs": [
      {
        "question": "How long does a brake pad last?",
        "answer": "Most brake pads last 30,000 to 70,000 miles."
      }
    ],
    "category": "Brake pads",
    "tags": [
      "brake pad"
    ],
    "seo_title": "When to replace brake pads | ESG",
    "seo_description": "Age, leaks and rising bills are the signs. Here is how to tell a repair from a replacement.",
    "status": "draft",
    "service_ids": [
      "66666666-6666-4666-8666-666666666666"
    ],
    "hero": {
      "status": "generating"
    }
  },
  "updated_at": null,
  "gate": {
    "status": "pass",
    "findings": [],
    "validation_id": "v1.opaque"
  },
  "preview_url": "https://app.example.com/preview/blog/bp1.opaque",
  "expires_in_minutes": 30,
  "next": "Saved as a draft. Show the owner the preview; publish_blog_post with this validation_id when they approve."
}

Check a post

validate_blog_postReads only

What you can ask

  • Check my draft before I post it.
  • Is this post ready to publish?

What it does

Check a draft or an unsaved post against the rules. Saves nothing.

What your assistant sends

FieldRequiredDescription
post_idstringNoA saved draft. Its validation_id can publish it.
postobjectNoUnsaved copy. Checked only; draft it before publishing.
Example: what your assistant sends
{
  "post_id": "55555555-5555-4555-8555-555555555555"
}
Example: what comes back
{
  "post_id": "55555555-5555-4555-8555-555555555555",
  "status": "pass",
  "findings": [],
  "validation_id": "v1.opaque"
}

Publish a post

publish_blog_postChanges your site or sends something

What you can ask

  • Publish the brake pad post.
  • Put that draft live.

What it does

Publish a blog draft now.

This asks you first, every time.

What it needs

  • The draft you want to publish.
  • A passed check of that exact draft. Your assistant runs it first.

What it won't do

It publishes only the version that passed the check. If the draft changed since then, it checks again first. To take a post down, your assistant sends us a request.

What your assistant sends

FieldRequiredDescription
post_idstringYes
validation_idstringYesFrom draft_blog_post or validate_blog_post for this draft as stored.
publish_atstringNoNot available yet; posts publish immediately.
confirmobjectYesRequired. One line stating the change, in the owner's words.
idempotency_keystringNoOptional. Reusing a key within 24 hours returns the first publish's result instead of publishing again.
owner_requeststringNoWhat the owner asked for, in their words, one sentence. Optional. Stored with the audit record so their team can see what owners ask for.
Example: what your assistant sends
{
  "post_id": "55555555-5555-4555-8555-555555555555",
  "validation_id": "v1.opaque",
  "confirm": {
    "intent": "Publish the brake pad post"
  },
  "idempotency_key": "publish-post-0930"
}
Example: what comes back
{
  "published": true,
  "post_id": "55555555-5555-4555-8555-555555555555",
  "slug": "when-to-replace-brake-pads",
  "live_url": "https://esg.example/blog/when-to-replace-brake-pads",
  "published_at": "2026-09-30T14:00:00.000Z",
  "note": "Live now. Unpublishing or deleting it is a submit_request."
}

Edit a post

update_blog_postChanges your site or sends something

What you can ask

  • Fix the typo in the second paragraph of our last post.
  • Change the photo on the brake pad post.

What it does

Edit a post's title, summary, body or main photo, live or draft.

This asks you first, every time.

What it needs

  • The post you want to change.

What it won't do

A live post gets the same check as publishing. If your change fails it, you get the reason. It won't write over a newer edit someone else made.

What your assistant sends

FieldRequiredDescription
post_idstringYesThe post, from get_blog_context.
titlestringNoThe new title. Omit to keep it.
excerptstringNoThe new summary. Omit to keep it.
bodystringNoThe new body, markdown. Omit to keep it.
hero_media_idstring or nullNoA library photo (list_media) for the post's main photo, or null to remove it. Omit to keep it.
expected_updated_atstring or nullNoThe post's updated_at as you read it. A post changed since is refused (post_changed) rather than overwritten.
confirmobjectYesRequired. One line stating the change, in the owner's words.
idempotency_keystringNoOptional. Reusing a key within 24 hours returns the first change's result instead of writing again.
owner_requeststringNoWhat the owner asked for, in their words, one sentence. Optional. Stored with the audit record so their team can see what owners ask for.
Example: what your assistant sends
{
  "post_id": "55555555-5555-4555-8555-555555555555",
  "hero_media_id": null,
  "confirm": {
    "intent": "Take the photo off the brake pad post"
  }
}
Example: what comes back
{
  "updated": true,
  "post_id": "55555555-5555-4555-8555-555555555555",
  "slug": "when-to-replace-brake-pads",
  "status": "published",
  "changed": [],
  "hero": "removed",
  "version": "2026-09-30T14:00:00.000Z",
  "findings": [],
  "live_url": "https://esg.example/blog/when-to-replace-brake-pads",
  "message": "Saved. The live post shows the change."
}
Write and publish blog posts through your assistant | The Shop Marketing Guys