Building Agentic SEO Workflows: Idempotency, Budgets, and CI
Five patterns for running yeet unattended — retries that don't duplicate, budgets that don't blow out, and a worked weekly automation end to end.
Everything in the previous four guides — MCP tools, the check primitive, auto-merge gates, outcome priors — is designed to be driven by an unattended process, not just a human clicking through a dashboard. This guide is the assembly manual: five patterns for wiring yeet into CI, cron, or a scheduled agent, closing with a full worked example.
The common thread across all five patterns is that none of them require yeet-specific infrastructure on your side. Idempotency is a header. Budgets are a lookup against a fixed cost table. Events are signed HTTP POSTs. If your CI system, your agent runtime, or your alerting stack already speaks HTTP and can verify an HMAC, it can drive yeet-seo unattended today.
a) Retry-safe automation with idempotency keys
Every mutating tool and endpoint — submit_urls, run_agent_job, run_check/POST /api/v1/check, card actions, hypothesis actions, content generation — accepts an idempotency_key (MCP) or Idempotency-Key header (REST). The server caches the response for 24 hours against that key. In any loop that might retry — a CI step that times out and re-runs, a webhook handler that redelivers, a scheduled agent that crashes mid-poll and restarts — always pass a deterministic key derived from the logical operation, not a fresh UUID per attempt:
{
"tool": "submit_urls",
"arguments": {
"urls": ["/playbook/new-post"],
"idempotency_key": "submit-2026-07-11-new-post"
}
}
Get this wrong and a flaky network turns one intended submission into five, or one intended check into five separate credit-charging runs.
b) Nightly CI check: fail the build or open an issue
The simplest CI integration is a scheduled job that calls the same primitive covered in the check API guide: POST /api/v1/check, poll GET /api/v1/check/{id} to complete/partial, then branch on the report. A reasonable policy: fail the build (or open a tracking issue via your own GitHub integration) if found.candidates_open spikes past a threshold relative to the last run, or if verified_since_last contains a regressed entry — a regression is the one outcome that should always interrupt someone, regardless of autonomy mode.
c) Deploy hooks
After a deploy, notify yeet so the verifier isn't working from a stale snapshot of the site: POST /api/v1/deploy-notify with the deploy's commit/URL context. The next check's verifier pass then re-scrapes against the deployed state rather than waiting for its normal cadence to catch up — useful if you ship SEO-relevant changes (metadata, routing, content) outside of yeet's own PR pipeline and want outcome tracking to reflect them promptly.
d) Budget control
Every credit-consuming action has a fixed cost:
| Action | Credits |
|---|---|
| Check | 1 |
| Deep investigation | 3 |
| PR opened | 2 |
| Verified hands-off fix | 5 |
Plan quotas (Pro: 60 credits/month; Agent: 300 credits/month — see pricing) are hard daily/monthly caps, not soft throttles. When a call would exceed the remaining budget, the API returns 402 { "error": "insufficient_credits" } as covered in the check API guide. Design your automation to treat that 402 as the stop signal it is — don't retry it, don't backoff-and-retry it, just halt and surface it. Combine this with the expected-value math from the outcome priors guide to decide, before you spend, whether a low-verified_rate subcategory is worth the credits an aggressive automation would burn on it.
A practical way to combine (b) and (d): before a scheduled check runs, have the automation reason about remaining monthly credits divided by remaining days in the billing cycle. If a nightly job is burning more than that daily allowance — say, because deep investigations keep triggering at 3 credits a pop — throttle to every other night rather than running headfirst into a 402 mid-month and losing coverage entirely for the remaining days.
e) Events over polling
Register a webhook once instead of polling everywhere:
POST /api/v1/webhooks
Authorization: Bearer yseo_your_key_here
Content-Type: application/json
{
"url": "https://your-service.example.com/hooks/yeet",
"events": ["card.*", "check.completed"]
}
Wildcard subscriptions like card.* or even * are supported. Every delivery carries X-YeetSeo-Event, X-YeetSeo-Webhook-Id, and X-YeetSeo-Signature: sha256=<hmac> computed against your endpoint's whsec_ secret — verify it before trusting the body, and respond within the 10-second delivery timeout (queue heavy processing async). The event catalog includes card.created/applied/dismissed/verified/regressed, agent_run.started/completed/failed, check.started/completed, rank.changed, hypothesis.created, property.connected/disconnected, and citation.detected.
Worked example: a weekly Claude Code automation
Put together, here's a full weekly loop, scheduled via a cron trigger driving a Claude Code session with the yeet-seo MCP server connected on a write-scoped key:
- Run the check. Call
run_checkwithidempotency_key: "weekly-check-2026-W28". - Poll to terminal. Call
get_checkon an interval untilstatusiscompleteorpartial. - Approve nothing automatically. This automation is read-and-report only — it never calls
approve_hypothesisor any card-mutating tool. Autonomy mode and auto-merge gates (see the auto-apply deep dive) already govern what yeet does unattended; this loop's job is visibility, not additional authority. - Post the report to Slack. Format
found,did, andverified_since_lastinto a summary andPOSTit to a Slack incoming webhook URL — plain HTTP, no yeet-specific machinery involved. - Escalate
awaiting_approvalspecifically. Anything inawaiting_approval.hypothesesorawaiting_approval.prs_awaiting_mergegets its own tagged message (or a separate high-priority channel) — that's the queue that structurally requires a human decision, and it's the one thing this automation should make impossible to miss.
This pattern generalizes: the automation is deliberately read-heavy and escalation-only. Any workspace that wants more autonomy than that grants it explicitly through set_autonomy/PUT /api/v1/autonomy (see auto-apply internals) — the reporting loop itself never needs to ask for more authority than "look and tell someone."
Quick FAQ
Q: Should idempotency keys be reused across different weekly runs?
A: No — derive a new key per logical run (e.g. include the ISO week), so retries within a run dedupe but distinct weekly runs don't collide with each other's cache.
Q: What should happen if the weekly check returns partial?
A: Report what's there and note it's partial in the Slack message; don't block the whole automation waiting past the 20-minute deadline — the next scheduled check fills in the rest.
Q: Is there a way to test the webhook signature locally before going live?
A: Register a webhook pointing at a local tunnel endpoint, capture a real delivery, and verify your HMAC implementation against the actual X-YeetSeo-Signature header before wiring it into production alerting.