blog_post — SEO Blog Post
| Field | Value |
|---|---|
| Type key | blog_post |
| Label | SEO Blog Post |
| Agent queue | blog-writer |
| Plan tier | Free+ (starter, growth, professional) |
| Credits/unit | 2 |
| Monthly range | 2–8 |
What it produces
A full SEO-optimised long-form blog post (typically 800–2,000 words). The agent writes to a BlogPost record with title, slug, full body, target keyword, meta title, and meta description.
Pipeline sequence
content_brief (prerequisite) ──→ blog-writer ──→ BlogPost: dm_review
│
[Gate 1: DM Portal]
│
dm-approve ─┼─ dm-reject → re-queued with feedback
│
BlogPost: client_review
│
[Gate 2: Dashboard]
│
approve ───────┼───── reject → re-queued with feedback
│
BlogPost: client_approved
│
[auto-ingest to CMS]
│
BlogPost: publishedStatus machine
| Status | Set by | Next |
|---|---|---|
dm_review | blog-writer worker on completion | → client_review or → rejected |
client_review | DM approves (POST /dm/v1/blog/:id/dm-approve) | → client_approved or → rejected |
client_approved | Client approves (POST /client/v1/blog/:id/approve) | → published (auto) |
rejected | DM rejects or client rejects | Re-queues blog-writer with rejection note |
published | Auto-ingest triggered on client_approved | Terminal |
Fields: dmRejectionNote, dmApprovedBy, dmApprovedAt, clientRejectionNote, clientApprovedBy, clientApprovedAt, publishedAt, version.
HITL gates
| Gate | Location | Who | Action |
|---|---|---|---|
| Gate 1 | DM Portal → Approvals | Reviewer | Approve (→ client_review) or Reject with note (→ rejected, re-queue) |
| Gate 2 | Dashboard → Deliverables | Client | Approve (→ client_approved) or Reject (→ rejected, re-queue) |
Deliverable status progression
| Deliverable status | Trigger |
|---|---|
generating | Worker picks up the activity |
in_review | BlogPost created with dm_review |
needs_approval | DM approves → client_review |
approved | Client approves → client_approved |
published | Auto-ingest completes |
Dependencies
- Requires:
content_brief— the planner always schedules 1 content brief per blog post. The brief’soutputPayloadis injected into the blog-writer’s input. - Optional:
keyword_cluster— primary keyword data used by the brief writer and surfaced in the blog writer’s context. Activity.dependsOnis set so the blog-writer job does not start until the linked content_brief activity reachesdone.
DB records created
| Record | Created by | Notes |
|---|---|---|
DeliverableTemplate | deliverable-planner | Monthly quota (e.g., “4 blog posts”) |
Activity | activity-planner | One per blog post; type = blog_post |
Deliverable | blog-writer worker | Client-facing status record |
BlogPost | blog-writer worker | Full content record with status machine |
Auto-ingest
On client_approved, a CMS ingestion job is triggered automatically. The BlogPost moves to published and a PublishedContent record is created linking the post to the tenant’s content history.
Key rules
- The planner must include 1
content_briefperblog_post. Creating a blog post without a brief is invalid. - On rejection (either gate), the blog-writer is re-queued with the rejection note injected as additional context so the next run addresses the feedback.
versionincrements on each re-run after rejection.