Publishing and GitHub
How an approved idea actually becomes a live page - the pull request, the checks, and who presses merge.
On this page
DispatchSEO never touches your live site directly. Everything it ships goes through GitHub, as a pull request, checked before it merges. This page covers the mechanics: what opens the PR, what decides whether it merges itself, and what happens after.
The flow
- You (or auto mode) approve a suggestion in the queue.
- The builder picks up the oldest approved idea and opens a pull request
against your site's own repo, labeled
seo. - Automated checks run against it - your repo's own build, a preview deploy, a code review pass, and for tools a live functional test (below).
- You review it, or it merges itself. Either way, once it merges, the page goes live on your site's next deploy.
Never a direct push to your main branch. The label and the pull request are the review surface - even in fully automatic mode, "merged a green PR" is a different, safer claim than "committed straight to main," because it means every gate below actually ran and passed before anything shipped.
Publish modes
Whether that merge needs you is a per-project setting, not a fixed behavior:
- Semi-automatic (recommended to start): your agent researches and builds on its own, but a PR only ships once you press Merge on the dashboard.
- Automatic: PRs whose checks all pass merge themselves. You watch the activity feed and can still undo from the dashboard.
Change it any time on Automations and modes, or from the dashboard's Semi/Auto toggle in the top bar - see Step 5 of the setup wizard for what it means when you first pick it. It's never a final answer.
Auto-merge mechanics
The actual merging - even in automatic mode - is done by
seo-auto-merge.yml, a workflow the pipeline installs into your repo. It
runs on an hourly schedule plus whenever a check reports, so a PR usually
merges within a minute of its last check finishing, not on the hour.
Each pass, for every open PR labeled seo:
- It checks whether the project's
auto_mergeautomation flag is actually on right now (read live from the dashboard) -offmeans every eligible PR just waits, checks and all. - It classifies the PR by shape: a guide PR must touch only files under
your repo's declared publish directories (
.dispatchseo/publish-paths, or the reference-stack defaults); a tool PR must carry thetool-validatedlabel from the validation workflow below. Anything else - a structural change, an unvalidated tool - waits for you no matter what the checks say. - It reads every check on the PR (your build, the preview deploy, code review) into one verdict: any check still pending waits for the next pass, any failing check leaves the PR for you, and zero checks reported is never treated as green - no gate is the same as an unsafe gate.
- Only when every check has actually passed does it squash-merge and delete the branch.
Tool validation
Guide PRs merge on the checks above. Tool PRs get one more gate first, run by
seo-tool-validate.yml: a real production build, started for real, opened
in a headless browser, with every interactive element on the new tool's page
actually clicked and checked - inputs filled, outputs verified, edge cases
(empty input, garbage input) confirmed not to break it, zero console errors.
Pass, and the PR gets a tool-validated label; fail, and it gets
tool-validation-failed plus a comment naming exactly what broke.
That label is what seo-auto-merge.yml looks for before it will touch a
tool PR at all - a widget that merely builds is not the same claim as a
widget that was proven to work.
Why validation holds no secrets
The validate job checks out and runs the tool PR's own code - code an LLM
wrote, in a PR that could in principle contain anything. So it deliberately
carries no secrets at all, not even the project's own MCP key: it can only
read and label, never merge or report outcomes. A separate merge job, on a
fresh runner that never checks out the PR's code, does the actual
squash-merge and phones the result home to the dashboard. Untrusted code and
write credentials never share a job.
The one-tap Merge button
On the dashboard (and over MCP via merge_pr), an open, green PR gets a
one-click Merge button - useful in semi-automatic mode, where you're
approving the merge yourself instead of waiting on the workflow above.
It needs a GitHub token with write access to your repo (GH_MERGE_TOKEN on
a classic install, or the pasted token from the wizard's GitHub step on
Docker). Without one, the button degrades to a plain link to the PR on
GitHub - you can still merge it there, DispatchSEO just can't do it for you.
On a Docker install using the bundled builder, this same token is also how
the builder reaches your repo at all to open PRs in the first place - see
Step 6 of the setup wizard for
what happens if it's skipped there.
After a PR merges
Three things happen automatically, no button to press:
- The page is logged (
log_page) - it enters the dashboard's Guides list and the internal-linking picture your agent reads before writing the next one. - Indexing is requested - the page joins the "Get it on Google" queue for a Search Console indexing request, and DispatchSEO starts checking whether Google actually picked it up.
- Its keyword starts being tracked - it joins the daily rank-check rotation, so its position shows up on Rankings without you doing anything further.
A freshly logged page shows as "awaiting publish" until its URL actually serves HTTP 200 - the merge and the live deploy aren't the same moment, and DispatchSEO waits for the real one before calling a page live.
Cloud vs self-host: connecting GitHub
The mechanics above are identical either way; only how DispatchSEO authenticates to your repo differs:
- Cloud: one-click install of the DispatchSEO GitHub App on your repo. The platform commits the pipeline and manages PRs through the App - no tokens to create or paste.
- Self-host: you generate a personal access token once
(
GH_MERGE_TOKEN, or pasted through the wizard) and DispatchSEO uses it for merging, and on a Docker install, for the builder's own clone-and-push access.
See Cloud or self-hosted for the full comparison, and Step 6 of the setup wizard for exactly what that step asks for on a self-hosted install.
GitHub Actions costs
The 11 workflows above run as GitHub Actions in your own repo, on your own GitHub account - not on DispatchSEO's infrastructure, and we never touch or mark up what GitHub charges for them. Each connected site burns roughly 800 Actions minutes a month keeping research, builds, and validation running.
GitHub's free tier includes 2,000 Actions minutes a month, so your first two sites are comfortably covered at $0. From a third site you're past that allowance - GitHub Pro is $4/month and is the simplest fix, or you can let GitHub bill the overage directly. Rough numbers, since actual minutes depend on how long your builds run:
| Sites | Roughly |
|---|---|
| 1 | $0 |
| 2 | $0 |
| 3 | ~$4 (GitHub Pro covers it) |
| 4 | ~$5 |
| 5 | ~$10 |
| 6 | ~$15 |
| 7 | ~$20 |
| 8 | ~$24 |
| 9 | ~$29 |
| 10 | ~$34 |
Treat this as a ballpark, not a bill - repo size and build time move it in either direction.
GitHub doesn't bill you by default - it pauses you
A personal account has no Actions budget by default. Once you're past the free minutes, GitHub does not send you a bill - it silently pauses your workflows. Fixing it takes two things, and the first is easy to miss: a payment method on file, then a budget, because a budget does nothing without a card behind it.
At github.com/settings/billing, add your payment method first - GitHub refuses to create a budget without one. Then open Budgets and alerts -> New budget. Under Budget type choose Product-level budget (it preselects the AI credits one), continue to Next: Configure budget, and pick Actions as the product before setting your amount. (GitHub used to call this a "spending limit"; the phrase still turns up in some of its error messages, but the control is now a budget.) Set yours before you need it, not after a build goes mysteriously quiet.
One way to sidestep all of this: public repositories get unlimited free GitHub Actions minutes. If your site's repo can be public, GitHub Actions costs nothing at all, regardless of how many sites you connect.
Turning it off for a site
Settings has a Disconnect repo button in the danger zone. It switches off the
seo-* workflows in your repo, deletes them along with the .dispatchseo folder
and the SEO_MCP_API_KEY secret, and clears the connection. Every schedule stops,
so it stops using your GitHub Actions minutes.
What it never touches: the guides and tools that already shipped, any templates the setup agent added to your site, and everything this dashboard has tracked - keywords, rankings, history. Reconnecting the repo later re-installs the pipeline.
The button is there for your first site too. That one can't be deleted, because the rest of your setup is anchored to it, so disconnecting is how you stop it.
If GitHub can't be reached - a rotated token, an uninstalled App - the repo stays connected on purpose and the error says why. Clearing the connection while the workflows were still live would leave them running against a project that no longer claims them, with nothing left to retry from.
Claude Code can do the same thing with the disconnect_repo tool.
Publishing pace
However fast ideas get approved, guides ship at a flat rate: at most one guide per UTC calendar day, permanently - not "while the site is new," a fixed ceiling. It's the single biggest protection against DispatchSEO ever reading as scaled, templated content: a queue of ten approved guides still surfaces as one PR a day, every day, whether the project is one week old or two years old. Tools aren't rate-limited the same way - a validated tool PR can ship the moment it passes.
DispatchSEO