Internal · NiCE Organic Intelligence · Updated 30 Jul 2026

The pipeline, and every dial on it.

A fleet of n8n workflows watches the market, scores what matters, writes content briefs, and checks three weeks later whether published work actually moved anything.

This page is both the briefing and the atlas: what it does, where you look, how you talk to it, where every configurable piece lives — and what is honestly still missing.

2
live signal feeds
21
day review window
15
documented dials
6
days since it last ran

01 — The story

Every piece of content traces back to a scored signal

Marketing teams drown in market noise and guess what to publish. This replaces the guessing with a closed loop — and nothing is fire-and-forget.

1 · Collect
Signals arrivenews · search demand
2 · Score
0–100fixed maths + bounded AI nudge
3 · Board
Opportunity Boardeverything ≥ 40
4 · Brief
AI content briefangle · hook · post
5 · Publish
A human ships itand pastes the URL back
6 · Verify
After 21 daysmoved / flat
7 · Recycle
Flat re-entersas a Refresh opportunity

A flat verdict — the URL never surfaced in search, share-of-voice didn't move — automatically queues a Refresh opportunity, so failed bets come back with a new angle instead of being quietly forgotten.

Who selects the topics? Nobody — here's how a board row is born

No person curates the board. Every row is generated by a three-step funnel, and the human decision starts at the board, not before it:

  1. The watch-list decides what's monitored (the only "selection", set once in config): every morning Google News is searched for the last 7 days of "NiCE CXone" OR Genesys OR Five9 OR Talkdesk OR Verint OR Zendesk OR "Salesforce Service Cloud" OR "Amazon Connect" OR "Zoom Contact Center" — the list lives in entities.json → ingest_query_competitors (see § 04). Typically 25–40 articles return.
  2. Code classifies & scores each article, deterministically: a category from a fixed dictionary (AI agents · Contact center · Enterprise AI · Voice AI · Customer experience · Analyst recognition · Workforce management · Competitive landscape · Market moves), which brands are mentioned, sentiment, and a base score 0–70. No AI yet — the same article always scores the same.
  3. Claude clusters & names: related articles merge into one opportunity (three Sopra Steria deployment stories → one cluster), and Claude writes the topic line you see on the board, the why it matters, trigger type and format suggestions — plus a bounded −15…+30 score nudge.

Final score = base + nudge. Score ≥ 40 → it becomes a board row. Below 40 → stored for trend maths, never shown. Your team's job begins here: pick rows, request briefs, publish.

How scoring stays honest — the part that makes trends comparable

Each signal gets a deterministic base score 0–70 computed in code — topical relevance, brand/competitor presence, engagement, recency. The same signal always scores the same. Claude only adds a bounded −15…+30 nudge for what code can't judge (newsworthiness, search demand, format gaps). Day-over-day score movement is therefore real market movement, not AI mood. Thresholds: ≥40 lands on the board · ≥60 makes the digest · ≥80 is flagged Immediate.

An editorial gate keeps the board clean: stock-price, earnings and layoff stories are auto-marked Intel-Only — tracked for awareness, never turned into content briefs.

A blind spot worth knowing about

The watch-list contains competitors. It does not contain our own acquired brands — so Cognigy has never once entered the pipeline, despite 146,824 people seeing us for Cognigy searches in the last 28 days. The engine can only see what it is pointed at. Fixing it is one line in entities.json (§ 04, row 2).

02 — Where to look

Four human surfaces, one machine memory

Supabase feeds them all — you never need to open it, but you can.

SurfaceWhat you'll findWhen to check
Opportunity Board sheetEvery opportunity scoring ≥40: topic, score, why it matters, trigger, suggested formats, owning team, status (Immediate / Weekly Queue / Monitor / Intel-Only / Refresh-Queued)Daily — fresh rows after the 07:00 run
Content Briefs docReady-to-execute briefs formatted for writers: angle, hook, keywords, key messages, publish-ready LinkedIn post, CTA. Mirrored to the Sheet's "Content Briefs" tabWhenever a brief is generated
Teams digest chatDaily card with opportunities scoring ≥60, plus failure alerts if a workflow breaksArrives by itself — once the production Teams webhook is configured. Currently a test endpoint
n8n engineThe workflows themselves. "Supabase with n8n" = the live fleet · "Turso with n8n" = the retired archive. Executions tab shows every runOnly when curious or something looks off
Supabase dataThe machine memory: signal history, opportunities, briefs, publications, verdicts, error log — browsable in Table EditorRarely; useful for "show me everything we ever scored about Genesys"
What runs when — all times America/New_York
TimeWorkflowOutput
Daily 07:00Signal SpineNew signals → scores → board rows → digest
Daily 09:00Outcome Loop (review)Verdicts on publications past their review date
Monday 08:00Trend ReportTrending vs dying topics (7d vs prior 7d) — pure maths, zero AI cost
MondaySEMrush Search DemandTracked-keyword volume + related-keyword discovery, merged into the spine
On demandContent Brief AgentOne brief for the top un-briefed opportunity

⚠ These are the configured schedules. They only fire while the host machine is running — see § 06.

03 — How to interact

Three touchpoints, one that actually matters

All are authenticated webhooks — requests need the X-Webhook-Token header (value lives in .apsolut/10-config/env/.envN8N_WEBHOOK_TOKEN; ask the operator once and save it).

1 · "We published something" — close the loop most important

The single habit that makes the whole system learn. When a brief becomes a live URL, tell the pipeline — each brief's Doc entry carries this instruction pre-filled with its ID:

curl -X POST http://localhost:5678/webhook/nice-publish \
  -H "Content-Type: application/json" \
  -H "X-Webhook-Token: <token>" \
  -d '{"brief_id":"BRIEF-20260721-XXXXXXXX","published_url":"https://www.nice.com/blog/your-post"}'

Optional: "review_days": 30 (default 21, max 90). After that window the daily 09:00 review issues a moved / flat verdict automatically.

2 · "Give me a brief now"

curl -X POST http://localhost:5678/webhook/nice-brief-run \
  -H "X-Webhook-Token: <token>"

Picks the highest-scoring opportunity with no brief yet, writes the brief, appends it to the Doc and Sheet. The response includes a preview.

3 · "Run the pipeline / trend report now"

curl -X POST http://localhost:5678/webhook/nice-spine-run  -H "X-Webhook-Token: <token>"   # collect + score now
curl -X POST http://localhost:5678/webhook/nice-trend-run  -H "X-Webhook-Token: <token>"   # trend report now

The spine also accepts {"semrush": true} to pull the search-demand branch in the same run.

04 — The configuration atlas

Everything tunable, in one filterable table

Type what you want to change — competitor, keyword, schedule, sheet, secret.

I want to change…Edit hereThen
Competitor list (tracked + detected)north-star registry C:/sites/d/north-star/config/companies.json; competitors north-star doesn't track (Talkdesk, Verint, Cisco, Avaya, 8x8) → .apsolut/10-config/entities.json → extra_competitorsPropagation chain ①
Which brands go into search queries (News + Reddit)entities.json → ingest_query_competitors — currently Genesys, Five9, Talkdesk, Verint, Zendesk, Salesforce Service Cloud, Amazon Connect, Zoom Contact Center. ⚠ Our own brands (Cognigy, LiveVox, Playvox, Actimize) are not here — they are match-only, so their news is never fetchedPropagation chain ①
NiCE brand aliases & match rules (what counts as "us")entities.json → self.match — any-case terms (CXone, Cognigy…), case-sensitive rules, the all-caps-NICE context gate that excludes the UK health instituteChain ① — 75 matcher tests must pass
Kill or add a single aliasentities.json → alias_overrides.drop / alias_additionsPropagation chain ①
News query window (last 7 days)scripts/build-spine-workflow.mjs → RSS_URL (when:7d)Rebuild + deploy (chain ① from step 3)
Subreddits watched005-reddit-ingestion/workflow.json → Build Subreddit List code nodeDeploy
Scoring — topic weights, thresholds (40/60/80), signal cap (25)scripts/build-spine-workflow.mjsTOPIC_RELEVANCE map, baseScore(), MAX_SIGNALS. ⚠ The spine's JSON is generated: edit the builder, never the exportRebuild + deploy
Editorial gate (what counts as Intel-Only)scripts/build-spine-workflow.mjs → intelOnly regex (stock/earnings/layoffs) + the prompt's EDITORIAL SCOPE rulesRebuild + deploy
Schedules (07:00 spine · Mon 08:00 trend · 09:00 review)Trigger nodes in each workflow — timezone America/New_York set on the spine — or ask the operatorDeploy, or edit live in the n8n UI
The Google Sheet (board + briefs tabs).env → GOOGLE_SHEET_ID; the brief agent's Sheets node also carries the ID inline — change both. Column headers must exactly match scripts/sheets-ensure-headers.mjsDeploy
The Content Briefs Google DocDoc ID appears in the brief agent (Docs — Get End Index URL + 2 more spots). A new Doc must be shared as Editor with n8n-local-service-v1@n8n-local-nice.iam.gserviceaccount.comDeploy
Teams channel for digests and alerts.env → TEAMS_WEBHOOK_URL (Power Automate URL; payload is an Adaptive Card). Currently a test endpoint — swap for the real channel URLprototype-deploy.mjs --activate
Review window before a publication is verdictedPer publication: review_days in the nice-publish call (default 21, max 90)
Any secret or API key (Anthropic, Tavily, Supabase, SEMrush, Reddit, Google).apsolut/10-config/env/.env — template with instructions in env.example. Never pasted into workflows; scripts create n8n credentials from itsetup-auth-creds.mjs → deploy
Database (tables, indexes, functions).apsolut/09-workflows/000-architecture/supabase-schema.sql — the single source of truth for the schemaOperator applies via tooling

Propagation chain ① — the entity-registry edit ritual

Competitor and alias edits touch five places — search queries, detection, the scoring prompt, verdict domains, tests. One command chain propagates everywhere, and order matters:

cd C:/sites/nice/n8n
node .apsolut/09-workflows/scripts/sync-entities.mjs          # 1. registry into the subworkflows + matcher
node .apsolut/09-workflows/scripts/test-entity-matcher.mjs    # 2. 75 alias tests must pass
node .apsolut/09-workflows/scripts/build-spine-workflow.mjs   # 3. regenerate the spine
node .apsolut/09-workflows/scripts/prototype-deploy.mjs --activate   # 4. ship it
Why the ceremony

Alias matching is where intelligence systems quietly rot — "NICE" also means the UK health institute, and "nice systems" is plain English. The 75-case test suite is the guardrail; it caught real collisions during the adversarial alias review on 11 Jul.

The same discipline has not yet been applied to competitor names, which is why the feed currently pulls in Genesys Logic (a Taiwanese chip maker), Genesys Capital (a life-sciences fund) and a Thermo Fisher spectrophotometer called GENESYS. Worth a second pass.

05 — Adding data sources

Sources never touch the scoring core

The pipeline was built plug-in first. Any new source is a small side workflow that fetches, maps to a 7-field contract, and hands off to the shared normalizer. Everything downstream treats all sources identically.

1 · New
Fetch from the API
2 · Map
To 7 fieldssource_type · source_name · headline · url · engagement · published_at_raw · body_text
3 · Hand off
Shared normalizer
4 · Existing
Dedup → score → board

SEMrush live

Search demand as a signal source — tracked keyword volume plus related-keyword discovery, merged into the spine on Mondays. Went live 24 Jul.

UNUSED SO FAR: a fully populated position-tracking campaign — 3,999 keywords, daily, of which 848 already trigger an AI Overview. Already inside the subscription; nothing new to buy.

Google Search Console connected, not flowing

Our own first-party data — what Google actually showed people and whether they clicked. Property access was granted 30 Jul; the daily feed still has to be built.

HAVE: read access to www.nice.com, verified live.
PERISHABLE: Google keeps 498 days of history and drops the oldest day daily — a one-off backfill should happen before more of it is lost.

Anything else recipe

YouTube, LinkedIn, review sites — same recipe. The proof it works: Reddit was added exactly this way without touching the spine.

TEMPLATE: the plug-in demo workflow
GUIDE: 03-plan/003-phase2-multi-source
EFFORT: roughly a day including tests

06 — Current status

Everything works. Nothing is running.

"Live" below means configured, deployed and proven with real output. It does not mean it fired this morning — read the callout first.

Read this before the table

The engine has not executed since 24 July. Every workflow below is deployed and active, but they live on a local machine rather than a server, so the schedules only fire while that machine is on. Nothing is broken; nothing is arriving either. The move to a permanent home is blocked on one decision — which channel the daily digest should land in.

WorkflowStateNotes
Signal Spine ⇒ Supabase + Sheetsdeployed · daily 07:00Claude scoring active; ~41 news items per run
Outcome Loop ⇒ Supabasedeployed · daily 09:00Publish intake + verdicts; share-of-voice check included
Trend Report ⇒ Supabasedeployed · Mon 08:0012 tracked series
Content Brief Agent ⇒ Supabase + Sheets + Docsdeployed · on demandGrounded with live web search
SEMrush Search Demand ⇒ Supabasedeployed · MondaysLive since 24 Jul; 12 keywords tracked
Ops Error Alert ⇒ Supabase + Teamsdeployed · on failureDurable: every failure lands in the database even if Teams is down
Search Console ingestionaccess granted, not builtProperty shared 30 Jul; feed still to be written
Reddit Ingestionpending OAuthShips when Reddit API credentials are approved
Sales win/loss ingestnot startedNo agreed source system yet
Old Turso fleet (5 workflows)archivedDeactivated — rollback insurance, deletable after a clean week
The hostpaused 6 daysLocal install, last run 24 Jul — the blocker above
The 21 Jul migration, in one paragraph

Persistence moved from Turso — a community database node we had to maintain ourselves — to Supabase, managed cloud Postgres accessed through n8n's native integration over REST. All 352 historical rows migrated and verified, schema and row-level security applied to every table, and all five production workflows ported and live-tested the same day.

Why it matters: zero custom components remain, which is the property that makes the planned move to the company's online n8n instance a configuration exercise rather than an engineering project.

07 — Questions

Asked and answered

Where do I see today's opportunities?

The Opportunity Board sheet, sorted by score. New rows land after 07:00 — on days the host is running. "Immediate" status means act this week.

We published a post from a brief — now what?

Send the one-liner in § 03.1, or ask whoever operates the pipeline to. That's the entire ask — the system schedules its own follow-up and verdicts it three weeks later.

I want to track a new competitor — how hard is it?

One JSON edit plus one command chain (§ 04, chain ①). Minutes, not days. The same applies to removing noisy aliases — and it's worth doing, since the feed currently pulls in several unrelated companies that happen to share a competitor's name.

Can we plug in Search Console or SEMrush data?

SEMrush is already live. Search Console access was granted on 30 Jul and the daily feed is the next thing to build. Neither touches the scoring core — see § 05.

Is everything in the Sheet and Doc also stored in Supabase?

Yes, field for field — Supabase is the system of record; the Sheet and Doc are human-readable copies. The Supabase write is the protected one; Sheet and Doc writes fail soft with a logged warning. Caveat: the mirror is one-way. Hand-edits in the Sheet are not synced back, and the pipeline reads only Supabase. Use the Sheet to read and annotate, not to steer.

What does it cost to run?

Claude usage on the daily scoring run and per brief, bounded at 25 signals per run with briefs on demand. Tavily search on briefs and reviews. SEMrush comes out of the existing subscription. Supabase is on the free tier — worth upgrading before the online move. The trend report is deliberately zero-AI.

Is the AI making the decisions?

No. Code computes the base score deterministically; the AI adds a bounded adjustment and drafts prose. Dedup ledgers, thresholds, verdicts and the editorial gate are all deterministic rules you can read and change.

What happens when something breaks?

The failure fires the error workflow: it's written durably to the ops_errors table and posted to Teams once the real channel is wired. Nothing fails silently — that's a design rule, including soft failures like a Doc-sharing problem.

What is it currently blind to?

Three things worth naming. Our own acquired brands are not in the search queries, so Cognigy news has never entered the pipeline. Conversation on Reddit and LinkedIn is not flowing yet. And why we actually lose deals — sales win/loss — has no agreed source. Each is a decision or an approval rather than an engineering problem.

Who do I ask for what?

Pipeline operations, config edits and new sources: the operator (Aleksandar). Content decisions on board items: the owning team named on each row. This page is regenerated from the vault — ask for a refresh anytime.