A code-first GTM engineering portfolio: real, version-controlled signal → route → act pipelines built on Sygnal buyer-intent signals with a 100% free tool stack.
Status: Pipeline A (Signal Router) is runnable end-to-end against Postgres; the competitor/alternative SEO generator is live on Cloudflare Pages.
Sygnal webhook Sygnal owns scoring.
│ POST + X-Sygnal-Secret We consume its verdict:
▼ score, icp_score, entities,
┌──────────────────────────────┐ competitors, suggested_action.
│ webhook_receiver (FastAPI) │ verify secret header
│ services/webhook_receiver │ idempotent insert into raw_events
│ │ ACK 202, process async
└───────────────┬──────────────┘
│ normalize → route (pipeline_core)
▼
┌──────────────────────────────┐
│ n8n orchestration workflow │ branch on priority, notify, respond
│ n8n/workflows/…router.json │
└───────────────┬──────────────┘
▼
Slack/Discord alert · Postgres signal state · event log
Design choices worth talking about in interviews:
- Consume the verdict, don't recompute it. Sygnal already scores intent against an ICP blueprint; this pipeline maps
score→priority = score × 10and routes (pipeline_core/verdict.py). Only the routing logic is ours. - Verify at the edge. Sygnal sends a static shared secret in
X-Sygnal-Secret(asha256=HMAC is also accepted); compared in constant time (pipeline_core/signature.py). - Idempotency.
raw_events.event_idis the PK; duplicate deliveries are acked, not reprocessed. - ACK fast, work async. Receiver returns
202and processes in a background task. - Event sourcing.
pipeline_eventsis an append-only audit log of every stage. - Graceful degradation. n8n forward retries with exponential backoff, then dead-letters.
- Enriched routing.
entities.competitorstriggers acompetitiveplay;suggested_actionis carried through to the alert.
# 1. Deps
make install # uv sync --extra dev
# 2. Config
cp .env.example .env # set SYGNAL_WEBHOOK_SECRET; leave DATABASE_URL blank to start
# 3. Run the receiver (in-memory store until Postgres is up)
make run # http://localhost:8080
# 4. Fire a signed sample signal at it
make send-test # -> 202 {"status":"accepted", ...}Add Postgres + n8n (both free, Docker):
make up # postgres:5433, n8n:http://localhost:5678
make import-workflow # loads the Pipeline A workflow into n8nThe container Postgres host port is
${POSTGRES_HOST_PORT}(default5432); set5433in.envif you already run Postgres locally.
Expose your machine so Sygnal can reach it (no paid hosting):
make tunnel # prints https://<random>.trycloudflare.comPoint a Sygnal webhook at https://<random>.trycloudflare.com/webhooks/sygnal, set the same secret in .env, and the flow runs end-to-end. Full setup: docs/SETUP.md. Design notes: docs/ARCHITECTURE.md. Portfolio write-up: docs/CASE_STUDY.md.
Turns structured competitor data into static, answer-first comparison pages — the BOFU keywords that convert for a challenger brand:
make seo # -> seo/site/index.html, alternatives/*.html, vs/*.html, sitemap.xml, llms.txt
make seo-serve # preview at http://localhost:8090
make seo-deploy # publish to Cloudflare PagesLive: https://sygnal-alternatives.pages.dev (Cloudflare Pages).
Content is generated from seo/data.json (never free-written), so every page carries unique facts. Pricing in that file is source-cited (vendor pages + Vendr procurement data, Oct 2026) — re-verify before republishing, since prices change.
pipeline_core/ shared domain logic (framework-free, unit-tested)
signature.py webhook shared-secret (or HMAC) verification
normalize.py Sygnal webhook envelope -> NormalizedSignal
verdict.py consume Sygnal's score (priority, competitive, ICP match)
routing.py bucket + owner + SLA + play
db.py Postgres store + in-memory fallback
services/webhook_receiver/ FastAPI edge service
n8n/workflows/ version-controlled workflow JSON
db/migrations/ append-only event store schema
seo/ competitor/alternative static-site generator
tests/ pytest for signature, normalize, verdict, routing, seo
fixtures/, scripts/ sample payload + signed test sender
| Pipeline | Scope | Status |
|---|---|---|
| A — Signal Router | webhook → verify → normalize → route → n8n notify | ✅ runnable |
| B — Enrichment + scoring | waterfall enrichment (free providers), real firmographic fit, dashboard | ⏭️ next |
| C — Personalization + feedback loop | LLM opener drafts, human review, reply outcomes re-weight scoring | planned |
| SEO — Competitor/alternative pages | programmatic pages from structured data, free static hosting | ✅ live (Cloudflare Pages) |
n8n (self-hosted) · Cloudflare Tunnel · Supabase/Postgres · HubSpot free CRM · Metabase (self-hosted) · GitHub Actions · Gemini/Groq free LLM tiers · Slack/Discord webhooks · Sygnal.