From a9e79c6f9144aaafe6457030249fbb1b2a00aa54 Mon Sep 17 00:00:00 2001 From: Jeff Emmett Date: Fri, 3 Apr 2026 13:11:27 -0700 Subject: [PATCH] Switch to Listmonk public subscription API for double opt-in MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The admin API creates subscribers but doesn't trigger opt-in emails. Use /api/public/subscription instead — no auth needed, triggers confirmation email automatically. Removes API token from env vars. Co-Authored-By: Claude Opus 4.6 --- frontend/docker-compose.yml | 4 +--- frontend/src/app/api/subscribe/route.ts | 20 ++++++-------------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/frontend/docker-compose.yml b/frontend/docker-compose.yml index 105747d..b3ec27a 100644 --- a/frontend/docker-compose.yml +++ b/frontend/docker-compose.yml @@ -23,9 +23,7 @@ services: - SMTP_PASS=rMailRelay2026!svc - SMTP_FROM=orders@katheryntrenshaw.com - LISTMONK_API_URL=https://newsletter.jeffemmett.com - - LISTMONK_API_USER=api-frontend - - LISTMONK_API_TOKEN=a875fb47eeb4b7fa973725eb8fca8f7b6590f917d910e26bf06e3e1c9386f710 - - LISTMONK_LIST_ID=3 + - LISTMONK_LIST_UUID=19f972ad-7286-49c2-952d-36f52718b58a labels: - "traefik.enable=true" # Staging diff --git a/frontend/src/app/api/subscribe/route.ts b/frontend/src/app/api/subscribe/route.ts index 793607a..6989dc9 100644 --- a/frontend/src/app/api/subscribe/route.ts +++ b/frontend/src/app/api/subscribe/route.ts @@ -2,9 +2,7 @@ import { NextRequest, NextResponse } from 'next/server'; import { sendSubscribeNotification } from '@/lib/email'; const LISTMONK_API_URL = process.env.LISTMONK_API_URL; -const LISTMONK_API_USER = process.env.LISTMONK_API_USER; -const LISTMONK_API_TOKEN = process.env.LISTMONK_API_TOKEN; -const LISTMONK_LIST_ID = parseInt(process.env.LISTMONK_LIST_ID || '3'); +const LISTMONK_LIST_UUID = process.env.LISTMONK_LIST_UUID; export async function POST(request: NextRequest) { try { @@ -15,20 +13,14 @@ export async function POST(request: NextRequest) { return NextResponse.json({ error: 'Email is required' }, { status: 400 }); } - // If Listmonk is configured, add subscriber there - if (LISTMONK_API_URL && LISTMONK_API_USER && LISTMONK_API_TOKEN) { - const res = await fetch(`${LISTMONK_API_URL}/api/subscribers`, { + // If Listmonk is configured, use public subscription API (triggers double opt-in) + if (LISTMONK_API_URL && LISTMONK_LIST_UUID) { + const res = await fetch(`${LISTMONK_API_URL}/api/public/subscription`, { method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `token ${LISTMONK_API_USER}:${LISTMONK_API_TOKEN}`, - }, + headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, - name: '', - lists: [LISTMONK_LIST_ID], - status: 'enabled', - preconfirm_subscriptions: false, + list_uuids: [LISTMONK_LIST_UUID], }), });