Email service (email-octopus)
Метадані
Section titled “Метадані”| Поле | Значення |
|---|---|
| Продукт | Abitly |
| Тип | worker / email-API |
| Статус | 🟢 prod (Lambda живий; lifecycle-ланцюжок кошика живий з боку бекенда — 179 CHECKOUT#-станів у DynamoDB і ростуть, нагадування шлються; звірено 18.07) |
| Власник | @Vladbandurin |
Призначення
Section titled “Призначення”Email-платформа Abitly: broadcast-кампанії as-code через власний SES-движок, welcome-серія новим реєстраціям, lifecycle-нагадування покинутого кошика (2 листи: +2 год / +12 год, персональні resume-лінки оплати), транзакційні листи, сегментація всієї бази з prod-БД. Сервіс stateless — власної Postgres-БД немає; стан (send-ledger, suppression, чекаути, оплати) у DynamoDB abitly-email-state, розклад у EventBridge Scheduler. Аналітика — PostHog (проєкт 221580): email_sent/delivered/opened/clicked, A/B тем.
Рішення 16–17.07.2026 (Б3, варіант c): SES-only + власна сегментація. EmailOctopus більше не використовується для розсилок і автоматизацій: welcome-автоматизацію EO зупинено 16.07 (замінена власною SES-серією), кампанії й suppression живуть у своєму движку, міграції лістів в EO не буде. EO-клієнт у коді лишається тільки як legacy-шлях upsert-у контактів.
Не плутати з Marketing (blueprint) — то запланований сервіс AI-персоналізованих кампаній (email — цикл 2027); email-service — жива інфраструктура сьогоднішніх листів. Загальна мапа каналу → Email-маркетинг.
Стан фаз
Section titled “Стан фаз”Фази 1–7 — з CLAUDE.md §10 репо сервісу (джерело правди); дельта 16–18.07 — робота циклу вступної кампанії 2026.
| Фаза | Що | Стан |
|---|---|---|
| 1–4 | Core · routes/services · templates+SES · deploy (Lambda + Terraform) | ✅ |
| 5 | Campaign automation: конвенція campaigns/<slug>/, движок на SES, інфра deliverability (DynamoDB, SNS-вебхуки, unsubscribe RFC 8058, S3+CloudFront) | ✅ |
| 6 | Lifecycle (кошик) + PostHog A/B | ✅ · каденс змінено на +2 год / +12 год (було +1/+24/+72) |
| 7 | Go-live + орг-інфра | ▶ у процесі — docs/implementation-plan.md у репо |
Дельта 16–18.07 (цикл кампаній 2026):
| Що | Стан |
|---|---|
| Scheduled-broadcast движок (prepare → human launch → worker у Lambda) | 🟢 prod із 17.07 |
| Welcome-серія на SES замість EO-автоматизації | 🟢 prod (PR #21 у dev; ≥100 активних розкладів у Scheduler) |
12 кампаній as-code у campaigns/ (webinar-промо, abandoned-checkout, nmt-done, welcome-2026 та ін.); відправлено 1 406 листів 16–17.07 | 🟢 |
Сегментація всієї бази: config/segments.json (10 взаємовиключних сегментів, драбина «перший збіг виграє»), npm run segments зі звіркою колізій між кампаніями | у PR #23 |
Deliverability-інструменти: Google Postmaster Tools верифіковано 17.07 (акаунт abitly.team@abitly.org), spam-rate гард у campaign:send (Postmaster v2 API, передпольотний), 10% holdout-механіка | код у PR #23; Postmaster-гард чекає OAuth-креденшели (кроки в docs/go-live-checklist.md) |
| Дослідження email-стратегії: 25 тверджень перевірено змагально, 10 підтверджено / 15 спростовано | docs/email-marketing-strategy-2026-07.md + CEO-бриф у репо |
⚠️ Ключове обмеження з дослідження: розсилка ≥5 000 листів/добу на Gmail назавжди класифікує домен як bulk sender (незворотно). Сегменти 14 612 «обрали спеціальності» і 6 089 «давні НМТ» перетинають поріг одним сендом — рішення про них приймається один раз.
Репозиторій та рантайм
Section titled “Репозиторій та рантайм”| Репо | https://github.com/abitly-org/email-octopus (внутрішня назва abitly-email-service) |
| Стек | Node 20 · TypeScript 5 · Fastify 5 · Zod · Vitest |
| Хостинг | AWS Lambda (eu-central-1) + API Gateway · DynamoDB · EventBridge Scheduler · SES configuration set |
| Спосіб деплою | ручний AWS_PROFILE=yangon-admin npm run lambda:deploy з робочої машини — CI-деплою немає |
| Публічні URL | https://email.api.abitly.org (GET /health) |
| Гілки | ⚠️ гілки main у репо немає (всупереч CLAUDE.md самого репо) — робоча і реліз-гілка = dev, деплой з неї. Основна незмерджена робота — PR #23 (feature/sending-automation-phase3 → dev) |
Залежності
Section titled “Залежності”- Залежить від: AWS SES (eu-central-1, production access, ліміт 50k листів/день), DynamoDB + EventBridge Scheduler, PostHog EU (221580), прод-Postgres read-only (сегментація, через SSM-тунель зі скриптів). Legacy: EmailOctopus Connect API (лише contact upsert).
- Від нього залежать: API —
EmailServiceClient(src/email-service/email-service.client.ts) викликає його на реєстрації (syncContact), старті оплати (checkoutStarted→ планує нагадування) і success-вебхуку Monobank (checkoutCompleted→ скасовує їх). Клієнт fire-and-forget: ніколи не кидає помилок і no-op, якщоEMAIL_SERVICE_URLне заданий — падіння email-сервісу не ламає оплату/реєстрацію. Ланцюжок у prod живий (179CHECKOUT#-станів, 18.07).
Env-змінні
Section titled “Env-змінні”Лише назви. Повний індекс: environments. Prod-параметри сервісу — SSM /abitly/email/* (eu-central-1).
| Змінна | Де | Призначення |
|---|---|---|
API_SECRET | email-service (SSM /abitly/email/API_SECRET) | автентифікація вхідних викликів |
DYNAMODB_TABLE / SES_CONFIGURATION_SET / SCHEDULER_* | email-service | broadcast-движок + lifecycle |
LIFECYCLE_REMINDER_OFFSETS | email-service (SSM) | каденс нагадувань; у prod = 2 кроки (+2 год/+12 год) |
POSTHOG_API_KEY | email-service | аналітика подій, проєкт 221580 |
EMAILOCTOPUS_* | email-service | legacy contact upsert |
EMAIL_SERVICE_URL / EMAIL_SERVICE_API_KEY | api-v2 | база сервісу + ключ (= API_SECRET); задані — ланцюжок живий |
GOOGLE_OAUTH_CLIENT_ID/SECRET + POSTMASTER_REFRESH_TOKEN | локальні скрипти (не Lambda) | spam-rate гард із Postmaster; ще не видані |
Ключові ендпоінти / структура
Section titled “Ключові ендпоінти / структура”GET /health # liveness (публічний)POST /api/contacts # реєстрація → welcome-серія (SES) + legacy EO-upsertPUT /api/contacts/:email # оновлення полів/тегівDELETE /api/contacts/:email # видалення (GDPR / self-delete)POST /api/lifecycle/checkout-started # планує нагадування +2h/+12h (email, checkoutId, resumeUrl, amount)POST /api/lifecycle/checkout-completed# скасовує нагадування після оплати (markPaid)POST /api/campaigns/:slug/prepare|launch|send # broadcast-движок (prepare → human launch → worker) /api/transactional, /analytics, /unsubscribe, /webhooks/sescampaigns/ # 12 кампаній as-code: body.hbs + body.txt.hbs + campaign.json + copy.mdconfig/segments.json # драбина сегментації всієї бази (10 сегментів, редагується руками)Движок кампаній: per-step копірайт у campaign.json variables.steps[], send-ledger (SEND#), suppression (SUPPRESS#), markPaid-guard (PAID#, оплаченим не шле), чесна ціна в промо (promoForAmount). Гарди відправки в campaign:send: SES HEALTHY → квота → hard-bounce (PostHog) → spam rate (Postmaster, передпольотний). Скаффолдінг нової кампанії — скіл write-campaign у репо сервісу.
Деплой та відкат
Section titled “Деплой та відкат”Merge у dev не деплоїть — після мержа запустити AWS_PROFILE=yangon-admin npm run lambda:deploy вручну; smoke: GET /health + dry-run нагадувань (docs/deploy-checklist.md, docs/go-live-checklist.md у репо). ⚠️ campaigns/ пакується всередину бандла — нова кампанія дає 404 до деплою. Відкат — повторний deploy попереднього коміту.
Логи та моніторинг
Section titled “Логи та моніторинг”CloudWatch (Lambda execution logs, eu-central-1) + PostHog 221580 (події email_*, дашборди кампаній) + Google Postmaster Tools (spam rate Gmail; дані з’являться на більших обсягах). Правило команди: кожна відправка — спершу на тестову пошту, потім dryRun, потім live.
Типові проблеми
Section titled “Типові проблеми”- «Нагадування не працюють» → перевір
CHECKOUT#у DynamoDB і розклади в Scheduler-групіabitly-campaigns; ланцюжок у prod живий з 16–17.07. Історичний контекст (до wiring) → Email-маркетинг. - Ламані лінки у plain-text листах → історичний баг рендера
.txt.hbs(HTML-escape); виправлено у PR #17 (noEscape+ декод ентіті). - PostHog-аналітика → з 17.07 події йдуть у орг-проєкт 221580 (дашборди й bounce-гард працюють по ньому). Якщо подій не видно — перевір
/abitly/email/POSTHOG_API_KEY. - Нова кампанія 404 на prod →
campaigns/їде всередині Lambda-бандла; треба deploy. - 503/504 при великому чанку → API Gateway ріже на 30с, а Lambda дошле; ledger — джерело правди, не відповідь скрипта; чанк ≤60.
TODO:у якому акаунті живе EmailOctopus (логін/біллінг) — зафіксувати (актуально, поки живий legacy contact upsert).