UgrĂĄs a tartalomhoz
← Back to the journal

Content scheduler — how it works, the pipeline that published this very article

How the Nortinia Sales AI content scheduler works: target, plan, generate, eval, approval, publish. Idempotency keys, retry policy, 2,847 articles in six months.

Content scheduler — how it works

The Nortinia Sales AI content-scheduler module plays two roles at once: marketing automation for the sales pipeline (articles, newsletters, sales-driven content), and infrastructure for publishing Nortinia's own family-site articles. The article you are reading right now was put out by this same pipeline — one target, one generation, one eval pass, one publish HTTP POST.

The target concept

A target defines a concrete destination:

  • URL — where we publish (storefront /api/blog/publish endpoint)
  • Auth — bearer token for the receiving panel
  • Topics — which subjects we generate for (e.g. salesai, assistant, content)
  • Cadence — how often (weekly N articles, or event-triggered)
  • Locale — which language (HU/EN today, DE/SK next quarter)
  • Approval mode — auto-publish or manual editor queue
  • Brand voice — tone, banned phrases, mandatory CTAs, persona

The target lives in Postgres under FORCE RLS, and every generation starts from it. A tenant can have multiple targets (e.g. nortinia.com + finance.netorigo.com + a separate customer site).

Pipeline stages

  1. Trigger — timer (BullMQ cron) or event (new product launch, new lead segment, blog cadence). The trigger picks a target and a topic.
  2. Plan — the engine gets a structured prompt with the target's persona and tone, and returns an article outline: title, structure, key claims, expected length.
  3. Generate — the full article is written from the outline. One call, one model. No retry mid-generation: if it errors, drop and start a new job.
  4. Eval — strict gate (see the Content Studio piece). Scores on six dimensions. If it fails, back to the generator — max 3 iterations.
  5. Approval — auto mode publishes immediately; manual mode parks it in a queue for an editor to approve or fix.
  6. Publish — HTTP POST to the receiving panel's /api/blog/publish endpoint, idempotent by articleId.

Idempotency keys

The scheduler's central rule: the same articleId never goes out twice. If the HTTP POST returned 2xx, we record it locally; if the receiving side already has the articleId, it responds 200 and does not duplicate.

Why this matters: the retry policy makes 3 attempts with exponential backoff (5s, 30s, 300s). If the first attempt succeeded but the network dropped before the response, the second attempt must not publish again. The articleId is the safety rail.

Retry policy and failure modes

Four failure categories:

  • TRANSIENT (5xx, network timeout): retry up to 3x
  • AUTH (401, 403): dead-letter queue, ops alarm — bearer token expired or revoked
  • VALIDATION (400, schema mismatch): dead-letter queue, content team alarm — Zod validation failure on the receiving side
  • DUPLICATE (200 with existed: true): logged, no action

The dead-letter queue gets a weekly review. In the first half of 2026 it accumulated 23 items: 18 AUTH (one customer forgot to rotate a token), 4 VALIDATION (a new schema field we were not yet generating), 1 unknown (Cloudflare 522, eventually resolved).

Per-target config — one concrete example

This article was produced with the following target:

{
  "id": "01HX-NRT-SEED-TARGET-NORTINIA-SALESAI",
  "url": "https://nortinia.com/api/blog/publish",
  "topics": ["salesai"],
  "cadence": "weekly-2",
  "locale": "hu+en",
  "approvalMode": "manual",
  "brandVoice": "nortinia-technical-honest"
}

Brief on the nortinia-technical-honest brand voice: argue with concrete numbers, never use "revolutionary" or "disruptive", flag what we do not know or have not built, and admit at least one mistake per article.

What the scheduler does not do

  • It does not send email directly — that is a separate newsletter pipeline
  • It does not post on social — content-scheduler publishes blog articles only; the social-post system (LinkedIn, X) is a separate module and sends a short variant derived from the article
  • It does not auto-optimise SEO — SEO keyword targeting is a separate feature, opt-in per target

Numbers after six months

  • 2,847 articles published across 14 design-partner tenants
  • 67% first-iteration eval pass rate
  • 12-minute average end-to-end pipeline time (target read → publish HTTP 200)
  • 0.31 USD average model cost per article
  • 23 dead-letter items, all retroactively processed

What we are building now

  • Article versioning — every iteration should be traceable, so a tenant can see why eval failed on the first try
  • In-line editing in the manual queue — today we edit from code or a separate UI; this is a 2-week UX focus
  • Cross-target learning — two similar-industry targets could share a learning bank, but the privacy side is non-trivial (we do not want one tenant's content samples leaking into another's). Deferred.

The scheduler documents itself: this article exists, you are reading it, so the thing works.

Let's talk about your project

Tell us what you are building — we will figure out how to help.

Content scheduler — how it works, the pipeline that published this very article — Nortinia Journal | Nortinia