Stage 1 — Monthly Research
How the monthly research stage produces 30 approval-ready topics — gap analysis, brief generation, and the Google Sheet handoff.
When Stage 1 Runs
Stage 1 fires automatically on the 20th of every month at 09:00 UTC — that timing gives you ten days before the month ends to review and approve topics, so the daily drafter has an approved queue to pull from starting on day 1 of the next month. You can also trigger Stage 1 on demand any time ("run the monthly research again with these new seed keywords"), which is useful when you want to experiment with different niches or refresh the calendar mid-month. Ad-hoc runs create a new sheet rather than appending to the current one, so your existing approvals are not disturbed.
What Stage 1 Does
The research stage runs through five steps in sequence. Step 1 — call `/seo/blog_gap` with your configured seed keywords, minimum volume, maximum difficulty, and blog URL. Step 2 — take the top 30 gap keywords (trending, rankable, not already covered by an existing post on your blog). Step 3 — create a fresh Google Sheet with the 14-column calendar schema, share it with you as an editor. Step 4 — for every one of the 30 rows, call the LLM to generate a first-person title, a content idea paragraph, and a slug. Step 5 — email you the sheet link with a short summary of what was generated and how the filters landed (existing posts detected, candidates dropped as covered, final gap count).
The email that lands in your inbox
What Stage 1 hands back to you.
The Calendar Sheet Schema
Every monthly calendar sheet has 14 columns. `id` and `proposed_at` are set by the workflow. `keyword`, `slug`, `title`, `content_idea` come from the research + LLM brief. `search_volume`, `difficulty`, `competition` come from DataForSEO. `status` is what you control (values: proposed, approved, rejected). `draft_status` is what the workflows control (pending, sent, approved, published, failed). `draft_message_id` is set by the drafter when it emails you the draft (used by the publisher to match your approval reply). `publish_url` is set by the publisher on successful publish. `notes` is a free-form column for both you and the workflows to use.
You can edit the `keyword`, `slug`, `title`, or `content_idea` columns on any row before approving. The drafter uses the final values at the moment it picks the row, so edits are always respected. You cannot usefully edit the other columns — they're workflow state.
What Happens If Stage 1 Fails
Stage 1 has built-in hard-fail behavior rather than quiet drift. If blog gap returns fewer than 3 usable keywords (your filters were too strict, or your seed keywords were too narrow), the workflow aborts and emails you a diagnostic explaining what happened and what to adjust (widen seeds, raise max_difficulty by 10, lower min_volume to 50). If some rows get their LLM brief generation and others do not, the sheet is still delivered with the successful rows — the failed rows get a "| BRIEF FAILED" suffix in the notes column so you can ask the AI to regenerate just those. The pipeline never silently ships an empty or half-empty calendar.