Content Loops: Turn Your Own Data Into Published, Keyword-Targeted Content
Most "programmatic SEO" is a spreadsheet, a template, and a one-off script that nobody re-runs. The data keeps changing; the pages don't. Content loops close that gap: they are a standing Cogny automation that watches a data source and publishes a real piece of content every time something new appears, in your voice, against your keywords, into the system your site already reads from.
What a content loop is
A content loop has four parts, and the wizard on the Reports tab collects them in order:
- Source. A BigQuery table, an RSS/Atom feed, a long document in your context tree, or the pages of your own site that already perform. For BigQuery you pick the dataset and table and the column that identifies a row (a date, an id, a slug). That column is the cursor: each run only looks at rows it has not seen. If a table pick isn't enough, you can write the read-only query yourself. For a document, a research paper, a report, a guide, the loop deconstructs it into its sections (markdown headings, numbered section lines, appendices; references are skipped) and each section becomes one piece, one per run, until the whole document is covered. A document without headings is split into parts of roughly a page. Tick "Keep going with new angles" and the loop does not stop when the document is covered: it starts another pass with the next keyword from your keyword node as the angle, so one paper becomes one explainer per section per keyword. The fourth source is your own best-performing pages: with Google Search Console connected, the loop reads the pages under a path that clear a click or impression threshold over the last 28 days and writes each one once per language you list, as a native piece for that market with its own query, not a word-for-word translation. It publishes through the same destination, the way that destination already stores languages, and stops with a clear message if it cannot hold one.
- Destination. One place content can live, pinned to one target: a Supabase table your site renders from, a Webflow or Framer CMS collection, a Sanity document type, a Shopify blog, a WordPress or Ghost draft, a Lovable site. Or your site's own repository: the coding agent adds the piece where your posts live and opens a pull request, no integration needed. Analytics and ads connections never appear in this list.
- Brief. The content type, where the target keywords come from, and a writing style. Keywords can come from a context-tree node you maintain, from Google Search Console's live top queries (the writer prefers queries with impressions but a weak position), from Google Ads keyword ideas, or the writer picks one specific query itself. Styles range from your brand voice (the default) to news wire, a feature, a markets note, a plain how-to or a LinkedIn post, or your own instructions.
- Schedule. A cron, hourly at most. Loops start paused.
There is no field contract to write. Before publishing, the writer reads the destination itself, its columns or fields and its two most recent items, and maps the piece onto exactly those conventions.
Brand voice and compliance constraints are not part of the brief either. They come from your context tree automatically, the same way they do for every ticket Cogny's agents run.
Test first, then let it run
When you create a loop, Cogny immediately writes one test piece from the newest source item. The test run is read-only: the writer can inspect your schema and your existing rows, but it cannot publish, and the cursor does not move. The draft lands as a ticket on the loop's page. If the angle is wrong, change the brief and press Test piece again.
When the draft reads the way you want, enable the schedule. From then on every run:
- fetches the source, drops everything already covered, and takes the next new item (one per run by default, up to five);
- mints one ticket per item and hands it to the ticket executor with the per-item brief;
- publishes through the pinned destination, or opens a pull request;
- records what was published, with slug or URL, as the ticket result.
Nothing new in the source is a quiet run: no ticket, no credits spent.
What about the rows that were already there? By default the first live run writes the newest item and marks everything older as already covered, so a daily recap loop never publishes last month's recap as if it were news. If you want a page for every row that exists, for a product catalog, say, tick "Also write the items that already exist in the source" when you create the loop and it works through them newest first, one per run.
The rules that make it safe to leave running
Every number is traceable. The writer gets the source item as data and a hard rule: any figure, name, date or claim must come from that item or from a read tool it called during the run. An item without enough substance ends the run without publishing. This is the rule that stopped us from shipping a fabricated "daily recap" for a fintech customer, and it stays.
One tool, one target. The publish authorization is an action grant: integration, write tool, and a pinned field such as the table or collection. The executor checks every call against it. A loop that is allowed to insert into one table cannot touch another one, and cannot call any other write tool. A loop that publishes through your repository never publishes directly at all: every piece is a pull request you review.
A broken source pauses itself. Three consecutive fetch failures (a query error, a dead feed) pause the loop and post to Slack. A paused loop never mints a ticket.
Every piece is written for search and for answer engines. The writer works against a fixed rulebook: one primary query per piece, front-loaded in the title, the first sentence, one subheading and the slug; a summary sized as a meta description with the query and a fact in it; subheadings phrased as the questions people search; entities named in full; at least one internal link with descriptive anchor text. For answer engines, the first paragraph is a self-contained answer that reads correctly when quoted alone, every key fact is its own sentence with its number, date and source, terms are defined on first use, and a short "Questions people ask" section closes pieces that answer more than one question. Before publishing, the writer runs a ten-point check and reports it in the run summary.
Each piece is a ticket. Runs show on the loop page and on the Tickets board, so every published piece has a card with its source item, the run transcript, and the result. That is also where search performance will attach as we extend the loop.
Publishing into Supabase
Many sites render content from their own database. The Supabase integration now carries one write tool, insert_row, built for this: it inserts a single row with server-side literal quoting (strings dollar-quoted, objects and arrays as jsonb), returns the row, and is greylisted by default so it never runs without an explicit grant. Pin it to a table in the loop wizard and your site picks the piece up on its next render, with no pull request in the way.
Where it goes next
Phase 2 closes the loop on ranking: after seven and twenty-eight days the ticket's analysis step checks Search Console for the published URL, records impressions and the queries it appears for, and writes what worked back into the keyword node so the next brief is better than the last. After that, a create_content_loop tool so your own agent can set one up over MCP without opening the wizard.