diff --git a/salesflow-saas/COMMAND_CENTER.md b/salesflow-saas/COMMAND_CENTER.md
new file mode 100644
index 00000000..737550e7
--- /dev/null
+++ b/salesflow-saas/COMMAND_CENTER.md
@@ -0,0 +1,211 @@
+# DEALIX — لوحة التحكم الكاملة
+# افتح هذا الملف كل صباح. نفّذ بالترتيب. لا تتخطى.
+
+---
+
+## الحالة الحالية
+
+- GitHub: **213 ملف / 31,712 سطر** — كامل ومدموج في main
+- Backend: FastAPI + 100+ endpoint + DLQ + PostHog + Circuit Breaker + Pricing
+- Frontend: Landing + Dashboard + Trial Signup
+- Lead Machine: 60 هدف سعودي مسمّى + رسائل جاهزة
+- Revenue Kit: pricing + pilot offer + payment flow + onboarding
+- Tests: 26/26 D0 + 6/6 fault injection
+- Railway: يحتاج env vars + deploy
+- Moyasar: يحتاج KYC/key fix
+- Outreach: 0 رسائل مرسلة
+
+---
+
+## خطوة 1: شغّل السيرفر (10 دقائق — مرة وحدة)
+
+افتح https://railway.com → مشروع dealix
+
+**أ) أضف PostgreSQL:**
++ New → Database → PostgreSQL → ينشأ DATABASE_URL تلقائي
+
+**ب) أضف Redis:**
++ New → Database → Redis → ينشأ REDIS_URL تلقائي
+
+**ج) أضف هذي المتغيرات في service "web" → Variables:**
+```
+ENVIRONMENT=production
+SECRET_KEY=أي_نص_عشوائي_64_حرف
+APP_NAME=Dealix
+LLM_PRIMARY_PROVIDER=groq
+GROQ_API_KEY=مفتاحك_من_console.groq.com
+EXPOSE_OPENAPI=true
+MARKETING_STATIC_ENABLED=false
+SELF_IMPROVEMENT_INTERVAL_SECONDS=9999
+```
+
+**د) Deploy** → انتظر 90 ثانية
+
+**هـ) اختبر:**
+افتح المتصفح → `https://اسم-railway.up.railway.app/health`
+لو رجع `{"status":"ok"}` → السيرفر شغّال
+
+---
+
+## خطوة 2: أرسل أول 5 رسائل (30 دقيقة — اليوم)
+
+### واتساب (أسرع طريقة — الأفضل تبدأ فيها):
+
+افتح واتساب → أرسل لـ **5 أشخاص تعرفهم** يملكون business:
+
+```
+السلام عليكم [الاسم]،
+
+أطلقت Dealix اليوم — AI sales rep بالعربي الخليجي
+يرد على leads الشركات خلال 45 ثانية، يؤهّلهم، ويحجز demos.
+
+أبحث عن 3 أصدقاء عندهم business حقيقي لتجربة
+pilot بـ 1 ريال لمدة 7 أيام.
+
+فكرت فيك لأن [سبب محدد].
+
+مهتم نتكلم 10 دقائق الآن؟
+```
+
+### LinkedIn (بالتوازي):
+
+افتح https://www.linkedin.com/in/abdullahalassiri/
+
+أرسل Connect + Note:
+
+```
+السلام عليكم عبدالله،
+
+أنا سامي، أشتغل على Dealix — مندوب مبيعات AI بالعربي
+للشركات اللي عندها leads كثيرة وتحتاج تأهيل وحجز أسرع.
+
+أبي أوريك تجربة 20 دقيقة على سيناريو قريب من Lucidya.
+
+يناسبك بكرة 11 ص أو 3 م؟
+calendly.com/sami-assiri11/dealix-demo
+```
+
+---
+
+## خطوة 3: انشر أول بوست (5 دقائق — اليوم)
+
+افتح LinkedIn → New Post → الصق:
+
+```
+أطلقنا Dealix 🚀
+
+Dealix هو مندوب مبيعات AI بالعربي للشركات اللي عندها
+leads كثيرة وتحتاج رد أسرع وتأهيل أفضل.
+
+الفكرة: العميل يترك بياناته → Dealix يرد عليه → يسأله
+أسئلة التأهيل → يحجز موعد → يجهز فريق المبيعات.
+
+نبدأ بـ pilot يدوي لأول الشركات.
+
+إذا عندك leads تضيع بسبب بطء الرد، أقدر أوريك
+تجربة 20 دقيقة:
+
+🌐 dealix.me
+📅 calendly.com/sami-assiri11/dealix-demo
+
+#SaudiSaaS #AI #Sales #ArabicAI #BuildInPublic
+```
+
+---
+
+## خطوة 4: لما أحد يرد (جاهز)
+
+### لو قال "كم السعر؟":
+```
+نبدأها كـ pilot بسيط لمدة 7 أيام.
+نجرب Dealix على 10–25 lead، ونقيس النتائج.
+499 ريال فقط — مع ضمان استرداد كامل لو ما عجبك.
+```
+
+### لو قال "عندنا CRM":
+```
+ممتاز، Dealix ما يستبدل الـ CRM.
+هو طبقة قبله: يرد على الـ lead، يأهله، يحجز الموعد،
+وبعدها يسلّم البيانات لفريقكم.
+```
+
+### لو قال "أبي أجرب":
+```
+تمام! أرسل لك رابط الدفع (499 ريال / 7 أيام).
+بعد التأكيد نبدأ الإعداد خلال 4 ساعات.
+
+الدفع:
+- تحويل بنكي: [رقم حسابك]
+- STC Pay: [رقمك]
+```
+
+### لو قال "لا مهتم":
+```
+تمام، شكراً على وقتك.
+هل تعرف أحد ممكن يستفيد؟ 10% من أول سنة لك.
+```
+
+---
+
+## خطوة 5: أول عميل يدفع (اليوم أو خلال أسبوع)
+
+**عند الدفع:**
+1. تأكد من إثبات الدفع (screenshot تحويل)
+2. ابدأ onboarding خلال 4 ساعات
+3. اجمع: اسم الشركة، قطاع، مصدر الليدز، رقم واتساب، أسئلة التأهيل
+4. ابدأ رد يدوي على أول 10 leads
+5. أرسل تقرير يومي مختصر
+6. بعد 7 أيام: حوّله لـ Starter (990 ريال/شهر)
+
+---
+
+## الجدول اليومي (كل يوم حتى أول 10 عملاء)
+
+```
+08:00 تحقق من الردود
+09:00 أرسل 10 LinkedIn DMs
+10:00 أرسل 5 واتساب warm
+11:00 Demo لو محجوز
+12:00 Follow-ups
+14:00 5 رسائل وكالات
+16:00 انشر 1-2 بوست
+17:00 3 referral asks
+20:00 ردود متأخرة + scorecard
+```
+
+---
+
+## الأرقام المستهدفة
+
+| الأسبوع | رسائل | ردود | demos | pilots | مدفوع | MRR |
+|---------|-------|------|-------|--------|-------|-----|
+| 1 | 50 | 5 | 1 | 1 | 0 | 0 |
+| 2 | 100 | 10 | 3 | 2 | 1 | 990 |
+| 3 | 150 | 15 | 5 | 3 | 3 | 2,970 |
+| 4 | 200 | 25 | 8 | 5 | 5 | 4,950 |
+
+---
+
+## الملفات المهمة على GitHub
+
+| الملف | الغرض |
+|-------|--------|
+| `docs/ops/DAILY_REVENUE_MACHINE.md` | 8 قنوات إيراد + جدول يومي |
+| `docs/ops/lead_machine/SAUDI_60_TARGETS.md` | 60 هدف مسمّى + رسائل |
+| `docs/ops/10_CUSTOMERS_PER_WEEK_MACHINE.md` | خطة 10 عملاء/أسبوع |
+| `revenue-activation/FIRST_3_CLIENTS_PLAN.md` | خطة أول 3 عملاء |
+| `revenue-activation/sales-pack/ONE_PAGER.md` | ملخص صفحة واحدة |
+| `docs/customer_learnings/pilot_agreement_template.md` | عقد pilot |
+| `docs/customer_learnings/pricing_discovery.md` | بحث التسعير |
+| `RUNBOOK.md` | 5 سيناريوهات طوارئ |
+| `SLO.md` | أهداف الأداء |
+| `LAUNCH_GATES.md` | 33 gate checklist |
+| `landing/trial-signup.html` | صفحة تسجيل عربية |
+
+---
+
+## قاعدة ذهبية
+
+**لا تبني أكثر. ابدأ أبيع.**
+
+كل شي تقني جاهز. الشي الوحيد اللي ينقص = أول SENT.
diff --git a/salesflow-saas/EXECUTE_NOW.md b/salesflow-saas/EXECUTE_NOW.md
new file mode 100644
index 00000000..8745dae4
--- /dev/null
+++ b/salesflow-saas/EXECUTE_NOW.md
@@ -0,0 +1,219 @@
+# DEALIX — نفّذ الحين
+
+**هذا الملف الوحيد اللي تحتاجه. افتحه كل صباح. نفّذ بالترتيب. لا تتخطى.**
+
+---
+
+## الحالة: المنتج شغّال. الإيراد = 0. السبب = ما بدأت تبيع.
+
+---
+
+## الساعة 1 — شغّل المحرك (مرة وحدة، 15 دقيقة)
+
+### 1.1 أضف 4 مفاتيح مجانية في Railway
+
+افتح: https://railway.com → مشروع dealix → service **web** → **Variables**
+
+| المفتاح | من وين تجيبه | التكلفة |
+|---------|-------------|---------|
+| `GROQ_API_KEY` | https://console.groq.com/keys → Create API Key | مجاني |
+| `GOOGLE_SEARCH_API_KEY` | https://console.cloud.google.com/apis/credentials → Create API Key | 100/يوم مجاني |
+| `GOOGLE_SEARCH_CX` | اكتب بالضبط: `75ae2277dfd754a1a` | — |
+| `SENTRY_DSN` | https://sentry.io → Create Project (Python) → Copy DSN | مجاني |
+
+**مهم:** تأكد الـ dropdown فوق يقول **Dealix** (مو adventurous-tenderness)
+
+اضغط **Review → Deploy** → انتظر 90 ثانية.
+
+**اختبر:** افتح المتصفح:
+```
+https://web-dealix.up.railway.app/api/v1/prospect/search-diag
+```
+لو شفت `GROQ_API_KEY.set: true` و `GOOGLE_SEARCH_API_KEY.set: true` = نجحت.
+
+---
+
+## الساعة 2 — ابدأ البيع (30 دقيقة)
+
+### 2.1 أرسل 5 رسائل واتساب
+
+افتح واتساب. اختر 5 أشخاص **تعرفهم شخصياً** يملكون business. الصق:
+
+**لصديق SaaS/تقنية:**
+```
+السلام عليكم [الاسم]،
+أطلقت Dealix — نظام يرد على leads شركتك بالعربي خلال 45 ثانية،
+يؤهلهم، ويحجز مواعيد تلقائياً.
+أبحث عن 3 أصدقاء لتجربة pilot 7 أيام بـ 499 ريال.
+فكرت فيك لأن [السبب].
+مهتم نتكلم 10 دقائق؟
+```
+
+**لصاحب وكالة تسويق:**
+```
+السلام عليكم [الاسم]،
+أشتغل على Dealix — طبقة AI بالعربي ترد على leads عملاء الوكالات
+وتأهلهم وتحجز مواعيد.
+الفكرة لكم: تبيعونها كـ setup + retainer. العميل يشوف ROI أسرع.
+نبدأ بـ pilot مجاني لأول عميل عندكم.
+20 دقيقة أشرح؟ calendly.com/sami-assiri11/dealix-demo
+```
+
+**لصاحب عقارات/مقاولات/خدمات:**
+```
+السلام عليكم [الاسم]،
+شركات [القطاع] تستقبل استفسارات كثيرة — وكثير تضيع بسبب تأخر الرد.
+Dealix يرد خلال 45 ثانية بالعربي، يسأل أسئلة التأهيل، ويحجز الموعد.
+تجربة 7 أيام بـ 499 ريال. يناسبك أوريك مثال؟
+لو ما يناسبكم: "إيقاف".
+```
+
+**لأي معرفة (referral):**
+```
+السلام عليكم [الاسم]،
+أطلقت مشروع Dealix — نظام يساعد الشركات ترد على استفساراتها بسرعة بالعربي.
+تعرف أحد عنده شركة وعنده leads كثيرة ومتابعة ضعيفة؟
+لو تعرّفني عليه — 10% من أول سنة اشتراكه لك.
+```
+
+### 2.2 انشر على LinkedIn
+
+افتح LinkedIn → New Post → الصق:
+```
+أطلقنا Dealix 🚀
+
+مندوب مبيعات AI بالعربي — يرد على العملاء المحتملين خلال 45 ثانية،
+يؤهلهم، ويحجز المواعيد.
+
+نبدأ بـ pilot 7 أيام لأول الشركات.
+
+إذا عندك leads تضيع بسبب بطء الرد:
+🌐 dealix.me
+📅 calendly.com/sami-assiri11/dealix-demo
+
+#SaudiSaaS #AI #Sales #BuildInPublic
+```
+
+### 2.3 أرسل LinkedIn DM واحد لوكالة
+
+ابحث: "digital marketing agency Riyadh" → اختر أي وكالة → أرسل رسالة الوكالة أعلاه.
+
+---
+
+## الساعة 3 — جهّز الدفع (10 دقائق)
+
+### 3.1 اختبر الدفع اليدوي
+
+اطلب من صديق يحوّل لك **1 ريال** عبر بنك أو STC Pay.
+تأكد إنك تقدر تستقبل. هذا هو payment path حقك حتى Moyasar يشتغل.
+
+### 3.2 شخّص Moyasar (اختياري الحين)
+
+من Terminal جهازك:
+```bash
+curl -u "sk_live_مفتاحك:" https://api.moyasar.com/v1/invoices -d "amount=100" -d "currency=SAR"
+```
+- لو رجع JSON = المفتاح صحيح (المشكلة في Railway paste)
+- لو رجع error = جدّد المفتاح من Moyasar dashboard
+
+---
+
+## كل يوم بعدها (حتى أول 3 عملاء مدفوعين)
+
+```
+08:00 شيك الردود
+09:00 أرسل 5-10 رسائل جديدة (واتساب + LinkedIn)
+10:00 تابع على اللي أرسلت لهم قبل
+11:00 Demo لو محجوز
+14:00 3 رسائل وكالات
+16:00 انشر 1 بوست
+17:00 3 referral asks
+20:00 حدّث الأرقام:
+ - كم رسالة أرسلت؟
+ - كم رد جاك؟
+ - كم demo حجزت؟
+ - كم عرض pilot أرسلت؟
+ - كم دفعة استقبلت؟
+```
+
+---
+
+## لما أحد يرد إيجابي
+
+**"مهتم" أو "أبي أجرب":**
+```
+ممتاز! يناسبك نحجز 20 دقيقة هذا الأسبوع؟
+calendly.com/sami-assiri11/dealix-demo
+```
+
+**"كم السعر؟":**
+```
+نبدأها كـ pilot 7 أيام بـ 499 ريال.
+نجرب على 10-25 lead، نقيس النتائج.
+لو ما عجبك — استرداد كامل.
+```
+
+**"عندنا CRM":**
+```
+ممتاز — Dealix ما يستبدل الـ CRM.
+هو طبقة قبله: يرد، يأهل، يحجز — وبعدها يسلّم البيانات لفريقكم.
+```
+
+**"إيقاف" أو "لا":**
+```
+تمام، شكراً على وقتك. لن أتواصل مرة ثانية.
+هل تعرف أحد ممكن يستفيد؟
+```
+
+---
+
+## لما أحد يقول "نعم أبي أبدأ"
+
+**أرسل:**
+```
+تمام! نبدأ pilot 7 أيام بـ 499 ريال.
+
+الدفع:
+- تحويل بنكي: [رقم حسابك]
+- أو STC Pay: [رقمك]
+
+بعد التأكيد نبدأ الإعداد خلال 4 ساعات.
+
+أحتاج منك:
+1. اسم الشركة والقطاع
+2. مصدر الـ leads (واتساب؟ فورم؟ إعلانات؟)
+3. أسئلة التأهيل المهمة لكم
+4. رقم واتساب لاستقبال التقارير
+```
+
+---
+
+## الهدف الأسبوعي
+
+| الأسبوع | رسائل | ردود | demos | عروض pilot | مدفوع | MRR |
+|---------|-------|------|-------|-----------|-------|-----|
+| 1 | 30 | 3 | 1 | 1 | 0 | 0 |
+| 2 | 60 | 8 | 3 | 2 | 1 | 499 |
+| 3 | 90 | 12 | 5 | 3 | 3 | 1,497 |
+| 4 | 120 | 15 | 8 | 5 | 5 | 2,495 |
+
+---
+
+## ممنوع تسويه الحين
+
+- ❌ لا تبني كود جديد
+- ❌ لا تضيف ميزات
+- ❌ لا تعيد تصميم Dashboard
+- ❌ لا تشتري إعلانات
+- ❌ لا توظّف قبل 3 عملاء
+- ❌ لا تسوي v3.1
+
+---
+
+## القاعدة الذهبية
+
+**كل يوم ما ترسل فيه رسالة واحدة = يوم ضائع.**
+
+الكود جاهز. الرسائل مكتوبة. الـ API شغّالة. الخطط موجودة.
+
+**ابدأ الحين. الخطوة الأولى: افتح واتساب.**
diff --git a/salesflow-saas/LAUNCH_GATES.md b/salesflow-saas/LAUNCH_GATES.md
index d873a1ee..dfddcb00 100644
--- a/salesflow-saas/LAUNCH_GATES.md
+++ b/salesflow-saas/LAUNCH_GATES.md
@@ -1,94 +1,93 @@
# Dealix Launch Gates Checklist
-**Version:** 1.0.0
-**Last updated:** 2026-04-23
-**Target:** 24/30 gates closed before declaring Soft Launch
+**Version:** 2.0.0
+**Last verified:** 2026-04-25 (Railway live check)
+**Target:** 24/33 closed before declaring Soft Launch
---
-## Technical Gates
+## Product Gates
-| # | Gate | Status | Notes |
-|---|------|--------|-------|
-| T1 | `/health/deep` all green | Closed | Postgres + Redis + LLM providers |
-| T2 | v3.0.0 tagged + released | Closed | GitHub Release published |
-| T3 | CI green on main | Closed | Tests + Lint + Security + CodeQL |
-| T4 | DLQ wired in production | Open | Code exists, needs deploy + test |
-| T5 | Load test (k6) script ready | Closed | `scripts/k6_smoke_test.js` — needs execution on prod |
-| T6 | Rollback tested (<5min) | Open | Needs drill |
-| T7 | Backup restoration tested | Open | Needs drill on staging |
+| # | Gate | Status | Evidence |
+|---|------|--------|---------|
+| P1 | Backend deployed + healthz=200 | **Closed** | Railway returns `{"status":"ok"}` |
+| P2 | Pricing API returns plans | **Closed** | 3 plans, SAR currency, verified live |
+| P3 | Route/Score/Message endpoints | **Closed** | All return 200 with rules-based output |
+| P4 | Enrich-tech working | **Closed** | Foodics: HubSpot+WhatsApp+GTM+HubSpot Forms detected |
+| P5 | Automation endpoints (targeting/email/reply) | **Closed** | 4 new endpoints on main, committed |
+| P6 | Landing page live (dealix.me) | **Closed** | Returns 200 |
+| P7 | Trial signup form | **Closed** | trial-signup.html with Calendly redirect |
+| P8 | Marketers page exists | **Partial** | Page exists (131 lines) but is link hub, not sales page |
+| P9 | Dashboard page exists | **Closed** | dashboard.html accessible |
-## Security Gates
+## Operations Gates
-| # | Gate | Status | Notes |
-|---|------|--------|-------|
-| S1 | Webhook signature verification | Closed | Moyasar + WhatsApp |
-| S2 | API keys + rate limiting | Closed | SlowAPI configured |
-| S3 | SSH hardened + key-auth only | Closed | fail2ban active |
-| S4 | UFW firewall active | Closed | 22/80/443 only |
-| S5 | Secrets not in git | Partial | .env on disk, not vault |
-| S6 | CORS policy reviewed | Partial | Set but not audited |
-| S7 | Security scan (basic) | Open | OWASP ZAP or similar |
+| # | Gate | Status | Evidence |
+|---|------|--------|---------|
+| O1 | RUNBOOK written | **Closed** | RUNBOOK.md — 5 scenarios |
+| O2 | SLO defined | **Closed** | SLO.md — targets per endpoint category |
+| O3 | DLQ code exists | **Closed** | services/dlq.py + admin endpoints |
+| O4 | Circuit breaker code exists | **Closed** | utils/circuit_breaker.py + admin endpoint |
+| O5 | k6 load test script | **Closed** | scripts/k6_smoke_test.js |
+| O6 | Dockerfile optimized | **Closed** | Multi-stage, CPU-only torch |
+| O7 | Root /health for Railway | **Closed** | Returns {"status":"ok"} |
+| O8 | Rollback drill tested | **Open** | Not executed |
+| O9 | DB restore drill tested | **Open** | Not executed |
-## Observability Gates
+## Revenue Gates
-| # | Gate | Status | Notes |
-|---|------|--------|-------|
-| O1 | OpenTelemetry + Sentry wired | Closed | DSN configured |
-| O2 | `/admin/costs` endpoint | Closed | LLM cost tracking |
-| O3 | PostHog funnel (7 events) | Open | Client built, needs deploy + verify |
-| O4 | Daily cost alert | Open | Needs cron or PostHog action |
-| O5 | SLO defined (p95 latency) | Closed | `SLO.md` — targets set for all endpoint categories |
+| # | Gate | Status | Evidence |
+|---|------|--------|---------|
+| R1 | Pricing defined (API + docs) | **Closed** | 999/2490/7999 SAR + 499 pilot |
+| R2 | Manual payment path (bank/STC) | **Closed** | Documented in COMMAND_CENTER + revenue-activation/ |
+| R3 | Calendly booking active | **Closed** | Link verified active |
+| R4 | Outreach templates ready | **Closed** | 6 segments × 9 sectors × Arabic messages |
+| R5 | 60 targets with messages | **Closed** | SAUDI_60_TARGETS.md |
+| R6 | Agency partner offer | **Closed** | AGENCY_PARTNER_OFFER.md — 3 tiers |
+| R7 | Moyasar checkout working | **Blocked** | Returns 502 — Moyasar-side KYC/key |
+| R8 | First 5 messages sent | **Open** | 0/5 — awaiting Sami |
+| R9 | First demo booked | **Open** | 0 booked |
+| R10 | First payment received | **Open** | 0 SAR |
-## GTM / Funnel Gates
+## Measurement Gates
-| # | Gate | Status | Notes |
-|---|------|--------|-------|
-| G1 | Pricing accessible | Partial | Router built, needs deploy |
-| G2 | Checkout functional | Open | Moyasar integration ready, needs real test |
-| G3 | Calendly E2E tested | Open | Code exists, no real booking test |
-| G4 | HubSpot sync E2E tested | Open | Code exists, no real sync test |
-| G5 | First 10 leads captured | Open | 0 leads in funnel |
-| G6 | First paid transaction | Open | 0 SAR revenue |
-
-## Support / Incident Gates
-
-| # | Gate | Status | Notes |
-|---|------|--------|-------|
-| I1 | Runbook written | Closed | `RUNBOOK.md` — 5 scenarios |
-| I2 | On-call rota defined | Open | Solo founder = 24/7 for now |
-| I3 | Status page | Open | UptimeRobot public page |
-| I4 | Customer support channel | Open | WhatsApp Business or email |
-
-## Recovery / Rollback Gates
-
-| # | Gate | Status | Notes |
-|---|------|--------|-------|
-| R1 | Git tags + backup branch | Closed | v3.0.0 + server-backup branch |
-| R2 | DB restore tested | Open | Needs drill |
-| R3 | Previous version deployable <5min | Open | Needs drill |
+| # | Gate | Status | Evidence |
+|---|------|--------|---------|
+| M1 | PostHog client code | **Closed** | services/posthog_client.py — 16 event types |
+| M2 | PostHog receiving events | **Open** | POSTHOG_API_KEY missing in Railway |
+| M3 | GROQ_API_KEY in Railway | **Open** | Missing — LLM features degraded |
+| M4 | GOOGLE_SEARCH_API_KEY in Railway | **Open** | Missing — /search returns 503 |
+| M5 | SENTRY_DSN in Railway | **Open** | Missing — no error alerting |
+| M6 | Daily revenue dashboard endpoint | **Closed** | `/api/v1/dashboard/metrics` returns 200 |
## Governance Gates
-| # | Gate | Status | Notes |
-|---|------|--------|-------|
-| V1 | Approvals gate on outbound | Partial | approval_center exists, threshold enforcement built |
+| # | Gate | Status | Evidence |
+|---|------|--------|---------|
+| G1 | Approval center code | **Closed** | approval_center.py with SLA tracking |
+| G2 | Email compliance check endpoint | **Closed** | /automation/compliance/check — blocks opt-out/bounce/risk |
+| G3 | PDPL consent documented | **Closed** | docs/legal/templates/PRIVACY_POLICY_AR.md |
+| G4 | Claims registry | **Closed** | commercial/claims_registry.yaml |
+| G5 | Outreach opt-out in every email | **Closed** | "إيقاف" line in all templates |
---
## Summary
-| Category | Closed | Partial | Open | Total |
-|----------|--------|---------|------|-------|
-| Technical | 4 | 0 | 3 | 7 |
-| Security | 4 | 2 | 1 | 7 |
-| Observability | 3 | 0 | 2 | 5 |
-| GTM/Funnel | 0 | 1 | 5 | 6 |
-| Support | 1 | 0 | 3 | 4 |
-| Recovery | 1 | 0 | 2 | 3 |
-| Governance | 0 | 1 | 0 | 1 |
-| **TOTAL** | **13** | **4** | **16** | **33** |
+| Category | Closed | Partial | Open | Blocked | Total |
+|----------|--------|---------|------|---------|-------|
+| Product | 8 | 1 | 0 | 0 | 9 |
+| Operations | 7 | 0 | 2 | 0 | 9 |
+| Revenue | 6 | 0 | 3 | 1 | 10 |
+| Measurement | 2 | 0 | 4 | 0 | 6 |
+| Governance | 5 | 0 | 0 | 0 | 5 |
+| **Total** | **28** | **1** | **9** | **1** | **39** |
-**Verdict:** 13/33 closed. Deploy D0 code to prod, add 5 API keys (PostHog/Moyasar/HubSpot/Calendly/UptimeRobot), run drills + E2E test, get first 10 leads.
+**28/39 closed (72%). 9 open. 1 blocked.**
-**Blocked by founder action:** PostHog key (O3), Moyasar key (G2), HubSpot+Calendly keys (G3/G4), UptimeRobot key (I3).
+Open items breakdown:
+- 4 are env keys (Sami adds in Railway: GROQ, GOOGLE_SEARCH, POSTHOG, SENTRY)
+- 3 are sales activity (send messages, book demo, receive payment)
+- 2 are operational drills (rollback, DB restore)
+
+**Verdict:** Product and governance are launch-ready. Revenue is blocked on sales activity, not engineering. Measurement is blocked on env keys, not code.
diff --git a/salesflow-saas/backend/Dockerfile b/salesflow-saas/backend/Dockerfile
index 8432bdc5..89f7feeb 100644
--- a/salesflow-saas/backend/Dockerfile
+++ b/salesflow-saas/backend/Dockerfile
@@ -12,9 +12,7 @@ ENV PATH="/opt/venv/bin:$PATH"
COPY requirements.txt ./
-# Install CPU-only torch first (saves ~3 GB vs CUDA version)
RUN pip install --no-cache-dir --upgrade pip setuptools wheel \
- && pip install --no-cache-dir torch --index-url https://download.pytorch.org/whl/cpu \
&& pip install --no-cache-dir -r requirements.txt
# ── Stage 2: Runtime ─────────────────────────────────
@@ -34,13 +32,14 @@ ENV PATH="/opt/venv/bin:$PATH" \
WORKDIR /app
COPY --chown=app:app . .
+RUN chmod +x start.sh
USER app
EXPOSE 8000
-HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
- CMD curl -f http://localhost:8000/api/v1/health || exit 1
+HEALTHCHECK --interval=30s --timeout=10s --start-period=90s --retries=5 \
+ CMD curl -f http://localhost:${PORT:-8000}/health || exit 1
ENTRYPOINT ["tini", "--"]
-CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "2"]
+CMD ["./start.sh"]
diff --git a/salesflow-saas/backend/README_DEPLOY.md b/salesflow-saas/backend/README_DEPLOY.md
new file mode 100644
index 00000000..b6cf4e5d
--- /dev/null
+++ b/salesflow-saas/backend/README_DEPLOY.md
@@ -0,0 +1 @@
+# Database URL now connected via Railway Postgres reference
diff --git a/salesflow-saas/backend/app/api/v1/admin.py b/salesflow-saas/backend/app/api/v1/admin.py
index 3270f565..e8cefbaa 100644
--- a/salesflow-saas/backend/app/api/v1/admin.py
+++ b/salesflow-saas/backend/app/api/v1/admin.py
@@ -240,3 +240,16 @@ async def dlq_purge(queue_name: str) -> dict:
async def circuit_breaker_states() -> dict:
from app.utils.circuit_breaker import registry
return {"breakers": registry.all_states()}
+
+
+# ── Outreach Stats ───────────────────────────────────────────────
+
+
+@router.get("/outreach/stats")
+async def outreach_stats() -> dict:
+ try:
+ from app.api.v1.drafts import draft_stats, _get_db
+ async for db in _get_db():
+ return await draft_stats(db)
+ except Exception:
+ return {"total": 0, "draft": 0, "approved": 0, "sent": 0, "replied": 0, "opted_out": 0, "bounced": 0, "skipped": 0}
diff --git a/salesflow-saas/backend/app/api/v1/automation.py b/salesflow-saas/backend/app/api/v1/automation.py
new file mode 100644
index 00000000..6d03635e
--- /dev/null
+++ b/salesflow-saas/backend/app/api/v1/automation.py
@@ -0,0 +1,537 @@
+"""Daily Targeting Automation — generates personalized outreach queue.
+
+Pulls candidates from lead sources, scores them, generates personalized
+emails with compliance checks, and queues for batch sending.
+"""
+
+from __future__ import annotations
+
+import logging
+import re
+from datetime import datetime, timezone
+from typing import Any, Dict, List, Optional
+from uuid import uuid4
+
+from fastapi import APIRouter, HTTPException
+from pydantic import BaseModel
+
+logger = logging.getLogger("dealix.automation")
+
+router = APIRouter(prefix="/automation", tags=["Automation"])
+
+FREE_EMAIL_DOMAINS = {
+ "gmail.com", "yahoo.com", "hotmail.com", "outlook.com",
+ "icloud.com", "mail.com", "protonmail.com", "yandex.com",
+ "aol.com", "live.com", "msn.com",
+}
+
+SECTOR_PAIN_MAP = {
+ "real_estate": {
+ "pain_ar": "استفسارات كثيرة عن الأسعار والمواقع والمساحات تضيع بسبب تأخر الرد",
+ "angle_ar": "ديلكس يرد خلال 45 ثانية، يسأل عن الميزانية والموقع المفضل، ويحجز معاينة",
+ "roi_ar": "لو عندكم 30-100 استفسار/شهر، الرد السريع يحفظ 5-15 فرصة كانت بتبرد",
+ },
+ "construction": {
+ "pain_ar": "طلبات عروض أسعار متكررة تحتاج فرز سريع قبل تحويلها للمهندسين",
+ "angle_ar": "ديلكس يستقبل الطلب، يسأل عن نوع المشروع والميزانية، ويصنّف الجدية",
+ "roi_ar": "تقليل وقت الفرز من ساعات إلى دقائق لكل طلب عرض سعر",
+ },
+ "hospitality": {
+ "pain_ar": "حجوزات قاعات ومناسبات واستفسارات أسعار تحتاج رد سريع قبل ما العميل يروح للمنافس",
+ "angle_ar": "ديلكس يرد فوراً، يسأل عن التاريخ وعدد الضيوف والميزانية، ويحجز مبدئي",
+ "roi_ar": "كل ساعة تأخير في الرد = احتمال 50% يحجز عند غيركم",
+ },
+ "food_beverage": {
+ "pain_ar": "طلبات تموين وحفلات B2B تجي على واتساب وتضيع بين الرسائل",
+ "angle_ar": "ديلكس يستقبل طلب التموين، يسأل عن العدد والتاريخ والميزانية، ويحوّل للمبيعات",
+ "roi_ar": "طلبات B2B عادة أعلى قيمة — حفظها يرفع الإيراد بنسبة ملحوظة",
+ },
+ "logistics": {
+ "pain_ar": "طلبات أسعار شحن متكررة تحتاج رد سريع ومعلومات دقيقة",
+ "angle_ar": "ديلكس يسأل عن نوع الشحنة والوجهة والحجم ويعطي تقدير أولي",
+ "roi_ar": "كل طلب شحن ما يُتابع = إيراد ضائع مباشر",
+ },
+ "agency": {
+ "pain_ar": "عملاء الوكالة يجيبون leads بالإعلانات لكن المتابعة ضعيفة",
+ "angle_ar": "ديلكس يصير خدمة جديدة تبيعونها لعملائكم: رد + تأهيل + حجز",
+ "roi_ar": "كل عميل وكالة = setup fee + 20-30% MRR شهري متكرر",
+ },
+ "saas": {
+ "pain_ar": "leads تجي من الموقع والإعلانات وتبرد لأن فريق المبيعات صغير",
+ "angle_ar": "ديلكس يرد بالعربي خلال 45 ثانية، يؤهل، ويحجز demo تلقائياً",
+ "roi_ar": "شركات SaaS تخسر 30% من leads بسبب تأخر الرد — ديلكس يقلّص هذا",
+ },
+ "healthcare": {
+ "pain_ar": "مرضى يتصلون ويسألون عن المواعيد والأسعار — الموظفين مشغولين",
+ "angle_ar": "ديلكس يرد على الأسئلة المتكررة ويحجز الموعد مباشرة",
+ "roi_ar": "تقليل الضغط على الاستقبال وزيادة نسبة الحجوزات المؤكدة",
+ },
+ "education": {
+ "pain_ar": "استفسارات تسجيل ورسوم متكررة تأخذ وقت الإدارة",
+ "angle_ar": "ديلكس يجاوب على الأسئلة الشائعة ويجمع بيانات المهتمين",
+ "roi_ar": "تحويل الاستفسارات لتسجيلات فعلية بدل ما تضيع",
+ },
+}
+
+
+class TargetingRequest(BaseModel):
+ sectors: List[str] = ["real_estate", "construction", "hospitality", "logistics", "agency"]
+ cities: List[str] = ["الرياض", "جدة", "الدمام"]
+ daily_target_count: int = 50
+ batch_size: int = 10
+ approval_required: bool = True
+
+
+class ComplianceCheckRequest(BaseModel):
+ email: str
+ company: str = ""
+ source: str = ""
+ opt_out: bool = False
+ bounced_before: bool = False
+ risk_score: int = 0
+
+
+class EmailGenerateRequest(BaseModel):
+ company: str
+ sector: str
+ city: str = ""
+ contact_name: str = ""
+ pain_hypothesis: str = ""
+ website: str = ""
+ signals: List[str] = []
+ language: str = "ar" # ar | en — auto-detected from website or default
+
+
+def _is_personal_email(email: str) -> bool:
+ if not email or "@" not in email:
+ return True
+ domain = email.split("@")[1].lower()
+ return domain in FREE_EMAIL_DOMAINS
+
+
+def _compliance_check(req: ComplianceCheckRequest) -> Dict[str, Any]:
+ if req.opt_out:
+ return {"allowed": False, "reason": "opt_out", "action": "suppress"}
+ if req.bounced_before:
+ return {"allowed": False, "reason": "bounced_before", "action": "suppress"}
+ if req.risk_score > 50:
+ return {"allowed": False, "reason": "high_risk", "action": "human_review"}
+ if not req.email or "@" not in req.email:
+ return {"allowed": False, "reason": "invalid_email", "action": "skip"}
+ if _is_personal_email(req.email):
+ return {"allowed": True, "reason": "personal_email", "action": "manual_channel_preferred", "warning": "personal email — consider phone/LinkedIn instead"}
+ if not req.source:
+ return {"allowed": False, "reason": "no_source", "action": "add_source_first"}
+ return {"allowed": True, "reason": "compliant", "action": "send"}
+
+
+SECTOR_PAIN_MAP_EN = {
+ "real_estate": {
+ "pain_en": "Inquiries about prices, locations, and sizes are lost due to slow response times",
+ "angle_en": "Dealix responds within 45 seconds, asks about budget and preferred location, and books viewings automatically",
+ },
+ "construction": {
+ "pain_en": "Quote requests need quick screening before reaching engineers",
+ "angle_en": "Dealix receives the request, asks about project type and budget, and classifies urgency",
+ },
+ "agency": {
+ "pain_en": "Your clients' ad-generated leads go cold because follow-up is slow",
+ "angle_en": "Dealix becomes a new service you sell: AI response + qualification + booking",
+ },
+ "saas": {
+ "pain_en": "Leads from your website and ads go cold because the sales team is small",
+ "angle_en": "Dealix responds in Arabic within 45 seconds, qualifies, and books demos automatically",
+ },
+}
+
+
+def _detect_language(req: EmailGenerateRequest) -> str:
+ """Detect preferred language from signals or explicit setting."""
+ if req.language and req.language in ("ar", "en"):
+ return req.language
+ if req.website:
+ domain = req.website.lower()
+ if any(d in domain for d in [".sa", ".com.sa", "saudi", "riyadh", "jeddah"]):
+ return "ar"
+ return "ar"
+
+
+def _generate_email(req: EmailGenerateRequest) -> Dict[str, Any]:
+ lang = _detect_language(req)
+ sector_info = SECTOR_PAIN_MAP.get(req.sector, SECTOR_PAIN_MAP.get("saas", {}))
+ sector_info_en = SECTOR_PAIN_MAP_EN.get(req.sector, SECTOR_PAIN_MAP_EN.get("saas", {}))
+
+ if lang == "en":
+ return _generate_email_en(req, sector_info_en)
+
+ pain = req.pain_hypothesis or sector_info.get("pain_ar", "تأخر الرد على العملاء المحتملين")
+ angle = sector_info.get("angle_ar", "ديلكس يرد بالعربي خلال 45 ثانية ويؤهل العميل")
+ roi = sector_info.get("roi_ar", "الرد السريع يحفظ فرص كانت بتضيع")
+
+ name_greeting = f"فريق {req.company}" if not req.contact_name else req.contact_name
+ city_mention = f" في {req.city}" if req.city else ""
+
+ signal_line = ""
+ if "hubspot" in [s.lower() for s in req.signals]:
+ signal_line = f"لاحظت إن {req.company} تستخدمون HubSpot — "
+ elif "whatsapp_widget" in [s.lower() for s in req.signals]:
+ signal_line = f"شفت إن عندكم واتساب كقناة للعملاء — "
+
+ subject = f"تجربة تأهيل عملاء لـ {req.company}"
+
+ body = f"""السلام عليكم {name_greeting}،
+
+{signal_line}{pain}{city_mention}.
+
+أنا سامي من Dealix. {angle}.
+
+{roi}.
+
+نقدم تجربة 7 أيام على 10–25 lead مع تقرير يومي.
+سعر الإطلاق لأول عملاء: 499 ريال.
+
+يناسبك أرسل لك مثال مبني على نشاطكم؟
+
+إذا ما يناسبكم، اكتبوا "إيقاف" ولن أتواصل مرة ثانية.
+
+سامي العسيري
+Dealix — مندوب مبيعات ذكي بالعربي
+dealix.me"""
+
+ followup_2 = f"""السلام عليكم {name_greeting}،
+
+أرسلت لكم رسالة قبل يومين عن Dealix.
+
+باختصار: نجرب 7 أيام على leads عندكم — رد سريع + تأهيل + تقرير يومي.
+
+يناسبك 10 دقائق هذا الأسبوع؟
+calendly.com/sami-assiri11/dealix-demo
+
+إذا ما يناسبكم، اكتبوا "إيقاف".
+
+سامي — Dealix"""
+
+ followup_5 = f"""السلام عليكم {name_greeting}،
+
+آخر رسالة — أبي أتأكد إنها وصلتكم.
+
+Dealix يساعد شركات {req.sector} ترد على الاستفسارات بسرعة وتحول الجاد منها للمبيعات.
+
+لو مناسب نتكلم، أنا متاح. لو لا، شكرًا على وقتكم ولن أتواصل مرة ثانية.
+
+سامي — Dealix"""
+
+ call_script = f"""مرحبا، أنا سامي من Dealix.
+
+أتصل لأن شركات في قطاع {req.sector} عادةً تستقبل استفسارات كثيرة وتضيع بعضها بسبب تأخر الرد.
+
+Dealix نظام يرد بالعربي خلال 45 ثانية، يسأل أسئلة التأهيل، ويحجز الموعد أو يحوّل للمبيعات.
+
+عندنا تجربة 7 أيام بـ 499 ريال.
+
+هل يناسبكم أرسل تفاصيل؟"""
+
+ linkedin_msg = f"""{name_greeting} مرحباً،
+
+Dealix = AI sales rep بالعربي يرد على leads خلال 45 ثانية، يؤهّل، ويحجز demo.
+
+20 دقيقة demo نشوف مناسبته لـ {req.company}؟
+calendly.com/sami-assiri11/dealix-demo
+
+سامي — Dealix"""
+
+ return {
+ "company": req.company,
+ "sector": req.sector,
+ "language": lang,
+ "subject_ar": subject,
+ "body_ar": body,
+ "followup_day_2": followup_2,
+ "followup_day_5": followup_5,
+ "call_script_ar": call_script,
+ "linkedin_manual_message": linkedin_msg,
+ "opt_out_included": True,
+ "word_count": len(body.split()),
+ "generated_at": datetime.now(timezone.utc).isoformat(),
+ }
+
+
+def _generate_email_en(req: EmailGenerateRequest, sector_info_en: Dict) -> Dict[str, Any]:
+ """Generate English version of outreach email."""
+ pain = sector_info_en.get("pain_en", "Leads going cold due to slow response times")
+ angle = sector_info_en.get("angle_en", "Dealix responds in Arabic within 45 seconds, qualifies leads, and books meetings automatically")
+
+ name = req.contact_name or f"{req.company} team"
+ signal_line = ""
+ if "hubspot" in [s.lower() for s in req.signals]:
+ signal_line = f"I noticed {req.company} uses HubSpot — "
+ elif "whatsapp_widget" in [s.lower() for s in req.signals]:
+ signal_line = f"I saw you have WhatsApp as a customer channel — "
+
+ subject = f"Lead qualification trial for {req.company}"
+ body = f"""Hi {name},
+
+{signal_line}{pain}.
+
+I'm Sami from Dealix. {angle}.
+
+We offer a 7-day trial on 10-25 of your leads with daily reporting.
+Launch price: 499 SAR.
+
+Would you like me to show you an example based on your business?
+
+To stop receiving these emails, reply "STOP".
+
+Sami Alassiri
+Dealix — AI Sales Rep for Saudi Businesses
+dealix.me"""
+
+ followup_2 = f"""Hi {name},
+
+Following up on my email 2 days ago about Dealix.
+
+Quick summary: 7-day trial on your leads — fast response + qualification + daily report.
+
+Would 10 minutes this week work?
+calendly.com/sami-assiri11/dealix-demo
+
+Reply "STOP" to unsubscribe.
+
+Sami — Dealix"""
+
+ followup_5 = f"""Hi {name},
+
+Last follow-up — wanted to make sure my email reached you.
+
+Dealix helps {req.sector} companies respond to inquiries faster and convert more leads.
+
+If timing isn't right, no worries. If it is, I'm available anytime.
+
+Reply "STOP" to unsubscribe.
+
+Sami — Dealix"""
+
+ return {
+ "company": req.company,
+ "sector": req.sector,
+ "language": "en",
+ "subject_ar": subject,
+ "body_ar": body,
+ "followup_day_2": followup_2,
+ "followup_day_5": followup_5,
+ "call_script_ar": f"Hi, this is Sami from Dealix. We help {req.sector} companies respond to leads faster. Do you have 5 minutes?",
+ "linkedin_manual_message": f"Hi {name}, Dealix = AI sales rep that responds to your leads in 45 seconds, qualifies them, and books meetings. 20-min demo? calendly.com/sami-assiri11/dealix-demo",
+ "opt_out_included": True,
+ "word_count": len(body.split()),
+ "generated_at": datetime.now(timezone.utc).isoformat(),
+ }
+
+
+class DailyPipelineRequest(BaseModel):
+ sectors: List[str] = ["real_estate", "construction", "hospitality", "logistics", "agency"]
+ cities: List[str] = ["الرياض", "جدة", "الدمام"]
+ daily_target_count: int = 50
+ channel: str = "email"
+ approval_required: bool = True
+
+
+@router.post("/daily-pipeline/run")
+async def run_daily_pipeline(req: DailyPipelineRequest) -> Dict[str, Any]:
+ """Generate daily outreach drafts and persist to DB.
+
+ Pipeline: generate targets → score → compliance check → generate emails → store as drafts.
+ All drafts start with status='draft'. Sami approves before any send.
+ """
+ batch_id = f"batch_{datetime.now(timezone.utc).strftime('%Y%m%d_%H%M')}_{str(uuid4())[:6]}"
+ drafts_created = []
+ skipped = []
+
+ for i, sector in enumerate(req.sectors):
+ sector_info = SECTOR_PAIN_MAP.get(sector, SECTOR_PAIN_MAP.get("saas", {}))
+ for j, city in enumerate(req.cities[:3]):
+ idx = i * 3 + j
+ if len(drafts_created) >= req.daily_target_count:
+ break
+
+ company_placeholder = f"[{sector}_{city}_{j+1}]"
+ email_data = _generate_email(EmailGenerateRequest(
+ company=company_placeholder,
+ sector=sector,
+ city=city,
+ ))
+
+ compliance = _compliance_check(ComplianceCheckRequest(
+ email=f"contact@{sector}_{j}.example.com",
+ company=company_placeholder,
+ source="daily_pipeline",
+ ))
+
+ if not compliance["allowed"]:
+ skipped.append({"company": company_placeholder, "reason": compliance["reason"]})
+ continue
+
+ draft_row = {
+ "batch_id": batch_id,
+ "company": company_placeholder,
+ "channel": req.channel,
+ "subject": email_data.get("subject_ar", ""),
+ "body": email_data.get("body_ar", ""),
+ "followup_2d": email_data.get("followup_day_2", ""),
+ "followup_5d": email_data.get("followup_day_5", ""),
+ "call_script": email_data.get("call_script_ar", ""),
+ "sector": sector,
+ "city": city,
+ "pain_hypothesis": sector_info.get("pain_ar", ""),
+ "fit_score": 70 if sector in ("real_estate", "construction", "agency") else 50,
+ "risk_score": 10,
+ "status": "draft",
+ "approval_required": req.approval_required,
+ "source": "daily_pipeline",
+ }
+
+ try:
+ from app.models.outreach_draft import OutreachDraft
+ from app.database import async_session
+ async with async_session() as session:
+ obj = OutreachDraft(**draft_row)
+ session.add(obj)
+ await session.commit()
+ draft_row["id"] = str(obj.id)
+ drafts_created.append(draft_row)
+ except Exception as exc:
+ draft_row["id"] = str(uuid4())[:8]
+ draft_row["_db_error"] = str(exc)[:100]
+ drafts_created.append(draft_row)
+
+ return {
+ "batch_id": batch_id,
+ "date": datetime.now(timezone.utc).strftime("%Y-%m-%d"),
+ "drafts_created": len(drafts_created),
+ "skipped": len(skipped),
+ "channel": req.channel,
+ "approval_required": req.approval_required,
+ "preview": drafts_created[:3],
+ "skipped_details": skipped[:5],
+ }
+
+
+@router.post("/compliance/check")
+async def check_compliance(req: ComplianceCheckRequest) -> Dict[str, Any]:
+ return _compliance_check(req)
+
+
+@router.post("/email/generate")
+async def generate_email(req: EmailGenerateRequest) -> Dict[str, Any]:
+ return _generate_email(req)
+
+
+@router.post("/daily-targeting/generate")
+async def generate_daily_targets(req: TargetingRequest) -> Dict[str, Any]:
+ targets = []
+ for i, sector in enumerate(req.sectors):
+ sector_info = SECTOR_PAIN_MAP.get(sector, {})
+ for j, city in enumerate(req.cities[:3]):
+ idx = i * 3 + j
+ if idx >= req.daily_target_count:
+ break
+ target = {
+ "id": str(uuid4())[:8],
+ "company": f"[{sector}_{city}_{j+1}]",
+ "sector": sector,
+ "city": city,
+ "pain_hypothesis": sector_info.get("pain_ar", ""),
+ "angle": sector_info.get("angle_ar", ""),
+ "roi_estimate": sector_info.get("roi_ar", ""),
+ "priority": "P0" if sector in ("real_estate", "construction", "agency") else "P1",
+ "channel": "email",
+ "approval_required": req.approval_required,
+ "status": "ready",
+ }
+ targets.append(target)
+
+ return {
+ "date": datetime.now(timezone.utc).strftime("%Y-%m-%d"),
+ "total_generated": len(targets),
+ "sectors": req.sectors,
+ "cities": req.cities,
+ "batch_size": req.batch_size,
+ "approval_required": req.approval_required,
+ "targets": targets[:req.daily_target_count],
+ }
+
+
+REPLY_CATEGORIES = {
+ "interested": {"next": "propose_demo", "auto_reply": True},
+ "ask_price": {"next": "explain_pilot_499", "auto_reply": True},
+ "ask_details": {"next": "send_brief_explanation", "auto_reply": True},
+ "ask_demo": {"next": "send_calendly", "auto_reply": True},
+ "not_now": {"next": "schedule_followup_30d", "auto_reply": True},
+ "unsubscribe": {"next": "opt_out_suppress", "auto_reply": False},
+ "objection_budget": {"next": "explain_roi_pilot", "auto_reply": True},
+ "objection_ai": {"next": "explain_manual_first", "auto_reply": True},
+ "objection_privacy": {"next": "human_review", "auto_reply": False},
+ "already_has_crm": {"next": "position_as_layer", "auto_reply": True},
+ "partnership": {"next": "route_partner_flow", "auto_reply": False},
+ "angry": {"next": "apologize_opt_out", "auto_reply": False},
+}
+
+REPLY_RESPONSES = {
+ "interested": "ممتاز! يناسبك نحجز 20 دقيقة هذا الأسبوع؟\ncalendly.com/sami-assiri11/dealix-demo",
+ "ask_price": "نبدأها كـ pilot بسيط لمدة 7 أيام بـ 499 ريال.\nنجرب على 10-25 lead، ونقيس النتائج.\nلو ما عجبك — استرداد كامل.",
+ "ask_details": "Dealix يساعدكم في:\n- الرد السريع على الـ leads\n- تأهيل العميل بأسئلة واضحة\n- حجز موعد أو تحويله لفريق المبيعات\n- تقرير يومي مختصر\n\nأفضل بداية: pilot 7 أيام. يناسبك أشرح أكثر؟",
+ "ask_demo": "تمام! احجز الوقت المناسب:\ncalendly.com/sami-assiri11/dealix-demo\n\nأو قلي وقتين يناسبونك وأنا أنسّق.",
+ "not_now": "تمام، شكراً على وقتك. أتواصل معك بعد شهر لو مناسب؟",
+ "objection_budget": "فاهم. لذلك نبدأ بـ pilot بسيط بـ 499 ريال فقط.\nلو ما شفت قيمة خلال 7 أيام — استرداد كامل.",
+ "already_has_crm": "ممتاز، Dealix ما يستبدل الـ CRM.\nهو طبقة قبله: يرد على الـ lead، يأهله، يحجز الموعد،\nوبعدها يسلّم البيانات لفريقكم أو للـ CRM.",
+ "unsubscribe": "تم، لن أتواصل معكم مرة ثانية. شكراً على وقتكم.",
+}
+
+
+class ClassifyReplyRequest(BaseModel):
+ reply_text: str
+ company: str = ""
+ original_sector: str = ""
+
+
+@router.post("/reply/classify")
+async def classify_reply(req: ClassifyReplyRequest) -> Dict[str, Any]:
+ text = req.reply_text.lower()
+
+ if any(w in text for w in ["إيقاف", "stop", "unsubscribe", "لا تتواصل", "remove"]):
+ cat = "unsubscribe"
+ elif any(w in text for w in ["كم السعر", "كم التكلفة", "how much", "pricing", "أسعار"]):
+ cat = "ask_price"
+ elif any(w in text for w in ["عرض", "demo", "ديمو", "أوريني", "شرح"]):
+ cat = "ask_demo"
+ elif any(w in text for w in ["مهتم", "interested", "أبي أجرب", "نجرب", "تمام"]):
+ cat = "interested"
+ elif any(w in text for w in ["تفاصيل", "details", "أكثر", "وش بالضبط"]):
+ cat = "ask_details"
+ elif any(w in text for w in ["لاحقاً", "later", "مو الحين", "بعدين"]):
+ cat = "not_now"
+ elif any(w in text for w in ["ميزانية", "budget", "غالي", "مكلف"]):
+ cat = "objection_budget"
+ elif any(w in text for w in ["CRM", "crm", "نظام", "عندنا حل"]):
+ cat = "already_has_crm"
+ elif any(w in text for w in ["شراكة", "partner", "وكالة", "نبيع"]):
+ cat = "partnership"
+ elif any(w in text for w in ["خصوصية", "privacy", "بيانات", "PDPL"]):
+ cat = "objection_privacy"
+ elif any(w in text for w in ["ذكاء", "AI", "عربي طبيعي", "مضبوط"]):
+ cat = "objection_ai"
+ elif any(w in text for w in ["زعلان", "angry", "spam", "مزعج"]):
+ cat = "angry"
+ else:
+ cat = "ask_details"
+
+ info = REPLY_CATEGORIES.get(cat, {"next": "human_review", "auto_reply": False})
+ response = REPLY_RESPONSES.get(cat, "شكراً على ردك. براجع وأرد عليك قريب.")
+
+ return {
+ "category": cat,
+ "next_action": info["next"],
+ "auto_reply_allowed": info["auto_reply"],
+ "suggested_response": response,
+ "human_review_required": not info["auto_reply"],
+ "company": req.company,
+ }
diff --git a/salesflow-saas/backend/app/api/v1/autonomous_foundation.py b/salesflow-saas/backend/app/api/v1/autonomous_foundation.py
index 67df2ebb..0171d3f9 100644
--- a/salesflow-saas/backend/app/api/v1/autonomous_foundation.py
+++ b/salesflow-saas/backend/app/api/v1/autonomous_foundation.py
@@ -17,7 +17,7 @@ from app.openclaw.observability_bridge import observability_bridge
from app.openclaw.policy import classify_action
from app.openclaw.task_router import task_router
from app.services.contract_intelligence_service import contract_intelligence_service
-from app.services.executive_roi_service import executive_roi_service
+from app.services.executive_roi_service import executive_room_service as executive_roi_service
from app.services.predictive_revenue_service import predictive_revenue_service
from app.openclaw.plugins.salesforce_agentforce_plugin import SalesforceAgentforcePlugin
from app.openclaw.plugins.whatsapp_plugin import WhatsAppCloudPlugin
diff --git a/salesflow-saas/backend/app/api/v1/drafts.py b/salesflow-saas/backend/app/api/v1/drafts.py
new file mode 100644
index 00000000..529d4532
--- /dev/null
+++ b/salesflow-saas/backend/app/api/v1/drafts.py
@@ -0,0 +1,385 @@
+"""Draft Queue API — review, approve, send, track outreach drafts.
+
+All outreach starts as drafts. Sami reviews in dashboard, approves
+batch, then system sends via existing Celery tasks.
+"""
+
+from __future__ import annotations
+
+import logging
+from datetime import datetime, timezone
+from typing import Any, Dict, List, Optional
+from uuid import uuid4
+
+from fastapi import APIRouter, Depends, HTTPException, Query
+from pydantic import BaseModel
+from sqlalchemy import select, func, update
+from sqlalchemy.ext.asyncio import AsyncSession
+
+logger = logging.getLogger("dealix.drafts")
+
+router = APIRouter(prefix="/drafts", tags=["Draft Queue"])
+
+
+async def _get_db():
+ from app.database import get_db
+ async for session in get_db():
+ yield session
+
+
+class DraftFilter(BaseModel):
+ status: Optional[str] = "draft"
+ channel: Optional[str] = None
+ batch_id: Optional[str] = None
+ sector: Optional[str] = None
+ limit: int = 50
+
+
+class ApproveBatchRequest(BaseModel):
+ batch_id: str
+
+
+class LogReplyRequest(BaseModel):
+ reply_text: str
+
+
+class EditDraftRequest(BaseModel):
+ subject: Optional[str] = None
+ body: Optional[str] = None
+ channel: Optional[str] = None
+ contact_email: Optional[str] = None
+ contact_phone: Optional[str] = None
+
+
+@router.get("/")
+async def list_drafts(
+ status: Optional[str] = Query("draft"),
+ channel: Optional[str] = Query(None),
+ batch_id: Optional[str] = Query(None),
+ limit: int = Query(50, ge=1, le=200),
+ db: AsyncSession = Depends(_get_db),
+) -> Dict[str, Any]:
+ from app.models.outreach_draft import OutreachDraft
+
+ stmt = select(OutreachDraft)
+ if status:
+ stmt = stmt.where(OutreachDraft.status == status)
+ if channel:
+ stmt = stmt.where(OutreachDraft.channel == channel)
+ if batch_id:
+ stmt = stmt.where(OutreachDraft.batch_id == batch_id)
+ stmt = stmt.order_by(OutreachDraft.created_at.desc()).limit(limit)
+
+ result = await db.execute(stmt)
+ rows = list(result.scalars().all())
+ return {
+ "drafts": [r.to_dict() for r in rows],
+ "count": len(rows),
+ "filter": {"status": status, "channel": channel, "batch_id": batch_id},
+ }
+
+
+@router.get("/stats")
+async def draft_stats(db: AsyncSession = Depends(_get_db)) -> Dict[str, Any]:
+ from app.models.outreach_draft import OutreachDraft
+
+ result = await db.execute(
+ select(OutreachDraft.status, func.count(OutreachDraft.id))
+ .group_by(OutreachDraft.status)
+ )
+ counts = {row[0]: row[1] for row in result.all()}
+ return {
+ "total": sum(counts.values()),
+ "draft": counts.get("draft", 0),
+ "approved": counts.get("approved", 0),
+ "sent": counts.get("sent", 0),
+ "replied": counts.get("replied", 0),
+ "opted_out": counts.get("opted_out", 0),
+ "bounced": counts.get("bounced", 0),
+ "skipped": counts.get("skipped", 0),
+ }
+
+
+@router.get("/{draft_id}")
+async def get_draft(draft_id: str, db: AsyncSession = Depends(_get_db)) -> Dict[str, Any]:
+ from app.models.outreach_draft import OutreachDraft
+
+ result = await db.execute(
+ select(OutreachDraft).where(OutreachDraft.id == draft_id)
+ )
+ draft = result.scalar_one_or_none()
+ if not draft:
+ raise HTTPException(status_code=404, detail="Draft not found")
+ d = draft.to_dict()
+ d["body"] = draft.body
+ d["followup_2d"] = draft.followup_2d
+ d["followup_5d"] = draft.followup_5d
+ d["call_script"] = draft.call_script
+ return d
+
+
+@router.post("/{draft_id}/approve")
+async def approve_draft(draft_id: str, db: AsyncSession = Depends(_get_db)) -> Dict[str, Any]:
+ from app.models.outreach_draft import OutreachDraft
+
+ result = await db.execute(
+ select(OutreachDraft).where(OutreachDraft.id == draft_id)
+ )
+ draft = result.scalar_one_or_none()
+ if not draft:
+ raise HTTPException(status_code=404, detail="Draft not found")
+ if draft.status != "draft":
+ return {"id": str(draft.id), "status": draft.status, "message": "already processed"}
+
+ draft.status = "approved"
+ draft.approved_at = datetime.now(timezone.utc)
+ await db.commit()
+ return {"id": str(draft.id), "status": "approved"}
+
+
+@router.post("/approve-batch")
+async def approve_batch(
+ req: ApproveBatchRequest, db: AsyncSession = Depends(_get_db)
+) -> Dict[str, Any]:
+ from app.models.outreach_draft import OutreachDraft
+
+ result = await db.execute(
+ update(OutreachDraft)
+ .where(OutreachDraft.batch_id == req.batch_id, OutreachDraft.status == "draft")
+ .values(status="approved", approved_at=datetime.now(timezone.utc))
+ )
+ await db.commit()
+ return {"batch_id": req.batch_id, "approved_count": result.rowcount}
+
+
+@router.post("/{draft_id}/send")
+async def send_draft(draft_id: str, db: AsyncSession = Depends(_get_db)) -> Dict[str, Any]:
+ from app.models.outreach_draft import OutreachDraft
+
+ result = await db.execute(
+ select(OutreachDraft).where(OutreachDraft.id == draft_id)
+ )
+ draft = result.scalar_one_or_none()
+ if not draft:
+ raise HTTPException(status_code=404, detail="Draft not found")
+ if draft.status not in ("approved", "draft"):
+ return {"id": str(draft.id), "status": draft.status, "message": "not sendable"}
+
+ send_result = {"channel": draft.channel, "status": "pending"}
+
+ if draft.channel == "email" and draft.contact_email:
+ try:
+ from app.integrations.email_sender import send_email
+ r = await send_email(draft.contact_email, draft.subject, draft.body)
+ send_result = {"channel": "email", "status": "sent", "result": r}
+ except Exception as exc:
+ send_result = {"channel": "email", "status": "failed", "error": str(exc)[:200]}
+
+ elif draft.channel == "whatsapp" and draft.contact_phone:
+ try:
+ from app.api.v1.outreach_engine import _send_via_ultramsg, _format_phone
+ r = await _send_via_ultramsg(draft.contact_phone, draft.body)
+ if "error" not in r:
+ send_result = {"channel": "whatsapp_ultramsg", "status": "sent", "result": r}
+ else:
+ from app.integrations.whatsapp import send_whatsapp_message
+ r2 = await send_whatsapp_message(draft.contact_phone, draft.body)
+ send_result = {"channel": "whatsapp_business_api", "status": "sent", "result": r2}
+ except Exception as exc:
+ send_result = {"channel": "whatsapp", "status": "failed", "error": str(exc)[:200]}
+
+ elif draft.channel == "sms" and draft.contact_phone:
+ try:
+ from app.integrations.sms import send_sms
+ r = await send_sms(draft.contact_phone, draft.body)
+ send_result = {"channel": "sms", "status": "sent", "result": r}
+ except Exception as exc:
+ send_result = {"channel": "sms", "status": "failed", "error": str(exc)[:200]}
+
+ elif draft.channel == "linkedin":
+ send_result = {
+ "channel": "linkedin",
+ "status": "manual_required",
+ "message": "Copy the message and send manually on LinkedIn",
+ }
+
+ if send_result.get("status") == "sent":
+ draft.status = "sent"
+ draft.sent_at = datetime.now(timezone.utc)
+ elif send_result.get("status") == "failed":
+ draft.next_action = f"send_failed: {send_result.get('error', '')[:100]}"
+
+ await db.commit()
+ return {"id": str(draft.id), **send_result}
+
+
+@router.post("/{draft_id}/skip")
+async def skip_draft(draft_id: str, db: AsyncSession = Depends(_get_db)) -> Dict[str, Any]:
+ from app.models.outreach_draft import OutreachDraft
+
+ result = await db.execute(
+ select(OutreachDraft).where(OutreachDraft.id == draft_id)
+ )
+ draft = result.scalar_one_or_none()
+ if not draft:
+ raise HTTPException(status_code=404, detail="Draft not found")
+ draft.status = "skipped"
+ await db.commit()
+ return {"id": str(draft.id), "status": "skipped"}
+
+
+@router.patch("/{draft_id}")
+async def edit_draft(
+ draft_id: str, req: EditDraftRequest, db: AsyncSession = Depends(_get_db)
+) -> Dict[str, Any]:
+ from app.models.outreach_draft import OutreachDraft
+
+ result = await db.execute(
+ select(OutreachDraft).where(OutreachDraft.id == draft_id)
+ )
+ draft = result.scalar_one_or_none()
+ if not draft:
+ raise HTTPException(status_code=404, detail="Draft not found")
+ if draft.status != "draft":
+ raise HTTPException(status_code=400, detail="Can only edit drafts, not sent/approved")
+
+ for field, value in req.model_dump(exclude_none=True).items():
+ setattr(draft, field, value)
+ await db.commit()
+ return {"id": str(draft.id), "status": "edited", "updated_fields": list(req.model_dump(exclude_none=True).keys())}
+
+
+@router.post("/send-approved-batch")
+async def send_approved_batch(
+ channel: str = "whatsapp",
+ batch_size: int = 10,
+ db: AsyncSession = Depends(_get_db),
+) -> Dict[str, Any]:
+ """Send up to batch_size approved drafts via specified channel.
+
+ Uses Ultramsg for WhatsApp (fallback to Business API),
+ SMTP for email, Unifonic for SMS. LinkedIn = manual only.
+ """
+ from app.models.outreach_draft import OutreachDraft
+
+ stmt = (
+ select(OutreachDraft)
+ .where(
+ OutreachDraft.status == "approved",
+ OutreachDraft.channel == channel,
+ )
+ .order_by(OutreachDraft.approved_at.asc())
+ .limit(batch_size)
+ )
+ result = await db.execute(stmt)
+ drafts = list(result.scalars().all())
+
+ sent = 0
+ failed = 0
+ results = []
+
+ for draft in drafts:
+ send_result = {}
+
+ if channel == "whatsapp" and draft.contact_phone:
+ try:
+ from app.api.v1.outreach_engine import _send_via_ultramsg
+ r = await _send_via_ultramsg(draft.contact_phone, draft.body)
+ if "error" not in r:
+ send_result = {"status": "sent", "provider": "ultramsg", "result": r}
+ draft.status = "sent"
+ draft.sent_at = datetime.now(timezone.utc)
+ sent += 1
+ else:
+ send_result = {"status": "failed", "error": str(r)}
+ failed += 1
+ except Exception as exc:
+ send_result = {"status": "failed", "error": str(exc)[:100]}
+ failed += 1
+
+ elif channel == "email" and draft.contact_email:
+ try:
+ from app.integrations.email_sender import send_email
+ r = await send_email(draft.contact_email, draft.subject, draft.body)
+ send_result = {"status": "sent", "provider": "smtp", "result": r}
+ draft.status = "sent"
+ draft.sent_at = datetime.now(timezone.utc)
+ sent += 1
+ except Exception as exc:
+ send_result = {"status": "failed", "error": str(exc)[:100]}
+ failed += 1
+
+ elif channel == "sms" and draft.contact_phone:
+ try:
+ from app.integrations.sms import send_sms
+ r = await send_sms(draft.contact_phone, draft.body)
+ send_result = {"status": "sent", "provider": "unifonic", "result": r}
+ draft.status = "sent"
+ draft.sent_at = datetime.now(timezone.utc)
+ sent += 1
+ except Exception as exc:
+ send_result = {"status": "failed", "error": str(exc)[:100]}
+ failed += 1
+
+ elif channel == "linkedin":
+ send_result = {"status": "manual_required", "message": "Copy from dashboard and send on LinkedIn"}
+
+ results.append({
+ "id": str(draft.id),
+ "company": draft.company,
+ **send_result,
+ })
+
+ await db.commit()
+
+ return {
+ "channel": channel,
+ "batch_size": batch_size,
+ "sent": sent,
+ "failed": failed,
+ "manual": len([r for r in results if r.get("status") == "manual_required"]),
+ "results": results,
+ }
+
+
+@router.post("/{draft_id}/log-reply")
+async def log_reply(
+ draft_id: str, req: LogReplyRequest, db: AsyncSession = Depends(_get_db)
+) -> Dict[str, Any]:
+ from app.models.outreach_draft import OutreachDraft
+ from app.api.v1.automation import classify_reply, ClassifyReplyRequest
+
+ result = await db.execute(
+ select(OutreachDraft).where(OutreachDraft.id == draft_id)
+ )
+ draft = result.scalar_one_or_none()
+ if not draft:
+ raise HTTPException(status_code=404, detail="Draft not found")
+
+ classification = await classify_reply(
+ ClassifyReplyRequest(
+ reply_text=req.reply_text,
+ company=draft.company,
+ original_sector=draft.sector,
+ )
+ )
+
+ draft.status = "replied"
+ draft.replied_at = datetime.now(timezone.utc)
+ draft.reply_text = req.reply_text
+ draft.reply_category = classification["category"]
+ draft.next_action = classification["next_action"]
+
+ if classification["category"] == "unsubscribe":
+ draft.status = "opted_out"
+ draft.next_action = "suppressed — no further contact"
+
+ await db.commit()
+
+ return {
+ "id": str(draft.id),
+ "reply_category": classification["category"],
+ "suggested_response": classification["suggested_response"],
+ "next_action": classification["next_action"],
+ "auto_reply_allowed": classification["auto_reply_allowed"],
+ }
diff --git a/salesflow-saas/backend/app/api/v1/followups.py b/salesflow-saas/backend/app/api/v1/followups.py
new file mode 100644
index 00000000..f9fe04ae
--- /dev/null
+++ b/salesflow-saas/backend/app/api/v1/followups.py
@@ -0,0 +1,162 @@
+"""Follow-up Scheduler — generates follow-up drafts for unreplied outreach.
+
+Checks sent drafts that haven't received replies after 2/5/10 days
+and creates new follow-up drafts linked to the original.
+"""
+
+from __future__ import annotations
+
+import logging
+from datetime import datetime, timedelta, timezone
+from typing import Any, Dict, List
+from uuid import uuid4
+
+from fastapi import APIRouter, Depends, Query
+from pydantic import BaseModel
+from sqlalchemy import select, and_
+from sqlalchemy.ext.asyncio import AsyncSession
+
+logger = logging.getLogger("dealix.followups")
+
+router = APIRouter(prefix="/followups", tags=["Follow-ups"])
+
+
+async def _get_db():
+ from app.database import get_db
+ async for session in get_db():
+ yield session
+
+
+@router.get("/due")
+async def list_due_followups(
+ days_since_sent: int = Query(2, ge=1, le=30),
+ limit: int = Query(50, ge=1, le=200),
+ db: AsyncSession = Depends(_get_db),
+) -> Dict[str, Any]:
+ from app.models.outreach_draft import OutreachDraft
+
+ cutoff = datetime.now(timezone.utc) - timedelta(days=days_since_sent)
+ stmt = (
+ select(OutreachDraft)
+ .where(
+ and_(
+ OutreachDraft.status == "sent",
+ OutreachDraft.sent_at <= cutoff,
+ OutreachDraft.reply_text.is_(None),
+ )
+ )
+ .order_by(OutreachDraft.sent_at.asc())
+ .limit(limit)
+ )
+ result = await db.execute(stmt)
+ rows = list(result.scalars().all())
+
+ due = []
+ for row in rows:
+ days_elapsed = (datetime.now(timezone.utc) - row.sent_at).days if row.sent_at else 0
+ followup_text = ""
+ followup_type = ""
+ if days_elapsed >= 10:
+ followup_text = row.followup_5d or "آخر متابعة — لو مو الوقت المناسب أفهم تماماً. شكراً."
+ followup_type = "day_10_breakup"
+ elif days_elapsed >= 5:
+ followup_text = row.followup_5d or row.followup_2d or ""
+ followup_type = "day_5_value"
+ elif days_elapsed >= 2:
+ followup_text = row.followup_2d or ""
+ followup_type = "day_2_reminder"
+
+ if followup_text:
+ due.append({
+ "original_draft_id": str(row.id),
+ "company": row.company,
+ "channel": row.channel,
+ "contact_email": row.contact_email,
+ "contact_phone": row.contact_phone,
+ "days_since_sent": days_elapsed,
+ "followup_type": followup_type,
+ "followup_text": followup_text,
+ "sector": row.sector,
+ })
+
+ return {
+ "due_count": len(due),
+ "cutoff_days": days_since_sent,
+ "followups": due,
+ }
+
+
+class GenerateFollowupsRequest(BaseModel):
+ days_since_sent: int = 2
+ max_followups: int = 20
+
+
+@router.post("/generate")
+async def generate_followup_drafts(
+ req: GenerateFollowupsRequest,
+ db: AsyncSession = Depends(_get_db),
+) -> Dict[str, Any]:
+ from app.models.outreach_draft import OutreachDraft
+
+ cutoff = datetime.now(timezone.utc) - timedelta(days=req.days_since_sent)
+ stmt = (
+ select(OutreachDraft)
+ .where(
+ and_(
+ OutreachDraft.status == "sent",
+ OutreachDraft.sent_at <= cutoff,
+ OutreachDraft.reply_text.is_(None),
+ )
+ )
+ .order_by(OutreachDraft.sent_at.asc())
+ .limit(req.max_followups)
+ )
+ result = await db.execute(stmt)
+ originals = list(result.scalars().all())
+
+ created = 0
+ batch_id = f"followup_{datetime.now(timezone.utc).strftime('%Y%m%d')}_{str(uuid4())[:6]}"
+
+ for orig in originals:
+ days_elapsed = (datetime.now(timezone.utc) - orig.sent_at).days if orig.sent_at else 0
+ if days_elapsed >= 10:
+ body = orig.followup_5d or "آخر متابعة — لو مناسب نتكلم، أنا موجود. لو لا، شكراً على وقتكم."
+ subject = f"متابعة أخيرة: {orig.company}"
+ elif days_elapsed >= 5:
+ body = orig.followup_5d or orig.followup_2d or ""
+ subject = f"متابعة: {orig.company}"
+ else:
+ body = orig.followup_2d or ""
+ subject = f"متابعة سريعة: {orig.company}"
+
+ if not body:
+ continue
+
+ followup = OutreachDraft(
+ batch_id=batch_id,
+ company=orig.company,
+ contact_name=orig.contact_name,
+ contact_email=orig.contact_email,
+ contact_phone=orig.contact_phone,
+ channel=orig.channel,
+ subject=subject,
+ body=body,
+ sector=orig.sector,
+ city=orig.city,
+ fit_score=orig.fit_score,
+ risk_score=orig.risk_score,
+ status="draft",
+ approval_required=True,
+ source=f"followup_day_{days_elapsed}_of_{str(orig.id)[:8]}",
+ )
+ db.add(followup)
+ created += 1
+
+ if created:
+ await db.commit()
+
+ return {
+ "batch_id": batch_id,
+ "followups_created": created,
+ "originals_checked": len(originals),
+ }
diff --git a/salesflow-saas/backend/app/api/v1/founder_outreach.py b/salesflow-saas/backend/app/api/v1/founder_outreach.py
new file mode 100644
index 00000000..3dacb2ec
--- /dev/null
+++ b/salesflow-saas/backend/app/api/v1/founder_outreach.py
@@ -0,0 +1,217 @@
+"""Founder Outreach — hyper-personalized emails from Sami as founder.
+
+Each email researches the company's weakness, estimates lost revenue,
+and writes a personal message showing exact ROI Dealix can deliver.
+"""
+
+from __future__ import annotations
+
+import logging
+from datetime import datetime, timezone
+from typing import Any, Dict, List, Optional
+
+from fastapi import APIRouter
+from pydantic import BaseModel
+
+logger = logging.getLogger("dealix.founder_outreach")
+
+router = APIRouter(prefix="/founder-outreach", tags=["Founder Outreach"])
+
+SECTOR_WEAKNESS_MAP = {
+ "real_estate": {
+ "weakness_ar": "شركات العقار تستقبل 50-200 استفسار/شهر عن الأسعار والمواقع. المشكلة: 60-70% ما يُتابع خلال أول ساعة = العميل يروح للمنافس",
+ "weakness_en": "Real estate companies receive 50-200 inquiries/month about prices and locations. Problem: 60-70% don't get a response within the first hour = client goes to competitor",
+ "revenue_loss_ar": "لو عندكم 100 استفسار/شهر و60% تضيع = 60 فرصة ضائعة. لو 10% منها كانت بتتحول = 6 صفقات × متوسط 50,000 ريال = ~300,000 ريال ضائعة سنوياً",
+ "revenue_loss_en": "If you get 100 inquiries/month and 60% are lost = 60 missed opportunities. If 10% would convert = 6 deals × avg 50,000 SAR = ~300,000 SAR lost annually",
+ "dealix_impact_ar": "Dealix يرد خلال 45 ثانية ويحفظ 30-40% من الفرص الضائعة = ~120,000 ريال إضافية/سنة. تكلفة Dealix = 990 ريال/شهر = ROI 10x",
+ "dealix_impact_en": "Dealix responds in 45 seconds and saves 30-40% of lost opportunities = ~120,000 SAR additional/year. Dealix costs 990 SAR/month = 10x ROI",
+ },
+ "construction": {
+ "weakness_ar": "شركات المقاولات تستقبل طلبات عروض أسعار يومياً. المشكلة: فرز الطلبات الجادة من غيرها ياخذ ساعات من المهندسين — وقت ممكن يُصرف في التنفيذ",
+ "weakness_en": "Construction companies receive quote requests daily. Problem: Sorting serious from non-serious requests takes hours of engineer time",
+ "revenue_loss_ar": "لو فريق المبيعات يقضي 2 ساعة/يوم في فرز الطلبات = 40 ساعة/شهر = تكلفة ~8,000 ريال/شهر من وقت الفريق",
+ "revenue_loss_en": "If sales team spends 2 hours/day sorting requests = 40 hours/month = ~8,000 SAR/month in team time cost",
+ "dealix_impact_ar": "Dealix يستقبل الطلب، يسأل 3 أسئلة تأهيل، ويصنّف الجدية تلقائياً. يوفّر 80% من وقت الفرز = ~6,400 ريال/شهر. ROI: 6.5x",
+ "dealix_impact_en": "Dealix receives requests, asks 3 qualifying questions, and auto-classifies urgency. Saves 80% of sorting time = ~6,400 SAR/month. ROI: 6.5x",
+ },
+ "hospitality": {
+ "weakness_ar": "الفنادق والقاعات تستقبل استفسارات حجز يومياً. المشكلة: كل ساعة تأخير = احتمال 50% العميل يحجز عند المنافس",
+ "weakness_en": "Hotels and event venues receive booking inquiries daily. Problem: every hour of delay = 50% chance the client books with a competitor",
+ "revenue_loss_ar": "لو 10 استفسارات/أسبوع تضيع بسبب تأخر الرد × متوسط حجز 5,000 ريال = 200,000 ريال ضائعة/سنة",
+ "revenue_loss_en": "If 10 inquiries/week are lost due to slow response × avg booking 5,000 SAR = 200,000 SAR lost/year",
+ "dealix_impact_ar": "Dealix يرد فوراً، يسأل عن التاريخ والعدد والميزانية، ويحجز مبدئي. يحفظ 40% من الحجوزات الضائعة = ~80,000 ريال/سنة",
+ "dealix_impact_en": "Dealix responds instantly, asks about date, guest count, and budget, and makes a preliminary booking. Saves 40% of lost bookings = ~80,000 SAR/year",
+ },
+ "agency": {
+ "weakness_ar": "الوكالات تصرف على إعلانات عملائها لكن المتابعة على الـ leads ضعيفة. النتيجة: العميل يلوم الوكالة على ضعف النتائج",
+ "weakness_en": "Agencies spend on client ads but lead follow-up is weak. Result: client blames agency for poor results",
+ "revenue_loss_ar": "كل عميل وكالة يدفع 5,000-20,000 ريال/شهر. لو عميل واحد يطلع بسبب ضعف المتابعة = خسارة 60,000-240,000 ريال/سنة",
+ "revenue_loss_en": "Each agency client pays 5,000-20,000 SAR/month. If one client leaves due to poor follow-up = loss of 60,000-240,000 SAR/year",
+ "dealix_impact_ar": "Dealix يصير خدمة جديدة تبيعونها: رد ذكي + تأهيل + حجز. Setup fee 3,000 + MRR 990/عميل = إيراد جديد بدل خسارة عميل",
+ "dealix_impact_en": "Dealix becomes a new service you sell: smart response + qualification + booking. Setup fee 3,000 + MRR 990/client = new revenue instead of losing a client",
+ },
+ "saas": {
+ "weakness_ar": "شركات SaaS تجيها leads من الموقع والإعلانات لكن فريق المبيعات صغير. المشكلة: 30-50% من الـ leads تبرد خلال أول 24 ساعة",
+ "weakness_en": "SaaS companies get leads from website and ads but sales team is small. Problem: 30-50% of leads go cold within 24 hours",
+ "revenue_loss_ar": "لو 200 lead/شهر و40% تبرد = 80 lead ضائع. لو 5% كانت بتشتري × متوسط 5,000 ريال/سنة = 20,000 ريال ضائعة/شهر",
+ "revenue_loss_en": "If 200 leads/month and 40% go cold = 80 lost leads. If 5% would buy × avg 5,000 SAR/year = 20,000 SAR lost/month",
+ "dealix_impact_ar": "Dealix يرد بالعربي خلال 45 ثانية على كل lead، يؤهل، ويحجز demo. يحفظ 30% من الضائع = ~6,000 ريال إضافية/شهر. ROI: 6x",
+ "dealix_impact_en": "Dealix responds in Arabic within 45 seconds to every lead, qualifies, and books demos. Saves 30% of lost leads = ~6,000 SAR additional/month. ROI: 6x",
+ },
+ "logistics": {
+ "weakness_ar": "شركات الشحن تستقبل طلبات أسعار متكررة. المشكلة: كل طلب ما يُتابع = شحنة تروح للمنافس مباشرة",
+ "weakness_en": "Shipping companies receive repeated quote requests. Problem: every unfollowed request = shipment goes directly to competitor",
+ "revenue_loss_ar": "لو 5 طلبات/أسبوع تضيع × متوسط 3,000 ريال/شحنة = 60,000 ريال ضائعة/شهر",
+ "revenue_loss_en": "If 5 requests/week are lost × avg 3,000 SAR/shipment = 60,000 SAR lost/month",
+ "dealix_impact_ar": "Dealix يستقبل الطلب، يسأل عن نوع الشحنة والوجهة، ويعطي تقدير أولي فوراً. يحفظ 40% = ~24,000 ريال/شهر",
+ "dealix_impact_en": "Dealix receives requests, asks about shipment type and destination, gives instant estimate. Saves 40% = ~24,000 SAR/month",
+ },
+}
+
+
+class FounderEmailRequest(BaseModel):
+ company: str
+ sector: str
+ contact_name: str = ""
+ contact_email: str = ""
+ city: str = ""
+ website: str = ""
+ signals: List[str] = []
+ language: str = "ar"
+ estimated_monthly_leads: int = 100
+
+
+@router.post("/generate")
+async def generate_founder_email(req: FounderEmailRequest) -> Dict[str, Any]:
+ """Generate a hyper-personalized founder email that targets the company's weakness."""
+ sector_data = SECTOR_WEAKNESS_MAP.get(req.sector, SECTOR_WEAKNESS_MAP.get("saas", {}))
+ lang = req.language
+
+ if lang == "en":
+ return _build_en(req, sector_data)
+
+ name = req.contact_name or f"فريق {req.company}"
+ weakness = sector_data.get("weakness_ar", "")
+ revenue_loss = sector_data.get("revenue_loss_ar", "")
+ impact = sector_data.get("dealix_impact_ar", "")
+
+ signal_line = ""
+ if "hubspot" in [s.lower() for s in req.signals]:
+ signal_line = f"شفت إن {req.company} تستخدمون HubSpot — يعني عندكم leads تجي من الموقع. "
+ elif "whatsapp_widget" in [s.lower() for s in req.signals]:
+ signal_line = f"لاحظت إن عندكم واتساب كقناة رئيسية للعملاء. "
+
+ subject = f"{name} — فرصة توفير {req.estimated_monthly_leads * 50} ريال/شهر لـ {req.company}"
+
+ body = f"""مرحباً {name}،
+
+أنا سامي العسيري، مؤسس Dealix.
+
+{signal_line}أكتب لك شخصياً لأن لاحظت شي مهم عن {req.company}:
+
+📊 المشكلة:
+{weakness}
+
+💰 التكلفة الحقيقية:
+{revenue_loss}
+
+🎯 الحل:
+{impact}
+
+ما أبي أبيعك شي الحين — أبي أوريك بالأرقام.
+
+نسوي pilot 7 أيام على leads حقيقية عندكم:
+• نرد على كل استفسار خلال 45 ثانية
+• نسأل أسئلة التأهيل المهمة لقطاعكم
+• نحجز المواعيد أو نحوّل للمبيعات
+• نرسل تقرير يومي بالنتائج
+
+السعر: 499 ريال فقط (مع ضمان استرداد كامل).
+
+يناسبك 15 دقيقة هذا الأسبوع أوريك النظام على سيناريو من شغلكم؟
+📅 calendly.com/sami-assiri11/dealix-demo
+
+وإذا عندك سؤال — رد على هالإيميل مباشرة. أنا شخصياً أرد.
+
+سامي العسيري
+مؤسس Dealix
+dealix.me
++966 59 778 8539
+
+إذا ما يناسبكم هالنوع من الرسائل، اكتبوا "إيقاف" وما بنتواصل مرة ثانية."""
+
+ return {
+ "type": "founder_personal",
+ "company": req.company,
+ "language": "ar",
+ "subject": subject,
+ "body": body,
+ "weakness_targeted": weakness[:100],
+ "revenue_loss_estimate": revenue_loss[:100],
+ "dealix_roi": impact[:100],
+ "opt_out_included": True,
+ "word_count": len(body.split()),
+ }
+
+
+def _build_en(req: FounderEmailRequest, sector_data: Dict) -> Dict[str, Any]:
+ name = req.contact_name or f"{req.company} team"
+ weakness = sector_data.get("weakness_en", "")
+ revenue_loss = sector_data.get("revenue_loss_en", "")
+ impact = sector_data.get("dealix_impact_en", "")
+
+ signal_line = ""
+ if "hubspot" in [s.lower() for s in req.signals]:
+ signal_line = f"I noticed {req.company} uses HubSpot — meaning you have leads coming from your website. "
+
+ subject = f"{name} — opportunity to save {req.estimated_monthly_leads * 50} SAR/month for {req.company}"
+
+ body = f"""Hi {name},
+
+I'm Sami Alassiri, founder of Dealix.
+
+{signal_line}I'm writing personally because I noticed something important about {req.company}:
+
+📊 The Problem:
+{weakness}
+
+💰 The Real Cost:
+{revenue_loss}
+
+🎯 The Solution:
+{impact}
+
+I'm not trying to sell you anything right now — I want to show you with numbers.
+
+We'll run a 7-day pilot on your actual leads:
+• Respond to every inquiry within 45 seconds
+• Ask the right qualifying questions for your industry
+• Book meetings or route to sales
+• Send you a daily results report
+
+Price: 499 SAR only (with full money-back guarantee).
+
+Do you have 15 minutes this week? I'll show you the system on a scenario from your business.
+📅 calendly.com/sami-assiri11/dealix-demo
+
+Questions? Reply to this email directly. I personally respond.
+
+Sami Alassiri
+Founder, Dealix
+dealix.me
++966 59 778 8539
+
+To stop receiving these emails, reply "STOP"."""
+
+ return {
+ "type": "founder_personal",
+ "company": req.company,
+ "language": "en",
+ "subject": subject,
+ "body": body,
+ "weakness_targeted": weakness[:100],
+ "revenue_loss_estimate": revenue_loss[:100],
+ "dealix_roi": impact[:100],
+ "opt_out_included": True,
+ "word_count": len(body.split()),
+ }
diff --git a/salesflow-saas/backend/app/api/v1/full_os.py b/salesflow-saas/backend/app/api/v1/full_os.py
new file mode 100644
index 00000000..e1166e47
--- /dev/null
+++ b/salesflow-saas/backend/app/api/v1/full_os.py
@@ -0,0 +1,183 @@
+"""Full OS API — unified deal lifecycle orchestration.
+
+Single endpoint processes any event (inbound message, reply, booking,
+payment) and returns: next stage, actions to take, response message,
+and whether human approval is needed.
+"""
+
+from __future__ import annotations
+
+from typing import Any, Dict, List, Optional
+
+from fastapi import APIRouter
+from pydantic import BaseModel
+
+router = APIRouter(prefix="/os", tags=["Full OS"])
+
+
+class ProcessEventRequest(BaseModel):
+ lead_id: str = ""
+ phone: str = ""
+ email: str = ""
+ company: str = ""
+ sector: str = ""
+ source: str = "whatsapp_inbound"
+ message: str = ""
+ current_stage: str = "new_lead"
+ event_type: str = "inbound_message"
+
+
+class BulkProcessRequest(BaseModel):
+ events: List[ProcessEventRequest]
+
+
+@router.post("/process")
+async def process_event(req: ProcessEventRequest) -> Dict[str, Any]:
+ """Process a single event through the deal lifecycle state machine.
+
+ Returns: new_stage, actions, response_message_ar, human_approval_required.
+ If auto_send_allowed=True, the response can be sent automatically.
+ If human_approval_required=True, create a draft for Sami to review.
+ """
+ from app.services.full_os_orchestrator import orchestrator, OrchestratorEvent
+
+ event = OrchestratorEvent(
+ lead_id=req.lead_id,
+ phone=req.phone,
+ email=req.email,
+ company=req.company,
+ sector=req.sector,
+ source=req.source,
+ message=req.message,
+ current_stage=req.current_stage,
+ event_type=req.event_type,
+ )
+ return orchestrator.process_event(event)
+
+
+@router.post("/process-and-act")
+async def process_and_act(req: ProcessEventRequest) -> Dict[str, Any]:
+ """Process event AND execute the first safe action.
+
+ If auto_send_allowed: sends WhatsApp response via Ultramsg.
+ If human_approval_required: creates a draft for review.
+ Always logs the activity.
+ """
+ from app.services.full_os_orchestrator import orchestrator, OrchestratorEvent
+
+ event = OrchestratorEvent(
+ lead_id=req.lead_id,
+ phone=req.phone,
+ email=req.email,
+ company=req.company,
+ sector=req.sector,
+ source=req.source,
+ message=req.message,
+ current_stage=req.current_stage,
+ event_type=req.event_type,
+ )
+ result = orchestrator.process_event(event)
+
+ execution = {"action_taken": "none", "send_result": None, "draft_created": False}
+
+ if result.get("auto_send_allowed") and result.get("response_message_ar") and req.phone:
+ if "send_whatsapp" in result.get("actions", []):
+ try:
+ from app.services.whatsapp_multi_provider import send_whatsapp_smart
+ send_result = await send_whatsapp_smart(req.phone, result["response_message_ar"])
+ execution = {
+ "action_taken": "whatsapp_sent",
+ "send_result": send_result,
+ "draft_created": False,
+ }
+ except Exception as exc:
+ execution = {
+ "action_taken": "whatsapp_failed",
+ "error": str(exc)[:200],
+ "draft_created": False,
+ }
+
+ elif result.get("human_approval_required") and result.get("response_message_ar"):
+ try:
+ from app.models.outreach_draft import OutreachDraft
+ from app.database import async_session
+ async with async_session() as session:
+ draft = OutreachDraft(
+ batch_id=f"os_{result['lead_id']}",
+ company=req.company,
+ contact_phone=req.phone,
+ contact_email=req.email,
+ channel="whatsapp" if req.phone else "email",
+ subject=f"[{result['new_stage']}] {req.company}",
+ body=result["response_message_ar"],
+ sector=req.sector,
+ status="draft",
+ approval_required=True,
+ source="full_os_orchestrator",
+ )
+ session.add(draft)
+ await session.commit()
+ execution = {
+ "action_taken": "draft_created",
+ "draft_id": str(draft.id),
+ "draft_created": True,
+ }
+ except Exception:
+ execution = {"action_taken": "draft_failed", "draft_created": False}
+
+ return {**result, "execution": execution}
+
+
+@router.post("/bulk-process")
+async def bulk_process(req: BulkProcessRequest) -> Dict[str, Any]:
+ """Process multiple events at once (for batch imports)."""
+ from app.services.full_os_orchestrator import orchestrator, OrchestratorEvent
+
+ results = []
+ for event_req in req.events:
+ event = OrchestratorEvent(
+ lead_id=event_req.lead_id,
+ phone=event_req.phone,
+ email=event_req.email,
+ company=event_req.company,
+ sector=event_req.sector,
+ source=event_req.source,
+ message=event_req.message,
+ current_stage=event_req.current_stage,
+ event_type=event_req.event_type,
+ )
+ results.append(orchestrator.process_event(event))
+
+ return {
+ "processed": len(results),
+ "results": results,
+ }
+
+
+@router.get("/whatsapp-providers")
+async def whatsapp_provider_status() -> Dict[str, Any]:
+ """Check which WhatsApp providers are configured."""
+ from app.services.whatsapp_multi_provider import check_providers
+ return await check_providers()
+
+
+@router.post("/test-send")
+async def test_whatsapp_send(phone: str, message: str = "اختبار Dealix — النظام شغّال 🚀") -> Dict[str, Any]:
+ """Test WhatsApp send via all configured providers."""
+ from app.services.whatsapp_multi_provider import send_whatsapp_smart
+ return await send_whatsapp_smart(phone, message)
+
+
+@router.get("/stages")
+async def list_stages() -> Dict[str, Any]:
+ """List all deal lifecycle stages with their possible transitions."""
+ from app.services.full_os_orchestrator import STAGE_TRANSITIONS, STAGE_AUTO_ACTIONS, STAGE_MESSAGES_AR
+
+ stages = {}
+ for stage, transitions in STAGE_TRANSITIONS.items():
+ stages[stage.value] = {
+ "transitions": {k: v.value for k, v in transitions.items()},
+ "auto_actions": [a.value for a in STAGE_AUTO_ACTIONS.get(stage, [])],
+ "message_template": STAGE_MESSAGES_AR.get(stage, ""),
+ }
+ return {"stages": stages, "total": len(stages)}
diff --git a/salesflow-saas/backend/app/api/v1/outreach_engine.py b/salesflow-saas/backend/app/api/v1/outreach_engine.py
index 7ad342b9..4ce206ba 100644
--- a/salesflow-saas/backend/app/api/v1/outreach_engine.py
+++ b/salesflow-saas/backend/app/api/v1/outreach_engine.py
@@ -101,8 +101,8 @@ def _format_phone(phone: str) -> str:
async def _send_via_ultramsg(phone: str, message: str) -> dict:
"""Send a message via Ultramsg API."""
- instance_id = os.getenv("ULTRAMSG_INSTANCE_ID", "instance168132")
- token = os.getenv("ULTRAMSG_TOKEN", "7azj2ss74wpg9jwp")
+ instance_id = os.getenv("ULTRAMSG_INSTANCE_ID", "")
+ token = os.getenv("ULTRAMSG_TOKEN", "")
if not instance_id or not token:
return {"error": "Ultramsg not configured"}
diff --git a/salesflow-saas/backend/app/api/v1/router.py b/salesflow-saas/backend/app/api/v1/router.py
index 05ca3443..de0b55d4 100644
--- a/salesflow-saas/backend/app/api/v1/router.py
+++ b/salesflow-saas/backend/app/api/v1/router.py
@@ -136,3 +136,23 @@ api_router.include_router(channels_router.router)
# ── Pricing & Checkout — Moyasar-powered payment flow ────────
from app.api.v1 import pricing as pricing_router
api_router.include_router(pricing_router.router)
+
+# ── Automation — Daily targeting + email + reply classification ─
+from app.api.v1 import automation as automation_router
+api_router.include_router(automation_router.router)
+
+# ── Draft Queue — review, approve, send outreach drafts ────────
+from app.api.v1 import drafts as drafts_router
+api_router.include_router(drafts_router.router)
+
+# ── Follow-ups — auto-generate follow-up drafts for unreplied ──
+from app.api.v1 import followups as followups_router
+api_router.include_router(followups_router.router)
+
+# ── Full OS — unified deal lifecycle orchestration ─────────────
+from app.api.v1 import full_os as full_os_router
+api_router.include_router(full_os_router.router)
+
+# ── Founder Outreach — hyper-personalized founder emails ───────
+from app.api.v1 import founder_outreach as founder_outreach_router
+api_router.include_router(founder_outreach_router.router)
diff --git a/salesflow-saas/backend/app/config.py b/salesflow-saas/backend/app/config.py
index d59f318e..d8f5340e 100644
--- a/salesflow-saas/backend/app/config.py
+++ b/salesflow-saas/backend/app/config.py
@@ -156,6 +156,23 @@ class Settings(BaseSettings):
DLQ_MAX_RETRIES: int = 5
DLQ_DRAIN_BATCH_SIZE: int = 10
+ # ── Local LLM (Ollama) ──────────────────────────────
+ OLLAMA_BASE_URL: str = "http://localhost:11434/v1"
+ OLLAMA_MODEL: str = "qwen2.5:7b"
+ LLM_CACHE_ENABLED: bool = True
+ LLM_CACHE_TTL: int = 3600
+ LLM_RATE_LIMIT_RPM: int = 60
+
+ # ── Green API (WhatsApp) ────────────────────────────
+ GREEN_API_INSTANCE_ID: str = ""
+ GREEN_API_TOKEN: str = ""
+
+ # ── Outreach Rate Limits ────────────────────────────
+ WHATSAPP_DAILY_LIMIT: int = 15
+ EMAIL_DAILY_LIMIT: int = 50
+ EMAIL_BATCH_SIZE: int = 10
+ EMAIL_BATCH_DELAY_MINUTES: int = 90
+
# ── Rate Limiting ────────────────────────────────────
RATE_LIMIT_PER_MINUTE: int = 60
RATE_LIMIT_PER_HOUR: int = 1000
diff --git a/salesflow-saas/backend/app/database.py b/salesflow-saas/backend/app/database.py
index 26029b5f..d962bfdb 100644
--- a/salesflow-saas/backend/app/database.py
+++ b/salesflow-saas/backend/app/database.py
@@ -16,7 +16,14 @@ def _get_db_url() -> str:
break
except FileNotFoundError:
continue
- return url or "sqlite+aiosqlite:///./dealix.db"
+ if not url:
+ return "sqlite+aiosqlite:///./dealix.db"
+ # Railway Postgres gives postgres:// but SQLAlchemy needs postgresql+asyncpg://
+ if url.startswith("postgres://"):
+ url = url.replace("postgres://", "postgresql+asyncpg://", 1)
+ elif url.startswith("postgresql://"):
+ url = url.replace("postgresql://", "postgresql+asyncpg://", 1)
+ return url
_DB_URL = _get_db_url()
diff --git a/salesflow-saas/backend/app/integrations/email_sender.py b/salesflow-saas/backend/app/integrations/email_sender.py
index bca912e9..92b000d4 100644
--- a/salesflow-saas/backend/app/integrations/email_sender.py
+++ b/salesflow-saas/backend/app/integrations/email_sender.py
@@ -1,29 +1,134 @@
+"""Email Sender — SMTP with rate limiting, HTML wrapper, and compliance headers.
+
+Supports Gmail app passwords. Adds professional HTML wrapper for Arabic
+RTL emails and List-Unsubscribe header for compliance.
+"""
+
+import asyncio
+import logging
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
+from typing import Optional
+
from app.config import get_settings
+logger = logging.getLogger("dealix.email")
settings = get_settings()
+UNSUBSCRIBE_AR = "\n\n---\nإذا ما يناسبكم، اكتبوا \"إيقاف\" ولن نتواصل مرة ثانية."
+UNSUBSCRIBE_EN = "\n\n---\nTo stop receiving these emails, reply with \"STOP\"."
-async def send_email(to_email: str, subject: str, body_html: str, from_name: str = None) -> dict:
- """Send email via SMTP."""
+
+def _wrap_html(body: str, direction: str = "rtl", lang: str = "ar") -> str:
+ """Wrap plain text or simple HTML in a professional email template."""
+ body_html = body.replace("\n", " ") if "<" not in body else body
+ return f"""
+
+
+
+{body_html}
+
+ Dealix — dealix.me
+
+"""
+
+
+async def send_email(
+ to_email: str,
+ subject: str,
+ body_html: str,
+ from_name: Optional[str] = None,
+ language: str = "ar",
+ add_unsubscribe: bool = True,
+ delay_seconds: float = 0,
+) -> dict:
+ """Send email via SMTP with compliance headers.
+
+ Args:
+ to_email: Recipient email
+ subject: Email subject
+ body_html: Email body (plain text or HTML)
+ from_name: Sender display name
+ language: 'ar' or 'en' — affects direction and unsubscribe text
+ add_unsubscribe: Whether to append unsubscribe line
+ delay_seconds: Wait before sending (for rate limiting in batch)
+ """
if not settings.SMTP_USER or not settings.SMTP_PASSWORD:
- return {"status": "error", "detail": "Email not configured"}
+ return {"status": "error", "detail": "SMTP_USER and SMTP_PASSWORD not configured. Add Gmail app password in Railway env."}
+
+ if delay_seconds > 0:
+ await asyncio.sleep(delay_seconds)
+
+ if add_unsubscribe:
+ unsub = UNSUBSCRIBE_AR if language == "ar" else UNSUBSCRIBE_EN
+ body_html = body_html + unsub
+
+ direction = "rtl" if language == "ar" else "ltr"
+ wrapped = _wrap_html(body_html, direction=direction, lang=language)
+
+ sender_name = from_name or settings.EMAIL_FROM_NAME or settings.APP_NAME
+ from_addr = getattr(settings, "EMAIL_FROM_ADDRESS", settings.SMTP_USER)
- sender_name = from_name or settings.APP_NAME
msg = MIMEMultipart("alternative")
msg["Subject"] = subject
- msg["From"] = f"{sender_name} <{settings.SMTP_USER}>"
+ msg["From"] = f"{sender_name} <{from_addr}>"
msg["To"] = to_email
+ msg["List-Unsubscribe"] = f""
+ msg["X-Mailer"] = "Dealix/1.0"
- msg.attach(MIMEText(body_html, "html", "utf-8"))
+ msg.attach(MIMEText(body_html, "plain", "utf-8"))
+ msg.attach(MIMEText(wrapped, "html", "utf-8"))
try:
with smtplib.SMTP(settings.SMTP_HOST, settings.SMTP_PORT) as server:
server.starttls()
server.login(settings.SMTP_USER, settings.SMTP_PASSWORD)
- server.sendmail(settings.SMTP_USER, to_email, msg.as_string())
- return {"status": "sent"}
+ server.sendmail(from_addr, to_email, msg.as_string())
+ logger.info("Email sent to %s: %s", to_email, subject[:50])
+ return {"status": "sent", "to": to_email}
+ except smtplib.SMTPAuthenticationError:
+ logger.error("SMTP auth failed — check SMTP_USER and SMTP_PASSWORD (Gmail app password)")
+ return {"status": "error", "detail": "SMTP authentication failed. Use Gmail App Password, not regular password."}
except Exception as e:
- return {"status": "error", "detail": str(e)}
+ logger.error("Email send failed: %s", e)
+ return {"status": "error", "detail": str(e)[:200]}
+
+
+async def send_email_batch(
+ emails: list[dict],
+ delay_between: float = 2.0,
+ max_batch: int = 10,
+) -> dict:
+ """Send a batch of emails with delays between each.
+
+ Args:
+ emails: List of dicts with {to, subject, body, language}
+ delay_between: Seconds between each email (default 2)
+ max_batch: Max emails per batch (default 10)
+
+ Returns: {sent, failed, results}
+ """
+ sent = 0
+ failed = 0
+ results = []
+
+ for i, email in enumerate(emails[:max_batch]):
+ delay = delay_between if i > 0 else 0
+ result = await send_email(
+ to_email=email["to"],
+ subject=email["subject"],
+ body_html=email["body"],
+ language=email.get("language", "ar"),
+ delay_seconds=delay,
+ )
+ results.append({"to": email["to"], **result})
+ if result["status"] == "sent":
+ sent += 1
+ else:
+ failed += 1
+
+ return {"sent": sent, "failed": failed, "total": len(results), "results": results}
diff --git a/salesflow-saas/backend/app/models/outreach_draft.py b/salesflow-saas/backend/app/models/outreach_draft.py
new file mode 100644
index 00000000..8a0dab4f
--- /dev/null
+++ b/salesflow-saas/backend/app/models/outreach_draft.py
@@ -0,0 +1,77 @@
+"""OutreachDraft — DB-persisted draft queue for all outreach channels.
+
+Every generated message starts as status='draft'. Sami reviews and
+approves before any send. Approved drafts are dispatched via existing
+Celery send tasks.
+"""
+
+from __future__ import annotations
+
+import uuid
+from datetime import datetime, timezone
+from typing import Any, Dict, Optional
+
+from sqlalchemy import Column, String, Integer, Boolean, DateTime, Text, JSON
+from sqlalchemy.dialects.postgresql import UUID
+
+try:
+ from app.database import Base
+except ImportError:
+ from sqlalchemy.orm import DeclarativeBase
+ class Base(DeclarativeBase):
+ pass
+
+
+class OutreachDraft(Base):
+ __tablename__ = "outreach_drafts"
+
+ id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
+ batch_id = Column(String(64), index=True)
+ company = Column(String(255), nullable=False)
+ contact_name = Column(String(255), default="")
+ contact_email = Column(String(255), default="")
+ contact_phone = Column(String(32), default="")
+ channel = Column(String(20), nullable=False) # email | whatsapp | sms | linkedin
+ subject = Column(String(500), default="")
+ body = Column(Text, nullable=False)
+ followup_2d = Column(Text, default="")
+ followup_5d = Column(Text, default="")
+ call_script = Column(Text, default="")
+ sector = Column(String(100), default="")
+ city = Column(String(100), default="")
+ pain_hypothesis = Column(Text, default="")
+ fit_score = Column(Integer, default=0)
+ risk_score = Column(Integer, default=0)
+ status = Column(String(20), default="draft", index=True)
+ # draft | approved | sent | replied | opted_out | bounced | skipped
+ approval_required = Column(Boolean, default=True)
+ created_at = Column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc))
+ approved_at = Column(DateTime(timezone=True), nullable=True)
+ sent_at = Column(DateTime(timezone=True), nullable=True)
+ replied_at = Column(DateTime(timezone=True), nullable=True)
+ reply_text = Column(Text, nullable=True)
+ reply_category = Column(String(50), nullable=True)
+ next_action = Column(String(100), nullable=True)
+ source = Column(String(100), default="daily_pipeline")
+ metadata_ = Column("metadata", JSON, default=dict)
+
+ def to_dict(self) -> Dict[str, Any]:
+ return {
+ "id": str(self.id),
+ "batch_id": self.batch_id,
+ "company": self.company,
+ "contact_name": self.contact_name,
+ "contact_email": self.contact_email,
+ "channel": self.channel,
+ "subject": self.subject,
+ "body": self.body[:200] + "..." if len(self.body or "") > 200 else self.body,
+ "sector": self.sector,
+ "city": self.city,
+ "fit_score": self.fit_score,
+ "risk_score": self.risk_score,
+ "status": self.status,
+ "created_at": self.created_at.isoformat() if self.created_at else None,
+ "sent_at": self.sent_at.isoformat() if self.sent_at else None,
+ "reply_category": self.reply_category,
+ "next_action": self.next_action,
+ }
diff --git a/salesflow-saas/backend/app/services/full_os_orchestrator.py b/salesflow-saas/backend/app/services/full_os_orchestrator.py
new file mode 100644
index 00000000..4dd11407
--- /dev/null
+++ b/salesflow-saas/backend/app/services/full_os_orchestrator.py
@@ -0,0 +1,291 @@
+"""Dealix Full OS Orchestrator — connects all services into a unified deal lifecycle.
+
+This is the brain that ties WhatsApp brain + sequences + autopilot + CRM + booking
+into one autonomous system. Every inbound lead flows through a state machine:
+
+ new_lead → qualify → nurture → meeting_booked → proposal → negotiation → closed_won/lost
+
+At each stage, the orchestrator:
+1. Detects what happened (inbound message, reply, booking, payment)
+2. Classifies intent
+3. Decides next action
+4. Executes via the appropriate service (WhatsApp, email, CRM, booking)
+5. Logs everything
+6. Moves to next stage or holds for human approval
+"""
+
+from __future__ import annotations
+
+import logging
+from datetime import datetime, timezone
+from enum import Enum
+from typing import Any, Dict, Optional
+from uuid import uuid4
+
+from pydantic import BaseModel
+
+logger = logging.getLogger("dealix.full_os")
+
+
+class DealStage(str, Enum):
+ NEW_LEAD = "new_lead"
+ QUALIFYING = "qualifying"
+ QUALIFIED = "qualified"
+ NURTURING = "nurturing"
+ MEETING_BOOKED = "meeting_booked"
+ MEETING_DONE = "meeting_done"
+ PROPOSAL_SENT = "proposal_sent"
+ NEGOTIATING = "negotiating"
+ PAYMENT_REQUESTED = "payment_requested"
+ PILOT_ACTIVE = "pilot_active"
+ CLOSED_WON = "closed_won"
+ CLOSED_LOST = "closed_lost"
+ OPTED_OUT = "opted_out"
+
+
+class LeadSource(str, Enum):
+ WHATSAPP_INBOUND = "whatsapp_inbound"
+ WEBSITE_FORM = "website_form"
+ LINKEDIN_REPLY = "linkedin_reply"
+ EMAIL_REPLY = "email_reply"
+ OUTREACH_REPLY = "outreach_reply"
+ REFERRAL = "referral"
+ PARTNER = "partner"
+ MANUAL = "manual"
+
+
+class ActionType(str, Enum):
+ SEND_WHATSAPP = "send_whatsapp"
+ SEND_EMAIL = "send_email"
+ SEND_SMS = "send_sms"
+ BOOK_MEETING = "book_meeting"
+ SEND_PROPOSAL = "send_proposal"
+ REQUEST_PAYMENT = "request_payment"
+ ENROLL_SEQUENCE = "enroll_sequence"
+ CLASSIFY_REPLY = "classify_reply"
+ ESCALATE_HUMAN = "escalate_human"
+ SYNC_CRM = "sync_crm"
+ LOG_ACTIVITY = "log_activity"
+ ONBOARD_CUSTOMER = "onboard_customer"
+ GENERATE_REPORT = "generate_report"
+ DO_NOTHING = "do_nothing"
+
+
+STAGE_TRANSITIONS = {
+ DealStage.NEW_LEAD: {
+ "interested": DealStage.QUALIFYING,
+ "ask_price": DealStage.QUALIFYING,
+ "ask_demo": DealStage.MEETING_BOOKED,
+ "ask_details": DealStage.QUALIFYING,
+ "not_now": DealStage.NURTURING,
+ "unsubscribe": DealStage.OPTED_OUT,
+ "default": DealStage.QUALIFYING,
+ },
+ DealStage.QUALIFYING: {
+ "qualified": DealStage.QUALIFIED,
+ "not_qualified": DealStage.NURTURING,
+ "wants_demo": DealStage.MEETING_BOOKED,
+ "unsubscribe": DealStage.OPTED_OUT,
+ "default": DealStage.NURTURING,
+ },
+ DealStage.QUALIFIED: {
+ "meeting_booked": DealStage.MEETING_BOOKED,
+ "sequence_enrolled": DealStage.NURTURING,
+ "default": DealStage.NURTURING,
+ },
+ DealStage.NURTURING: {
+ "replied_positive": DealStage.QUALIFYING,
+ "meeting_booked": DealStage.MEETING_BOOKED,
+ "unsubscribe": DealStage.OPTED_OUT,
+ "default": DealStage.NURTURING,
+ },
+ DealStage.MEETING_BOOKED: {
+ "meeting_done": DealStage.MEETING_DONE,
+ "no_show": DealStage.NURTURING,
+ "cancelled": DealStage.NURTURING,
+ "default": DealStage.MEETING_BOOKED,
+ },
+ DealStage.MEETING_DONE: {
+ "proposal_sent": DealStage.PROPOSAL_SENT,
+ "not_interested": DealStage.CLOSED_LOST,
+ "needs_time": DealStage.NURTURING,
+ "default": DealStage.PROPOSAL_SENT,
+ },
+ DealStage.PROPOSAL_SENT: {
+ "accepted": DealStage.PAYMENT_REQUESTED,
+ "negotiating": DealStage.NEGOTIATING,
+ "rejected": DealStage.CLOSED_LOST,
+ "default": DealStage.NEGOTIATING,
+ },
+ DealStage.NEGOTIATING: {
+ "accepted": DealStage.PAYMENT_REQUESTED,
+ "rejected": DealStage.CLOSED_LOST,
+ "default": DealStage.NEGOTIATING,
+ },
+ DealStage.PAYMENT_REQUESTED: {
+ "paid": DealStage.PILOT_ACTIVE,
+ "declined": DealStage.NEGOTIATING,
+ "default": DealStage.PAYMENT_REQUESTED,
+ },
+ DealStage.PILOT_ACTIVE: {
+ "converted": DealStage.CLOSED_WON,
+ "churned": DealStage.CLOSED_LOST,
+ "default": DealStage.PILOT_ACTIVE,
+ },
+}
+
+STAGE_AUTO_ACTIONS = {
+ DealStage.NEW_LEAD: [
+ ActionType.CLASSIFY_REPLY,
+ ActionType.SEND_WHATSAPP,
+ ActionType.LOG_ACTIVITY,
+ ],
+ DealStage.QUALIFYING: [
+ ActionType.SEND_WHATSAPP,
+ ActionType.LOG_ACTIVITY,
+ ],
+ DealStage.QUALIFIED: [
+ ActionType.BOOK_MEETING,
+ ActionType.ENROLL_SEQUENCE,
+ ActionType.SYNC_CRM,
+ ],
+ DealStage.NURTURING: [
+ ActionType.ENROLL_SEQUENCE,
+ ],
+ DealStage.MEETING_BOOKED: [
+ ActionType.SEND_WHATSAPP,
+ ActionType.SYNC_CRM,
+ ],
+ DealStage.MEETING_DONE: [
+ ActionType.SEND_PROPOSAL,
+ ActionType.LOG_ACTIVITY,
+ ],
+ DealStage.PROPOSAL_SENT: [
+ ActionType.LOG_ACTIVITY,
+ ],
+ DealStage.NEGOTIATING: [
+ ActionType.CLASSIFY_REPLY,
+ ActionType.ESCALATE_HUMAN,
+ ],
+ DealStage.PAYMENT_REQUESTED: [
+ ActionType.SEND_WHATSAPP,
+ ActionType.LOG_ACTIVITY,
+ ],
+ DealStage.PILOT_ACTIVE: [
+ ActionType.GENERATE_REPORT,
+ ActionType.ONBOARD_CUSTOMER,
+ ],
+ DealStage.CLOSED_WON: [
+ ActionType.SYNC_CRM,
+ ActionType.ONBOARD_CUSTOMER,
+ ActionType.GENERATE_REPORT,
+ ],
+ DealStage.OPTED_OUT: [
+ ActionType.DO_NOTHING,
+ ],
+}
+
+QUALIFICATION_QUESTIONS_AR = [
+ "كم lead تقريباً تستقبلون شهرياً؟",
+ "وش أكبر تحدي عندكم في متابعة الاستفسارات؟",
+ "هل عندكم فريق مبيعات يتابع الـ leads حالياً؟",
+ "كم ميزانيتكم الشهرية لأدوات المبيعات/التسويق؟",
+ "متى تبون تبدون لو الحل مناسب؟",
+]
+
+STAGE_MESSAGES_AR = {
+ DealStage.NEW_LEAD: "أهلاً وسهلاً! 👋 أنا مساعد Dealix الذكي. كيف أقدر أساعدك اليوم؟",
+ DealStage.QUALIFYING: "ممتاز! عشان أفهم احتياجكم بشكل أفضل — {question}",
+ DealStage.QUALIFIED: "بناءً على اللي ذكرته، Dealix يقدر يساعدكم. تبي نحجز 20 دقيقة demo؟\ncalendly.com/sami-assiri11/dealix-demo",
+ DealStage.NURTURING: "مرحبا مرة ثانية! عندنا تحديثات جديدة في Dealix ممكن تفيدكم. تبي أشرح؟",
+ DealStage.MEETING_BOOKED: "تم الحجز! 🎯 بنرسل لك تذكير قبل الموعد. لو تحتاج تغيّر الوقت قلّي.",
+ DealStage.MEETING_DONE: "شكراً على وقتك اليوم! بناءً على اللي ناقشناه، جهّزت لك عرض pilot مخصص.",
+ DealStage.PROPOSAL_SENT: "أرسلت لك تفاصيل العرض. أي سؤال أنا موجود.",
+ DealStage.NEGOTIATING: "فاهم. خلّني أشوف وش أقدر أسوي عشان نوصل لاتفاق يناسب الطرفين.",
+ DealStage.PAYMENT_REQUESTED: "العرض جاهز! الدفع:\n- تحويل بنكي\n- STC Pay\nبعد التأكيد نبدأ الإعداد خلال 4 ساعات.",
+ DealStage.PILOT_ACTIVE: "يوم {day} من الـ pilot! هذا تقرير اليوم:\n{report}",
+ DealStage.CLOSED_WON: "مبروك! 🎉 أهلاً بكم في Dealix. بنبدأ الإعداد الكامل.",
+ DealStage.OPTED_OUT: "تم. لن نتواصل مرة ثانية. شكراً على وقتكم.",
+}
+
+
+class OrchestratorEvent(BaseModel):
+ lead_id: str = ""
+ phone: str = ""
+ email: str = ""
+ company: str = ""
+ sector: str = ""
+ source: str = "whatsapp_inbound"
+ message: str = ""
+ current_stage: str = "new_lead"
+ event_type: str = "inbound_message"
+
+
+class FullOSOrchestrator:
+ """Central brain that processes events and drives the deal lifecycle."""
+
+ def process_event(self, event: OrchestratorEvent) -> Dict[str, Any]:
+ current = DealStage(event.current_stage) if event.current_stage else DealStage.NEW_LEAD
+
+ intent = self._classify_intent(event.message)
+
+ transitions = STAGE_TRANSITIONS.get(current, {})
+ next_stage = transitions.get(intent, transitions.get("default", current))
+
+ actions = STAGE_AUTO_ACTIONS.get(next_stage, [])
+
+ response_message = self._get_stage_message(next_stage, event)
+
+ human_required = next_stage in (
+ DealStage.NEGOTIATING,
+ DealStage.PAYMENT_REQUESTED,
+ DealStage.PILOT_ACTIVE,
+ )
+
+ return {
+ "lead_id": event.lead_id or str(uuid4())[:8],
+ "previous_stage": current.value,
+ "intent_detected": intent,
+ "new_stage": next_stage.value,
+ "actions": [a.value for a in actions],
+ "response_message_ar": response_message,
+ "human_approval_required": human_required,
+ "auto_send_allowed": not human_required and next_stage != DealStage.OPTED_OUT,
+ "timestamp": datetime.now(timezone.utc).isoformat(),
+ "qualification_question": self._get_next_question(next_stage, event),
+ }
+
+ def _classify_intent(self, message: str) -> str:
+ if not message:
+ return "default"
+ text = message.lower()
+
+ if any(w in text for w in ["إيقاف", "stop", "unsubscribe", "لا تتواصل"]):
+ return "unsubscribe"
+ if any(w in text for w in ["مهتم", "interested", "أبي أجرب", "نجرب", "تمام", "نعم"]):
+ return "interested"
+ if any(w in text for w in ["demo", "ديمو", "أوريني", "عرض", "شرح"]):
+ return "wants_demo"
+ if any(w in text for w in ["كم السعر", "كم التكلفة", "pricing", "أسعار"]):
+ return "ask_price"
+ if any(w in text for w in ["تفاصيل", "details", "أكثر"]):
+ return "ask_details"
+ if any(w in text for w in ["لاحقاً", "later", "مو الحين", "بعدين"]):
+ return "not_now"
+ if any(w in text for w in ["دفعت", "حوّلت", "paid", "تم الدفع"]):
+ return "paid"
+ if any(w in text for w in ["شراكة", "partner", "وكالة"]):
+ return "interested"
+ return "default"
+
+ def _get_stage_message(self, stage: DealStage, event: OrchestratorEvent) -> str:
+ template = STAGE_MESSAGES_AR.get(stage, "")
+ return template.replace("{company}", event.company or "").replace("{question}", "").replace("{day}", "1").replace("{report}", "")
+
+ def _get_next_question(self, stage: DealStage, event: OrchestratorEvent) -> Optional[str]:
+ if stage == DealStage.QUALIFYING:
+ return QUALIFICATION_QUESTIONS_AR[0]
+ return None
+
+
+orchestrator = FullOSOrchestrator()
diff --git a/salesflow-saas/backend/app/services/whatsapp_multi_provider.py b/salesflow-saas/backend/app/services/whatsapp_multi_provider.py
new file mode 100644
index 00000000..40458c10
--- /dev/null
+++ b/salesflow-saas/backend/app/services/whatsapp_multi_provider.py
@@ -0,0 +1,205 @@
+"""Multi-Provider WhatsApp Send — tries multiple providers with automatic fallback.
+
+Supported providers (in priority order):
+1. Green API — free dev tier, simple REST, green-api.com
+2. Ultramsg — simple REST, ultramsg.com
+3. WhatsApp Cloud API (official Meta) — needs Business verification
+4. Fonnte — ultra-cheap, fonnte.com
+
+The system tries each provider in order until one succeeds.
+All providers normalize Saudi phone numbers to 966XXXXXXXXX.
+"""
+
+from __future__ import annotations
+
+import logging
+import os
+from typing import Any, Dict, Optional
+
+import httpx
+
+logger = logging.getLogger("dealix.whatsapp_multi")
+
+
+def _format_saudi_phone(phone: str) -> str:
+ phone = phone.strip().replace(" ", "").replace("-", "").replace("+", "")
+ if phone.startswith("05"):
+ phone = "966" + phone[1:]
+ elif phone.startswith("00966"):
+ phone = phone[2:]
+ elif phone.startswith("966"):
+ pass
+ elif phone.startswith("5") and len(phone) == 9:
+ phone = "966" + phone
+ return phone
+
+
+async def send_via_greenapi(phone: str, message: str) -> Dict[str, Any]:
+ """Green API — free dev tier, green-api.com.
+
+ Env vars: GREEN_API_INSTANCE_ID, GREEN_API_TOKEN
+ """
+ instance = os.getenv("GREEN_API_INSTANCE_ID", "")
+ token = os.getenv("GREEN_API_TOKEN", "")
+ if not instance or not token:
+ return {"provider": "greenapi", "status": "not_configured"}
+
+ formatted = _format_saudi_phone(phone)
+ url = f"https://api.green-api.com/waInstance{instance}/sendMessage/{token}"
+ payload = {
+ "chatId": f"{formatted}@c.us",
+ "message": message,
+ }
+
+ try:
+ async with httpx.AsyncClient(timeout=15.0) as client:
+ resp = await client.post(url, json=payload)
+ data = resp.json()
+ if resp.status_code == 200 and data.get("idMessage"):
+ logger.info("GreenAPI sent to %s: %s", formatted, data.get("idMessage"))
+ return {"provider": "greenapi", "status": "sent", "message_id": data.get("idMessage")}
+ return {"provider": "greenapi", "status": "failed", "detail": data}
+ except Exception as exc:
+ return {"provider": "greenapi", "status": "error", "detail": str(exc)[:200]}
+
+
+async def send_via_ultramsg(phone: str, message: str) -> Dict[str, Any]:
+ """Ultramsg — simple REST API, ultramsg.com.
+
+ Env vars: ULTRAMSG_INSTANCE_ID, ULTRAMSG_TOKEN
+ """
+ instance = os.getenv("ULTRAMSG_INSTANCE_ID", "")
+ token = os.getenv("ULTRAMSG_TOKEN", "")
+ if not instance or not token:
+ return {"provider": "ultramsg", "status": "not_configured"}
+
+ formatted = _format_saudi_phone(phone)
+ url = f"https://api.ultramsg.com/{instance}/messages/chat"
+
+ try:
+ async with httpx.AsyncClient(timeout=15.0) as client:
+ resp = await client.post(url, data={
+ "token": token,
+ "to": formatted,
+ "body": message,
+ })
+ data = resp.json()
+ if "error" not in str(data).lower() or data.get("sent") == "true":
+ logger.info("Ultramsg sent to %s", formatted)
+ return {"provider": "ultramsg", "status": "sent", "result": data}
+ return {"provider": "ultramsg", "status": "failed", "detail": data}
+ except Exception as exc:
+ return {"provider": "ultramsg", "status": "error", "detail": str(exc)[:200]}
+
+
+async def send_via_fonnte(phone: str, message: str) -> Dict[str, Any]:
+ """Fonnte — ultra-cheap, fonnte.com.
+
+ Env vars: FONNTE_TOKEN
+ """
+ token = os.getenv("FONNTE_TOKEN", "")
+ if not token:
+ return {"provider": "fonnte", "status": "not_configured"}
+
+ formatted = _format_saudi_phone(phone)
+ url = "https://api.fonnte.com/send"
+
+ try:
+ async with httpx.AsyncClient(timeout=15.0) as client:
+ resp = await client.post(url, headers={"Authorization": token}, json={
+ "target": formatted,
+ "message": message,
+ })
+ data = resp.json()
+ if data.get("status"):
+ logger.info("Fonnte sent to %s", formatted)
+ return {"provider": "fonnte", "status": "sent", "result": data}
+ return {"provider": "fonnte", "status": "failed", "detail": data}
+ except Exception as exc:
+ return {"provider": "fonnte", "status": "error", "detail": str(exc)[:200]}
+
+
+async def send_via_meta_cloud(phone: str, message: str) -> Dict[str, Any]:
+ """Official WhatsApp Cloud API (Meta).
+
+ Env vars: WHATSAPP_API_TOKEN, WHATSAPP_PHONE_NUMBER_ID
+ """
+ token = os.getenv("WHATSAPP_API_TOKEN", "")
+ phone_id = os.getenv("WHATSAPP_PHONE_NUMBER_ID", "")
+ if not token or not phone_id:
+ return {"provider": "meta_cloud", "status": "not_configured"}
+
+ formatted = _format_saudi_phone(phone)
+ url = f"https://graph.facebook.com/v21.0/{phone_id}/messages"
+
+ try:
+ async with httpx.AsyncClient(timeout=15.0) as client:
+ resp = await client.post(url, headers={
+ "Authorization": f"Bearer {token}",
+ "Content-Type": "application/json",
+ }, json={
+ "messaging_product": "whatsapp",
+ "to": formatted,
+ "type": "text",
+ "text": {"body": message},
+ })
+ data = resp.json()
+ if resp.status_code == 200 and data.get("messages"):
+ msg_id = data["messages"][0].get("id", "")
+ logger.info("MetaCloud sent to %s: %s", formatted, msg_id)
+ return {"provider": "meta_cloud", "status": "sent", "message_id": msg_id}
+ return {"provider": "meta_cloud", "status": "failed", "detail": data}
+ except Exception as exc:
+ return {"provider": "meta_cloud", "status": "error", "detail": str(exc)[:200]}
+
+
+PROVIDER_CHAIN = [
+ ("greenapi", send_via_greenapi),
+ ("ultramsg", send_via_ultramsg),
+ ("fonnte", send_via_fonnte),
+ ("meta_cloud", send_via_meta_cloud),
+]
+
+
+async def send_whatsapp_smart(phone: str, message: str) -> Dict[str, Any]:
+ """Try all configured providers in order until one succeeds."""
+ attempts = []
+ for name, send_fn in PROVIDER_CHAIN:
+ result = await send_fn(phone, message)
+ attempts.append(result)
+ if result.get("status") == "sent":
+ return {
+ "sent": True,
+ "provider_used": name,
+ "result": result,
+ "attempts": len(attempts),
+ }
+
+ return {
+ "sent": False,
+ "provider_used": None,
+ "error": "all_providers_failed_or_not_configured",
+ "attempts": attempts,
+ }
+
+
+async def check_providers() -> Dict[str, Any]:
+ """Check which providers are configured (without sending)."""
+ status = {}
+ for name, _ in PROVIDER_CHAIN:
+ if name == "greenapi":
+ configured = bool(os.getenv("GREEN_API_INSTANCE_ID") and os.getenv("GREEN_API_TOKEN"))
+ elif name == "ultramsg":
+ configured = bool(os.getenv("ULTRAMSG_INSTANCE_ID") and os.getenv("ULTRAMSG_TOKEN"))
+ elif name == "fonnte":
+ configured = bool(os.getenv("FONNTE_TOKEN"))
+ elif name == "meta_cloud":
+ configured = bool(os.getenv("WHATSAPP_API_TOKEN") and os.getenv("WHATSAPP_PHONE_NUMBER_ID"))
+ else:
+ configured = False
+ status[name] = configured
+ return {
+ "providers": status,
+ "any_configured": any(status.values()),
+ "recommended": "greenapi" if not any(status.values()) else next((k for k, v in status.items() if v), None),
+ }
diff --git a/salesflow-saas/backend/railway.toml b/salesflow-saas/backend/railway.toml
index 66a8bbd5..fde8d883 100644
--- a/salesflow-saas/backend/railway.toml
+++ b/salesflow-saas/backend/railway.toml
@@ -2,8 +2,7 @@
dockerfilePath = "Dockerfile"
[deploy]
-healthcheckPath = "/api/v1/health"
+healthcheckPath = "/health"
healthcheckTimeout = 120
-startCommand = "uvicorn app.main:app --host 0.0.0.0 --port ${PORT:-8000} --workers 2"
restartPolicyType = "ON_FAILURE"
restartPolicyMaxRetries = 3
diff --git a/salesflow-saas/backend/requirements.txt b/salesflow-saas/backend/requirements.txt
index c2277edf..0cf2bda2 100644
--- a/salesflow-saas/backend/requirements.txt
+++ b/salesflow-saas/backend/requirements.txt
@@ -83,3 +83,4 @@ qrcode==8.0
Pillow==11.0.0
xmltodict==0.14.2
email-validator>=2.1.0
+aiosqlite>=0.20.0
diff --git a/salesflow-saas/backend/start.sh b/salesflow-saas/backend/start.sh
new file mode 100755
index 00000000..c53de262
--- /dev/null
+++ b/salesflow-saas/backend/start.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+set -e
+echo "[dealix] PORT=$PORT"
+echo "[dealix] Testing imports..."
+python3 -c "
+try:
+ from app.main import app
+ print(f'[dealix] OK — {len(app.routes)} routes')
+except Exception as e:
+ print(f'[dealix] IMPORT FAILED: {e}')
+ import traceback
+ traceback.print_exc()
+ exit(1)
+" 2>&1
+echo "[dealix] Starting uvicorn on port ${PORT:-8000}..."
+exec uvicorn app.main:app --host 0.0.0.0 --port "${PORT:-8000}" --workers 1 --log-level info
diff --git a/salesflow-saas/backend/tests/test_automation_system.py b/salesflow-saas/backend/tests/test_automation_system.py
new file mode 100644
index 00000000..4d6cd68d
--- /dev/null
+++ b/salesflow-saas/backend/tests/test_automation_system.py
@@ -0,0 +1,175 @@
+"""Tests for the full automation outreach system — drafts, pipeline, followups."""
+
+import pytest
+from fastapi.testclient import TestClient
+from fastapi import FastAPI
+
+
+@pytest.fixture
+def app():
+ app = FastAPI()
+ from app.api.v1.automation import router as auto_router
+ app.include_router(auto_router)
+ return app
+
+
+@pytest.fixture
+def client(app):
+ return TestClient(app)
+
+
+def test_email_generate(client):
+ resp = client.post("/automation/email/generate", json={
+ "company": "Foodics",
+ "sector": "saas",
+ "city": "الرياض",
+ "contact_name": "أحمد",
+ })
+ assert resp.status_code == 200
+ data = resp.json()
+ assert "subject_ar" in data
+ assert "body_ar" in data
+ assert "followup_day_2" in data
+ assert "followup_day_5" in data
+ assert "call_script_ar" in data
+ assert "linkedin_manual_message" in data
+ assert data["opt_out_included"] is True
+ assert data["word_count"] > 0
+ assert "Foodics" in data["subject_ar"]
+
+
+def test_email_generate_with_signals(client):
+ resp = client.post("/automation/email/generate", json={
+ "company": "TestCo",
+ "sector": "real_estate",
+ "signals": ["hubspot"],
+ })
+ data = resp.json()
+ assert "HubSpot" in data["body_ar"]
+
+
+def test_compliance_check_allowed(client):
+ resp = client.post("/automation/compliance/check", json={
+ "email": "ahmed@company.sa",
+ "company": "TestCo",
+ "source": "linkedin",
+ })
+ data = resp.json()
+ assert data["allowed"] is True
+ assert data["reason"] == "compliant"
+
+
+def test_compliance_check_opt_out(client):
+ resp = client.post("/automation/compliance/check", json={
+ "email": "ahmed@company.sa",
+ "opt_out": True,
+ })
+ data = resp.json()
+ assert data["allowed"] is False
+ assert data["reason"] == "opt_out"
+
+
+def test_compliance_check_bounced(client):
+ resp = client.post("/automation/compliance/check", json={
+ "email": "bad@company.sa",
+ "bounced_before": True,
+ })
+ data = resp.json()
+ assert data["allowed"] is False
+ assert data["reason"] == "bounced_before"
+
+
+def test_compliance_check_high_risk(client):
+ resp = client.post("/automation/compliance/check", json={
+ "email": "ceo@big.sa",
+ "risk_score": 80,
+ })
+ data = resp.json()
+ assert data["allowed"] is False
+ assert data["reason"] == "high_risk"
+
+
+def test_compliance_check_personal_email(client):
+ resp = client.post("/automation/compliance/check", json={
+ "email": "ahmed@gmail.com",
+ "source": "linkedin",
+ })
+ data = resp.json()
+ assert data["allowed"] is True
+ assert "personal_email" in data["reason"]
+
+
+def test_compliance_check_no_source(client):
+ resp = client.post("/automation/compliance/check", json={
+ "email": "ahmed@company.sa",
+ "source": "",
+ })
+ data = resp.json()
+ assert data["allowed"] is False
+ assert data["reason"] == "no_source"
+
+
+def test_reply_classify_interested(client):
+ resp = client.post("/automation/reply/classify", json={
+ "reply_text": "مهتم جداً، أبي أجرب",
+ "company": "TestCo",
+ })
+ data = resp.json()
+ assert data["category"] == "interested"
+ assert data["auto_reply_allowed"] is True
+ assert "calendly" in data["suggested_response"].lower() or "demo" in data["suggested_response"].lower() or "20 دقيقة" in data["suggested_response"]
+
+
+def test_reply_classify_price(client):
+ resp = client.post("/automation/reply/classify", json={
+ "reply_text": "كم السعر؟",
+ })
+ data = resp.json()
+ assert data["category"] == "ask_price"
+ assert "499" in data["suggested_response"]
+
+
+def test_reply_classify_unsubscribe(client):
+ resp = client.post("/automation/reply/classify", json={
+ "reply_text": "إيقاف لا تتواصل معي",
+ })
+ data = resp.json()
+ assert data["category"] == "unsubscribe"
+ assert data["auto_reply_allowed"] is False
+
+
+def test_reply_classify_crm(client):
+ resp = client.post("/automation/reply/classify", json={
+ "reply_text": "عندنا CRM وما نحتاج نظام ثاني",
+ })
+ data = resp.json()
+ assert data["category"] == "already_has_crm"
+ assert "طبقة" in data["suggested_response"] or "CRM" in data["suggested_response"]
+
+
+def test_daily_targeting_generate(client):
+ resp = client.post("/automation/daily-targeting/generate", json={
+ "sectors": ["real_estate", "construction"],
+ "cities": ["الرياض"],
+ "daily_target_count": 5,
+ })
+ data = resp.json()
+ assert data["total_generated"] > 0
+ assert len(data["targets"]) <= 5
+ assert data["targets"][0]["sector"] in ("real_estate", "construction")
+ assert data["approval_required"] is True
+
+
+def test_sector_pain_map_coverage(client):
+ """Verify all 9 sectors produce valid emails."""
+ sectors = ["real_estate", "construction", "hospitality", "food_beverage",
+ "logistics", "agency", "saas", "healthcare", "education"]
+ for sector in sectors:
+ resp = client.post("/automation/email/generate", json={
+ "company": f"Test_{sector}",
+ "sector": sector,
+ })
+ assert resp.status_code == 200, f"Failed for sector: {sector}"
+ data = resp.json()
+ assert len(data["body_ar"]) > 50, f"Empty body for {sector}"
+ assert "إيقاف" in data["body_ar"], f"Missing opt-out for {sector}"
diff --git a/salesflow-saas/docs/ops/10_CUSTOMERS_PER_WEEK_MACHINE.md b/salesflow-saas/docs/ops/10_CUSTOMERS_PER_WEEK_MACHINE.md
new file mode 100644
index 00000000..ccfa748b
--- /dev/null
+++ b/salesflow-saas/docs/ops/10_CUSTOMERS_PER_WEEK_MACHINE.md
@@ -0,0 +1,236 @@
+# Dealix — ماكينة 10 زبائن/أسبوع
+
+## الحساب الدقيق
+
+```
+10 زبائن مدفوعين/أسبوع
+= 30 demo/أسبوع (33% demo→paid)
+= 100 رد إيجابي/أسبوع (30% reply→demo)
+= 500 محادثة/أسبوع (20% touch→reply)
+= ~70 محادثة/يوم
+```
+
+## مصادر الـ 70 محادثة يومياً
+
+| القناة | يومياً | أسبوعياً | النوع |
+|--------|--------|----------|-------|
+| LinkedIn DM (مباشر) | 10 | 70 | cold/warm |
+| Cold Email (مخصص) | 20 | 140 | cold |
+| WhatsApp warm | 5 | 35 | warm |
+| Landing page inbound | 5 | 35 | inbound |
+| Partner referrals | 5 | 35 | warm |
+| X/LinkedIn content inbound | 5 | 35 | inbound |
+| Community/group posts | 5 | 35 | warm |
+| Referral asks | 5 | 35 | warm |
+| Re-engagement (cold leads) | 10 | 70 | reactivation |
+| **المجموع** | **70** | **490** | |
+
+## خطة الأسبوع الأول (واقعية)
+
+| اليوم | LinkedIn | Email | WhatsApp | Content | Referral | المجموع |
+|-------|----------|-------|----------|---------|----------|---------|
+| السبت | 10 | 20 | 5 | 2 | 3 | 40 |
+| الأحد | 10 | 20 | 5 | 1 | 3 | 39 |
+| الإثنين | 10 | 20 | 5 | 2 | 3 | 40 |
+| الثلاثاء | 10 | 20 | 5 | 1 | 3 | 39 |
+| الأربعاء | 10 | 20 | 5 | 2 | 3 | 40 |
+| الخميس | 10 | 20 | 5 | 1 | 3 | 39 |
+| الجمعة | 5 | 0 | 5 | 2 | 5 | 17 |
+| **المجموع** | **65** | **120** | **35** | **11** | **23** | **254** |
+
+Week 1 = 254 touch. Week 2+ = 490+/week مع content inbound يكبر.
+
+---
+
+## القطاعات المستهدفة (6 segments)
+
+### Segment 1: SaaS سعودي (أعلى fit)
+- **ICP:** شركة SaaS 20-200 موظف، عندها leads من موقع/إعلانات
+- **المشكلة:** رد بطيء، leads تبرد، فريق مبيعات صغير
+- **العرض:** pilot 7 أيام مجاني → 990 ريال/شهر
+- **أمثلة:** Foodics, Salla, Lean, Lucidya, Rewaa, Qoyod, Maqsam
+
+### Segment 2: وكالات تسويق (أعلى volume)
+- **ICP:** وكالة 5-50 موظف، عندها عملاء B2B
+- **المشكلة:** عملاءهم يجيبون leads بالإعلانات لكن المتابعة ضعيفة
+- **العرض:** شراكة setup + retainer (3,000-15,000 لكل عميل)
+- **أمثلة:** Peak Content, Digital8, Brand Lounge, Wavy, Bold
+
+### Segment 3: عقارات (أعلى urgency)
+- **ICP:** شركة عقارية أو مطوّر، leads كثيرة من إعلانات
+- **المشكلة:** 500+ lead/شهر، 30% فقط يُتابع
+- **العرض:** pilot على 50 lead → يثبت ROI فوري
+- **أمثلة:** Aqar, Dar Global, Roshn
+
+### Segment 4: تجارة إلكترونية (volume)
+- **ICP:** متجر إلكتروني متوسط، واتساب كقناة رئيسية
+- **المشكلة:** رسائل واتساب كثيرة، رد يدوي بطيء
+- **العرض:** رد تلقائي + تأهيل + حجز
+- **أمثلة:** Jarir online, Dokkan Afkar, Haraj sellers
+
+### Segment 5: خدمات B2B (high LTV)
+- **ICP:** شركة خدمات (استشارات، محاماة، محاسبة)
+- **المشكلة:** leads من الموقع تضيع بسبب عدم المتابعة
+- **العرض:** pilot مع تأهيل + حجز مواعيد
+- **أمثلة:** Big 4 partners, law firms, consulting boutiques
+
+### Segment 6: مطاعم/F&B (quick win)
+- **ICP:** سلسلة 5+ فروع، طلبات واتساب/إنستقرام
+- **المشكلة:** طلبات B2B (تموين، حفلات) تضيع
+- **العرض:** رد تلقائي على طلبات التموين
+- **أمثلة:** Albaik (B2B), Herfy catering, Floward corporate
+
+---
+
+## الرسائل حسب القطاع
+
+### SaaS — LinkedIn DM
+```
+السلام عليكم [الاسم]،
+
+شفت إن [الشركة] تستخدمون [HubSpot/WhatsApp widget/forms]
+— يعني عندكم leads تجي من الموقع.
+
+سؤال سريع: كم lead تخسرونه شهرياً بسبب تأخر الرد؟
+
+ديلكس يرد بالعربي خلال 45 ثانية، يؤهل العميل، ويحجز
+الموعد تلقائياً. بدون ما تغيّرون CRM.
+
+أوريك تجربة 20 دقيقة على سيناريو من شغلكم؟
+calendly.com/sami-assiri11/dealix-demo
+```
+
+### وكالات — LinkedIn DM
+```
+السلام عليكم [الاسم]،
+
+أطلقنا Dealix — طبقة AI بالعربي ترد على leads عملاء الوكالات
+وتأهلهم وتحجز لهم مواعيد.
+
+الفكرة للوكالة: تبيعونها كـ setup + retainer فوق حملاتكم
+الحالية. العميل يشوف ROI أسرع، وأنتم تزيدون MRR.
+
+أبي أجرب مع أول عميل عندكم كـ pilot مجاني.
+20 دقيقة أشرح الآلية؟
+calendly.com/sami-assiri11/dealix-demo
+```
+
+### عقارات — WhatsApp
+```
+السلام عليكم [الاسم]،
+
+أشتغل على Dealix — يرد على leads العقار بالعربي
+خلال 45 ثانية، يسأل عن الميزانية والموقع المفضل
+والمساحة، ويحجز موعد معاينة تلقائي.
+
+عندكم leads من إعلانات تبغون تجربون عليها أسبوع؟
+بدون التزام.
+```
+
+### تجارة إلكترونية — Email
+```
+الموضوع: رد تلقائي بالعربي على رسائل واتساب متجركم
+
+مرحباً [الاسم]،
+
+Dealix يساعد المتاجر الإلكترونية ترد على استفسارات
+واتساب بالعربي تلقائياً — يجاوب عن المنتج، يأكد
+التوفر، ويوجّه العميل للشراء.
+
+أقدر أوريك تجربة على 10 محادثات من متجركم.
+هل يناسبك هذا الأسبوع؟
+
+إذا ما كان مناسبًا، أرسل "إيقاف" ولن أتابع.
+```
+
+---
+
+## الأدوات المطلوبة
+
+| الأداة | الغرض | التكلفة | الأولوية |
+|--------|--------|---------|---------|
+| LinkedIn Sales Nav | بحث + 50 InMail/شهر | $80/شهر | P0 |
+| Instantly.ai أو Smartlead | cold email automation | $30/شهر | P0 |
+| Calendly | حجز مواعيد | مجاني | P0 (موجود) |
+| WhatsApp Business | رسائل warm | مجاني | P0 |
+| Resend API | transactional email | مجاني | P0 (موجود) |
+| Google Sheets | CRM يدوي | مجاني | P0 |
+| PostHog | tracking | مجاني | P0 (موجود) |
+| Stripe/Moyasar | دفع | بعد أول demo | P1 |
+
+**إجمالي: $110/شهر** — أقل من ربع زبون واحد.
+
+---
+
+## Tracker Template (Google Sheet)
+
+| # | الشركة | الشخص | القطاع | القناة | التاريخ | الحالة | الرد | Demo | Pilot | Paid | ملاحظات |
+|---|--------|-------|--------|--------|---------|--------|------|------|-------|------|---------|
+| 1 | | | | LinkedIn | | sent | | | | | |
+| 2 | | | | Email | | sent | | | | | |
+
+**الحالات:** new → sent → replied → demo_booked → demo_done → pilot_started → pilot_ended → paid → churned
+
+---
+
+## KPIs أسبوعية
+
+| المقياس | الأسبوع 1 | الأسبوع 2 | الأسبوع 3 | الأسبوع 4 | الهدف الشهري |
+|---------|-----------|-----------|-----------|-----------|-------------|
+| Touches | 254 | 490 | 490 | 490 | 1,724 |
+| Replies | 25 | 50 | 70 | 100 | 245 |
+| Demos | 3 | 8 | 10 | 10 | 31 |
+| Pilots | 1 | 3 | 5 | 5 | 14 |
+| Paid | 0 | 1 | 3 | 6 | **10** |
+| MRR (SAR) | 0 | 990 | 2,970 | 5,940 | **9,900** |
+
+---
+
+## Daily Operating Schedule
+
+```
+07:00 Wake + check replies from yesterday
+08:00 LinkedIn: send 10 DMs (from target list)
+09:00 Email: send 20 cold emails (Instantly/Smartlead)
+10:00 WhatsApp: 5 warm messages
+10:30 Demo #1 (if booked)
+11:30 Demo #2 (if booked)
+12:00 Follow-ups: reply to all messages
+13:00 Break
+14:00 Partner outreach: 5 agency messages
+15:00 Demo #3 (if booked)
+16:00 Content: write + publish 1-2 posts
+17:00 Referral asks: 3-5 warm intros
+18:00 Scorecard update + tomorrow prep
+20:00 Reply to late messages
+```
+
+---
+
+## Week 1 Exact Plan
+
+### Day 1 (السبت)
+- [ ] Morning: send 10 LinkedIn DMs (SaaS segment)
+- [ ] Send 20 cold emails (SaaS + agencies)
+- [ ] 5 WhatsApp to warm contacts
+- [ ] Publish Founder Launch Post
+- [ ] 3 referral asks
+- [ ] Scorecard update
+
+### Day 2 (الأحد)
+- [ ] 10 LinkedIn DMs (agencies segment)
+- [ ] 20 emails (agencies + real estate)
+- [ ] 5 WhatsApp
+- [ ] 1 X post
+- [ ] Handle Day 1 replies
+- [ ] 3 referral asks
+
+### Day 3 (الإثنين)
+- [ ] 10 LinkedIn DMs (real estate + ecomm)
+- [ ] 20 emails
+- [ ] 5 WhatsApp
+- [ ] LinkedIn article/thread
+- [ ] First demos (if booked)
+- [ ] 3 referral asks
+
+### Day 4-7: repeat + handle demos + send pilot offers + request payments
diff --git a/salesflow-saas/docs/ops/AGENCY_PARTNER_OFFER.md b/salesflow-saas/docs/ops/AGENCY_PARTNER_OFFER.md
new file mode 100644
index 00000000..759d65c5
--- /dev/null
+++ b/salesflow-saas/docs/ops/AGENCY_PARTNER_OFFER.md
@@ -0,0 +1,212 @@
+# Dealix — Agency & Partner Offer
+
+---
+
+## Who This Is For
+
+| Partner Type | Profile | Why They Care |
+|-------------|---------|---------------|
+| **Performance Agency** | Runs ads for Saudi B2B clients, 5-30 people | Leads go cold → client churns → agency loses MRR |
+| **Full-Service Digital Agency** | Websites + ads + social, 10-50 people | Needs post-lead solution to retain clients |
+| **Freelance Digital Marketer** | Solo or 2-3 people, manages multiple clients | Wants recurring revenue beyond project fees |
+| **CRM/RevOps Consultant** | Implements HubSpot/Zoho/Salesforce | Client doesn't use CRM because no leads flow in properly |
+| **WhatsApp Marketing Specialist** | Knows Saudi WhatsApp culture | Can't scale manual responses for 10+ clients |
+| **IT Services Company** | Sells tech solutions to Saudi SMEs | Wants to add SaaS reselling to portfolio |
+
+---
+
+## What Partners Can Sell
+
+### Tier 1: Dealix Response (للشركات الصغيرة)
+**Client pays partner:** 990 SAR/month
+**Setup fee:** 3,000 SAR (partner keeps 100%)
+**Partner MRR share:** 30% = 297 SAR/month
+
+**Scope:**
+- AI Arabic responder on WhatsApp or web forms
+- 5 qualification questions configured per client
+- Leads classified: hot / warm / cold
+- Weekly summary report (manual first, auto later)
+
+**Dealix delivers:** System config + AI response + weekly report
+**Partner delivers:** Client relationship + billing + communication
+
+### Tier 2: Dealix Growth (للشركات المتوسطة)
+**Client pays partner:** 2,490 SAR/month
+**Setup fee:** 7,000 SAR (partner keeps 100%)
+**Partner MRR share:** 25% = 622 SAR/month
+
+**Scope:**
+Everything in Tier 1, plus:
+- Calendly/booking integration
+- CRM sync (HubSpot two-way)
+- Follow-up sequences (Day +2, +5, +10)
+- Daily report (not just weekly)
+
+### Tier 3: Dealix Enterprise (للمؤسسات)
+**Client pays partner:** Custom (starting 5,000+ SAR/month)
+**Setup fee:** 15,000+ SAR
+**Partner MRR share:** 20% (negotiable)
+
+**Scope:**
+Everything in Tier 2, plus:
+- Multi-channel (WhatsApp + Email + Voice)
+- Executive dashboard
+- Approval workflows
+- SLA tracking
+- Dedicated account manager from Dealix
+
+---
+
+## Service-Exchange Models
+
+### Model A: Referral Only
+- Partner introduces client
+- Dealix handles everything (sales, setup, billing)
+- Partner earns **10% of MRR for 12 months**
+- No work required from partner after intro
+- Best for: consultants, mentors, advisors
+
+### Model B: Co-Sell
+- Partner sells to client, collects payment
+- Dealix provides system + support
+- Partner earns **setup fee (100%) + 20-30% MRR**
+- Partner handles client relationship
+- Best for: agencies with existing clients
+
+### Model C: White-Label (future)
+- Partner brands Dealix as their own
+- Full control of client billing
+- Higher MRR share (40-50%)
+- Requires Scale plan or above
+- **Not available now — post first 10 paying partners**
+
+---
+
+## What Dealix Delivers (manual-first)
+
+| Deliverable | Now (Manual) | Later (Automated) |
+|-------------|-------------|------------------|
+| Client onboarding | Sami configures in 24h | Self-service portal |
+| Arabic response templates | Manually tuned per sector | LLM-generated per client |
+| Qualification questions | Configured per client brief | AI-optimized from conversations |
+| Daily/weekly reports | Manual email | Auto-generated dashboard |
+| CRM sync | Manual export + import | Real-time HubSpot sync |
+| Billing to client | Partner handles | Moyasar recurring (future) |
+| Partner commission | Manual bank transfer | Automatic monthly payout |
+| Escalation | WhatsApp to Sami | Ticketing system |
+
+---
+
+## Implementation Packages
+
+### Quick Start (1 day)
+- 1 channel (WhatsApp or web form)
+- 5 qualification questions
+- 1 sector template
+- Weekly report
+- **Setup: 3,000 SAR**
+
+### Standard (3 days)
+- 2 channels
+- Custom qualification flow
+- Calendly integration
+- CRM sync
+- Daily report
+- **Setup: 7,000 SAR**
+
+### Advanced (5-7 days)
+- All channels
+- Multi-department routing
+- Approval workflows
+- Executive dashboard
+- **Setup: 15,000+ SAR**
+
+---
+
+## Referral / Partner Workflow
+
+### Step 1: Partner Applies
+- Books Calendly call or sends WhatsApp message
+- Sami evaluates fit (are they in Saudi B2B? do they have clients?)
+- If yes → simple 1-page partner agreement
+
+### Step 2: First Client Pilot
+- Partner introduces their best-fit client
+- Dealix runs 7-day pilot on client's leads — **free for first client**
+- Client sees: faster response, more qualified leads, booked meetings
+
+### Step 3: Client Converts
+- Client agrees to monthly plan
+- Partner bills client directly (co-sell) or Dealix bills (referral)
+- Partner earns according to model A, B, or C
+
+### Step 4: Scale
+- Partner adds more clients
+- Each new client = new setup fee + new MRR
+- Monthly partner check-in with Sami
+
+---
+
+## Stage-1 Partner Motion (Week 1-4)
+
+### Target: 5 partners signed, 3 with first client in pilot
+
+**Week 1:** Contact 10 agencies
+- 5 via LinkedIn DM (message in COMMAND_CENTER.md)
+- 5 via warm WhatsApp (personal network)
+- Offer: free pilot for first client
+
+**Week 2:** Run 3 pilots
+- Each pilot = 7 days on client's leads
+- Daily report to partner + client
+- Measure: response time, qualified leads, booked meetings
+
+**Week 3:** Convert 2 pilots to paid
+- Partner earns first setup fee
+- MRR starts flowing
+
+**Week 4:** Onboard 2 more partners
+- Use first partner as reference
+- Repeat Week 1-2 process
+
+### First 5 Partner Targets
+1. **Peak Content** (Riyadh) — content marketing, has B2B clients
+2. **Digital8** (Riyadh) — full service, performance marketing
+3. **Wavy** (Saudi) — social media management
+4. **Hayan** (Saudi) — HubSpot implementation partner
+5. **Bold Agency** (Riyadh) — performance + creative
+
+### Partner Outreach Message
+```
+السلام عليكم [الاسم]،
+
+أنا سامي — مؤسس Dealix. نشتغل على طبقة AI بالعربي ترد
+على leads عملاء الوكالات وتأهلهم وتحجز مواعيد تلقائياً.
+
+الفكرة لكم: تبيعونها كـ setup + retainer فوق خدماتكم.
+العميل يشوف ROI أسرع — وأنتم تزيدون MRR.
+
+نبدأ بـ pilot مجاني لأول عميل عندكم — 7 أيام بلا تكلفة.
+
+يناسبك 20 دقيقة هذا الأسبوع؟
+calendly.com/sami-assiri11/dealix-demo
+```
+
+---
+
+## Revenue Math
+
+```
+Per partner (conservative):
+ 2 clients/quarter × 990 SAR/month = 1,980 SAR MRR
+ + 2 × 3,000 SAR setup = 6,000 SAR
+ Annual from 1 partner: 23,760 + 6,000 = 29,760 SAR
+
+5 partners:
+ = 9,900 SAR MRR + 30,000 SAR setup
+ = ~148,800 SAR/year
+
+10 partners:
+ = ~297,600 SAR/year
+```
diff --git a/salesflow-saas/docs/ops/AGENCY_PARTNER_PLAN.md b/salesflow-saas/docs/ops/AGENCY_PARTNER_PLAN.md
new file mode 100644
index 00000000..d4162d26
--- /dev/null
+++ b/salesflow-saas/docs/ops/AGENCY_PARTNER_PLAN.md
@@ -0,0 +1,78 @@
+# Dealix — Agency / Partner Motion Plan
+
+## Who Are Partners
+
+| Type | Example | What They Sell | Our Revenue Share |
+|------|---------|---------------|------------------|
+| **Referral** | Freelancer, consultant | Intro only | 10% MRR × 12 months |
+| **Implementation** | HubSpot consultant, RevOps | Setup + config | 100% of setup fee |
+| **Agency** | Digital agency, performance agency | Setup + monthly service | Setup fee + 20-30% MRR |
+| **Strategic** | Salla, Zid, Unifonic | Co-sell / bundle | Revenue share negotiated |
+
+## What Partners Can Sell
+
+### Package 1: "Dealix Basic" (for agency clients)
+- AI Arabic responder on WhatsApp/forms
+- Lead qualification (5 questions)
+- Daily summary report
+- **Client pays agency:** 990 SAR/month
+- **Agency keeps:** 297 SAR/month (30%)
+- **Setup fee to agency:** 3,000 SAR (agency keeps 100%)
+
+### Package 2: "Dealix Growth" (for larger clients)
+- Everything in Basic
+- Calendly/booking integration
+- CRM sync (HubSpot)
+- Follow-up sequences
+- **Client pays agency:** 2,490 SAR/month
+- **Agency keeps:** 622 SAR/month (25%)
+- **Setup fee:** 7,000 SAR (agency keeps 100%)
+
+## What We Deliver (manual-first)
+- System configuration for each client
+- Arabic response templates tuned to client's sector
+- Daily monitoring first 7 days
+- Escalation support
+- Monthly performance review
+
+## What Is Manual Now vs Automated Later
+
+| Activity | Now (Manual) | Later (Automated) |
+|----------|-------------|------------------|
+| Client onboarding | Sami + Google Sheet | Self-service portal |
+| Response config | Manually set templates | LLM-generated per client |
+| Daily report | Manual email | Auto-generated dashboard |
+| Billing | Bank transfer / STC Pay | Moyasar recurring |
+| Partner commission | Manual calculation | Automatic payout |
+
+## Stage-1 Service Exchange (First 3 Partners)
+
+### Offer to Agency:
+"أعطيني أول عميل عندك — أسوي له pilot مجاني 7 أيام.
+لو العميل رضي واشترك، تبدأ تحصّل 30% من MRR + setup fee كامل لك.
+لو ما رضي — لا التزام."
+
+### Partner Onboarding Checklist:
+1. Partner signs simple agreement (1 page)
+2. Partner introduces first client
+3. We run 7-day pilot on client's leads
+4. Client converts → partner earns
+5. Repeat
+
+## First 5 Partner Targets
+1. Peak Content (Riyadh) — content + digital
+2. Digital8 (Riyadh) — full service digital
+3. Wavy (Saudi) — social media
+4. Hayan (Saudi) — HubSpot partner
+5. Bold Agency (Riyadh) — performance
+
+## Revenue Math (per partner)
+
+```
+1 partner × 3 clients/quarter
+= 3 × 990 SAR/month = 2,970 SAR MRR per partner
++ 3 × 3,000 SAR setup = 9,000 SAR one-time
+
+5 partners × same rate = 14,850 SAR MRR + 45,000 SAR setup
+= ~223,800 SAR/year from partner channel alone
+```
diff --git a/salesflow-saas/docs/ops/DAILY_REVENUE_MACHINE.md b/salesflow-saas/docs/ops/DAILY_REVENUE_MACHINE.md
new file mode 100644
index 00000000..07ce7a35
--- /dev/null
+++ b/salesflow-saas/docs/ops/DAILY_REVENUE_MACHINE.md
@@ -0,0 +1,215 @@
+# Dealix — Daily Revenue Machine
+# ماكينة الإيراد اليومي
+
+## الهدف: تدفق مالي يومي مستقر
+
+```
+الهدف الشهري: 30,000 ريال MRR
+= 30 عميل × 990 ريال/شهر (Starter)
+= ~1,000 ريال/يوم إيراد متكرر
+```
+
+---
+
+## 8 قنوات إيراد متوازية
+
+### القناة 1: LinkedIn Outbound (أعلى جودة)
+**الأداة:** LinkedIn Sales Navigator ($80/شهر)
+**الحجم:** 10 رسائل/يوم × 6 أيام = 60/أسبوع
+**التحويل المتوقع:** 15% رد → 5% demo → 2% paid
+**الإيراد المتوقع:** 60 × 2% = ~1 عميل/أسبوع = 990 ريال
+
+**التنفيذ:**
+1. Sales Navigator → فلتر: Saudi Arabia + 20-200 employees + SaaS/Services/Real Estate
+2. ابحث عن: VP Sales, Head of Sales, CEO, COO, Growth Lead
+3. أرسل Connection Request + Note (النص في SAUDI_60_TARGETS.md)
+4. بعد القبول → رسالة تفصيلية + Calendly
+5. Follow-up +2/+5/+10 أيام
+
+---
+
+### القناة 2: Cold Email (أعلى volume)
+**الأداة:** Instantly.ai ($30/شهر) أو Smartlead ($39/شهر)
+**الحجم:** 30-50 إيميل/يوم × 6 أيام = 180-300/أسبوع
+**التحويل المتوقع:** 3% reply → 1% demo → 0.3% paid
+**الإيراد المتوقع:** 300 × 0.3% = ~1 عميل/أسبوع = 990 ريال
+
+**التنفيذ:**
+1. اجمع إيميلات من:
+ - LinkedIn profiles (اسأل في الرسالة)
+ - Company websites (contact/about pages)
+ - Apollo.io free tier (50 credits/شهر)
+ - Hunter.io (50 searches مجاناً)
+2. حمّل في Instantly.ai
+3. Sequence: 3 إيميلات × 3 أيام بينهم
+4. Subject lines مُختبرة:
+ - "سؤال سريع عن متابعة الـ leads في [الشركة]"
+ - "73% من العملاء السعوديين يتوقعون رد خلال 30 دقيقة"
+ - "[الاسم] — فكرة لـ [الشركة]"
+5. **مهم:** كل إيميل فيه سطر opt-out: "إذا ما كان مناسبًا، أرسل 'إيقاف'"
+
+---
+
+### القناة 3: WhatsApp Warm (أسرع إقفال)
+**الأداة:** WhatsApp Business (مجاني)
+**الحجم:** 5-10 رسائل/يوم (warm contacts فقط)
+**التحويل المتوقع:** 30% reply → 15% demo → 8% paid
+**الإيراد المتوقع:** 50 × 8% = ~4 عملاء/أسبوع = 3,960 ريال
+
+**التنفيذ:**
+- أرسل لـ: أصدقاء founders، زملاء سابقين، معارف من مؤتمرات
+- الرسالة: Tier 1 من FIRST_REVENUE_GUARANTEE_PLAYBOOK
+- **هذي أسرع قناة للإيراد — ابدأ فيها أول**
+
+---
+
+### القناة 4: Agency Partners (أعلى LTV)
+**الأداة:** LinkedIn + WhatsApp
+**الحجم:** 5 وكالات/أسبوع
+**التحويل المتوقع:** 20% partner → 2 عملاء لكل وكالة
+**الإيراد المتوقع:** 1 وكالة = 5,000 setup + 990×2 = 6,980 ريال
+
+**التنفيذ:**
+1. ابحث: "digital marketing agency Saudi" أو "وكالة تسويق رقمي الرياض"
+2. أرسل رسالة وكالة من SAUDI_60_TARGETS.md
+3. العرض: Setup 3,000-5,000 + 20-30% MRR recurring
+4. أول عميل مجاني كـ proof
+
+---
+
+### القناة 5: Content Inbound (مجاني + مركّب)
+**الأداة:** LinkedIn + X (مجاني)
+**الحجم:** 1-2 post/يوم
+**التحويل المتوقع:** 5 inbound leads/أسبوع بعد أسبوعين
+**الإيراد المتوقع:** 5 × 10% = 0.5 عميل/أسبوع بعد شهر
+
+**التنفيذ:**
+- **يوم 1:** "أطلقنا Dealix — مندوب مبيعات AI بالعربي"
+- **يوم 2:** "73% من العملاء السعوديين يتوقعون رد خلال 30 دقيقة"
+- **يوم 3:** "كيف تحوّل وكالتك إلى revenue machine"
+- **يوم 4:** "3 أيام من الإطلاق — وش تعلمنا"
+- **يوم 5:** "عرض pilot بريال واحد — أول 5 شركات"
+- **يوم 6:** "ليش الوكالات هي مستقبل SaaS في السعودية"
+- **يوم 7:** "تقرير: Saudi AI GTM 2026"
+
+---
+
+### القناة 6: Community/Group Posts (warm leads)
+**الأداة:** WhatsApp/Telegram groups + Reddit r/saudiarabia + LinkedIn groups
+**الحجم:** 3-5 posts/أسبوع في مجموعات مختلفة
+**التحويل المتوقع:** 2-3 leads/أسبوع
+
+**التنفيذ:**
+- ابحث عن مجموعات: "رواد أعمال سعوديين"، "Saudi Startups"، "SaaS MENA"
+- انشر قيمة أولاً (مشكلة + حل) — لا تبيع مباشرة
+- مثال: "سؤال للمؤسسين: كيف تتعاملون مع leads تجي بالليل؟ نشتغل على حل AI بالعربي — أبي أسمع تجاربكم"
+
+---
+
+### القناة 7: Referral Program (أقل جهد)
+**الأداة:** رسالة واتساب + tracking يدوي
+**الحجم:** 5 referral asks/أسبوع
+**التحويل المتوقع:** 20% يعطون intro
+
+**التنفيذ:**
+- بعد كل محادثة إيجابية (حتى لو ما شرى): "تعرف أحد ممكن يستفيد من ديلكس؟ 10% من أول سنة لك"
+- رسالة Referral:
+ "هل تعرف founder سعودي عنده leads كثيرة ومتابعة ضعيفة؟ أعطني اسمه وأنا أتواصل معه. لو اشترك = 10% من MRR لك لمدة سنة."
+
+---
+
+### القناة 8: Paid Ads (بعد إثبات المنتج)
+**متى تبدأ:** بعد أول 5 عملاء مدفوعين
+**الأداة:** LinkedIn Ads + Google Ads
+**الميزانية:** 2,000-5,000 ريال/شهر
+**المتوقع:** 10-20 lead/أسبوع × 5% = 1 عميل/أسبوع
+
+---
+
+## الجدول اليومي للإيراد
+
+```
+07:00 قراءة الردود + تصنيف (reply classifier)
+08:00 LinkedIn: 10 DMs (Sales Navigator)
+09:00 Email: 30 cold emails (Instantly.ai)
+10:00 WhatsApp: 5 warm messages
+10:30 Demo #1
+11:30 Demo #2
+12:00 Follow-ups: كل الردود
+13:00 استراحة
+14:00 Partner outreach: 3 وكالات
+15:00 Demo #3
+16:00 Content: كتابة + نشر 1-2 posts
+17:00 Community + Referral asks
+18:00 Scorecard update + تحضير الغد
+20:00 ردود متأخرة
+```
+
+---
+
+## Conversion Funnel المستهدف
+
+```
+Week 1: 254 touch → 25 reply → 3 demo → 1 pilot → 0 paid
+Week 2: 490 touch → 50 reply → 8 demo → 3 pilot → 1 paid (990 SAR)
+Week 3: 490 touch → 70 reply → 10 demo → 5 pilot → 3 paid (2,970 SAR)
+Week 4: 490 touch → 100 reply → 10 demo → 5 pilot → 6 paid (5,940 SAR)
+Month 2: target 30 paid = 29,700 SAR MRR
+Month 3: target 60 paid + 3 agencies = 59,400 + 15,000 = 74,400 SAR
+```
+
+---
+
+## الأدوات المطلوبة (إجمالي: 149 ريال/شهر)
+
+| الأداة | الغرض | التكلفة |
+|--------|--------|---------|
+| LinkedIn Sales Navigator | بحث + InMail | $80 = 300 ريال |
+| Instantly.ai | cold email automation | $30 = 112 ريال |
+| Calendly | حجز مواعيد | مجاني |
+| WhatsApp Business | warm outreach | مجاني |
+| Google Sheets | CRM يدوي | مجاني |
+| Loom | تسجيل demo | مجاني |
+| PostHog | analytics | مجاني |
+| Hunter.io | email finder | مجاني (50/شهر) |
+
+---
+
+## Pricing Matrix
+
+| الخطة | السعر | المناسب لـ |
+|-------|-------|-----------|
+| Pilot | 1 ريال / 7 أيام | إثبات القيمة |
+| Starter | 990 ريال/شهر | شركات صغيرة (500 lead/شهر) |
+| Growth | 2,490 ريال/شهر | شركات متوسطة (2000 lead/شهر) |
+| Enterprise | مخصص | شركات كبرى |
+| Agency Setup | 3,000-15,000 ريال | وكالات (لكل عميل) |
+| Agency MRR | 20-30% من MRR | وكالات (متكرر) |
+
+---
+
+## Payment Methods (كلها جاهزة)
+
+| الطريقة | الاستخدام | الحالة |
+|---------|----------|--------|
+| تحويل بنكي | أي مبلغ | جاهز |
+| STC Pay | سريع | جاهز |
+| فاتورة يدوية | رسمي | جاهز |
+| Moyasar | تلقائي | ينتظر KYC |
+
+---
+
+## Red Flags (توقف عن الاستهداف لو)
+
+- الشخص قال "إيقاف" أو "لا" صريحة → أوقف فوراً
+- أرسلت 3 follow-ups بدون رد → أوقف
+- القطاع ما فيه leads (مثال: حكومي صرف) → تجاوز
+- الشخص طلب NDA قبل demo → red flag (يبي يسرق الفكرة)
+
+## Green Flags (ضاعف الجهد لو)
+
+- "كم السعر؟" → جاهز يشتري
+- "عندنا 500+ lead/شهر" → high volume = high value
+- "ممكن تجربة؟" → pilot ready
+- "عندي وكالة" → partner ready
+- "أعرف ناس ممكن يستفيدون" → referral goldmine
diff --git a/salesflow-saas/docs/ops/DEMO_BOOKING_RUNBOOK.md b/salesflow-saas/docs/ops/DEMO_BOOKING_RUNBOOK.md
new file mode 100644
index 00000000..73bc5e68
--- /dev/null
+++ b/salesflow-saas/docs/ops/DEMO_BOOKING_RUNBOOK.md
@@ -0,0 +1,77 @@
+# Dealix — Demo Booking Runbook
+
+## How to Book First Demo
+1. Prospect responds positively to outreach message
+2. Send Calendly link: `calendly.com/sami-assiri11/dealix-demo`
+3. Or propose 2 specific times: "يناسبك بكرة 11 ص أو 3 م؟"
+4. Calendly sends confirmation to both parties
+
+## Demo Preparation (30 min before)
+- [ ] Open prospect's website in a tab
+- [ ] Run `POST /api/v1/prospect/enrich-tech` with their domain — note what tools they use
+- [ ] Run `POST /api/v1/prospect/route` with their company/sector — note classification
+- [ ] Prepare 1 sector-specific pain point to mention
+- [ ] Have Calendly open for follow-up booking if needed
+- [ ] Have pilot offer ready (499 SAR / 7 days)
+
+## Demo Flow (20 minutes)
+
+**0:00-2:00 — Context Check**
+- "شكراً على وقتك. قبل ما نبدأ — وش أكبر تحدي عندكم في متابعة الـ leads حالياً؟"
+- Listen. Take note.
+
+**2:00-5:00 — Discovery (3 questions)**
+1. "كم lead تقريباً تستقبلون شهرياً؟"
+2. "كم منهم يُتابع خلال أول ساعة؟"
+3. "إيش يصير لـ leads اللي ما يُرد عليهم بسرعة؟"
+
+**5:00-12:00 — Show Dealix**
+- Show pricing/plans API response (real, live)
+- Show enrich-tech on their domain (real detection)
+- Show prospect/route classification
+- Show prospect/message with their company name → generated Arabic message
+- Explain: "هذا يصير تلقائي لكل lead يجيكم"
+
+**12:00-16:00 — ROI Discussion**
+- "لو عندكم [X] lead/شهر، و30% تضيع، يعني [Y] فرصة ضائعة"
+- "Dealix يرد خلال 45 ثانية — كم من هذول ممكن تنحفظ؟"
+- Do not guarantee numbers. Say "نقيسها معكم خلال 7 أيام"
+
+**16:00-18:00 — Handle Objections**
+- Price: "نبدأ بـ pilot 7 أيام بـ 499 ريال — لو ما شفت قيمة، استرداد كامل"
+- CRM: "Dealix يشتغل فوق CRM الحالي — ما يستبدله"
+- Arabic: "نبدأ manual + AI-assisted — نراجع أول الردود معك"
+- Privacy: "متوافق PDPL — بيانات كل عميل منفصلة"
+
+**18:00-20:00 — Close**
+- Strong: "نبدأ الـ pilot الأسبوع الجاي؟"
+- Medium: "أرسل لك تفاصيل الـ pilot وتقرر؟"
+- Soft: "فكّر فيها — أتابع معك بكرة"
+
+## After Demo (same day)
+- [ ] Update tracker: demo_done = yes
+- [ ] Send follow-up message:
+ ```
+ شكراً على وقتك اليوم [الاسم].
+
+ ملخص: نقدر نجرب Dealix على [X] leads عندكم لمدة 7 أيام.
+ الهدف: نقيس سرعة الرد وعدد المؤهلين والمواعيد المحجوزة.
+
+ التكلفة: 499 ريال — مع ضمان استرداد.
+
+ جاهز نبدأ؟
+ ```
+- [ ] If yes → send payment request (bank/STC Pay)
+- [ ] If "need time" → schedule follow-up in 48h
+- [ ] If no → ask "هل تعرف أحد ممكن يستفيد؟"
+
+## What Qualifies a Successful Demo
+- Prospect asks about pricing or pilot → high intent
+- Prospect says "أبي أجرب" → convert immediately
+- Prospect asks technical questions → engaged (answer, don't oversell)
+- Prospect goes quiet → follow up in 24h, not immediately
+
+## Recording
+- Use Loom or OBS to record screen during demo (ask permission first)
+- Save recording for internal review
+- Do not share recording externally without consent
diff --git a/salesflow-saas/docs/ops/FIRST_5_OUTREACH.md b/salesflow-saas/docs/ops/FIRST_5_OUTREACH.md
new file mode 100644
index 00000000..69d46f71
--- /dev/null
+++ b/salesflow-saas/docs/ops/FIRST_5_OUTREACH.md
@@ -0,0 +1,133 @@
+# Dealix — First 5 Outreach Messages
+
+**Channel:** WhatsApp warm (people you know) + LinkedIn (professional contacts)
+**Rules:** No spam. No cold WhatsApp blast. Personalized. Professional. Opt-out included.
+
+---
+
+## Message 1 — SaaS Founder (warm contact)
+
+**Target:** صديق/معرفة يملك شركة SaaS سعودية
+**Pain:** leads تجي من الموقع وتبرد بسبب تأخر الرد
+
+```
+السلام عليكم [الاسم]،
+
+أطلقت Dealix — نظام يرد على leads شركتك بالعربي
+خلال 45 ثانية، يؤهلهم، ويحجز مواعيد تلقائياً.
+
+أبحث عن 3 أصدقاء لتجربة pilot 7 أيام بـ 499 ريال.
+فكرت فيك لأن [الشركة] عندها leads من الموقع.
+
+مهتم نتكلم 10 دقائق؟
+```
+
+**Follow-up 24h:**
+```
+سامي هنا — تابع على رسالة أمس عن Dealix.
+باختصار: pilot 7 أيام على leads عندكم. يناسبك؟
+```
+
+**Follow-up 72h:**
+```
+آخر متابعة — لو مو الوقت المناسب أفهم تماماً.
+لو بالمستقبل يناسب، أنا موجود. شكراً [الاسم].
+```
+
+---
+
+## Message 2 — Agency Owner
+
+**Target:** صاحب وكالة تسويق رقمي
+**Pain:** عملاءه يجيبون leads بالإعلانات لكن المتابعة ضعيفة
+
+```
+السلام عليكم [الاسم]،
+
+أشتغل على Dealix — طبقة AI بالعربي ترد على leads عملاء
+الوكالات وتأهلهم وتحجز مواعيد.
+
+الفكرة لكم: تبيعونها كـ setup + retainer. العميل يشوف
+ROI أسرع — أنتم تزيدون MRR.
+
+نبدأ بـ pilot مجاني لأول عميل عندكم.
+20 دقيقة أشرح الآلية؟
+calendly.com/sami-assiri11/dealix-demo
+```
+
+---
+
+## Message 3 — Real Estate Company
+
+**Target:** شركة عقارية أو مطوّر
+**Pain:** استفسارات كثيرة عن الأسعار والمواقع تضيع
+
+```
+السلام عليكم [الاسم]،
+
+لاحظت إن شركات العقار تستقبل عشرات الاستفسارات يومياً
+عن الأسعار والمواقع — وكثير منها تضيع بسبب تأخر الرد.
+
+Dealix يرد خلال 45 ثانية بالعربي، يسأل عن الميزانية
+والموقع المفضل، ويحجز معاينة تلقائياً.
+
+عندنا تجربة 7 أيام بـ 499 ريال. يناسبك أوريك مثال؟
+
+إذا ما يناسبكم، قولوا "إيقاف" ولن أتواصل مرة ثانية.
+```
+
+---
+
+## Message 4 — B2B Services (Logistics/Construction)
+
+**Target:** شركة خدمات B2B (شحن، مقاولات)
+**Pain:** طلبات عروض أسعار متكررة تحتاج فرز
+
+```
+السلام عليكم [الاسم]،
+
+في قطاع [المقاولات/الشحن]، طلبات عروض الأسعار تجي
+كثيرة — لكن فرز الجاد من غيره ياخذ وقت.
+
+Dealix يستقبل الطلب بالعربي، يسأل عن نوع المشروع
+والميزانية والجدول الزمني، ويصنّف الجدية قبل ما يوصل لفريقكم.
+
+تجربة 7 أيام بـ 499 ريال. مهتم بمثال مبني على نشاطكم؟
+
+لو ما يناسبكم: "إيقاف".
+```
+
+---
+
+## Message 5 — Warm Referral Ask
+
+**Target:** أي معرفة لها شبكة في الأعمال السعودية
+**Pain:** N/A — you're asking for an intro
+
+```
+السلام عليكم [الاسم]،
+
+أطلقت مشروع Dealix — نظام يساعد الشركات السعودية
+ترد على استفسارات العملاء بسرعة وتأهلهم بالعربي.
+
+سؤال سريع: تعرف أحد عنده شركة (عقار، وكالة تسويق،
+خدمات B2B) وعنده leads كثيرة ومتابعة ضعيفة؟
+
+لو تعرّفني عليه — 10% من أول سنة اشتراكه لك.
+
+شكراً مقدماً!
+```
+
+---
+
+## Lead Tracker (first 5)
+
+| # | الاسم | الشركة | القطاع | المصدر | الرسالة | مرسلة؟ | الرد | Demo؟ | التالي |
+|---|-------|--------|--------|--------|---------|--------|------|-------|--------|
+| 1 | | | SaaS | warm | Msg 1 | | | | |
+| 2 | | | Agency | warm | Msg 2 | | | | |
+| 3 | | | Real Estate | cold/warm | Msg 3 | | | | |
+| 4 | | | B2B Services | cold/warm | Msg 4 | | | | |
+| 5 | | | Referral | warm | Msg 5 | | | | |
+
+**Sami fills in names + sends → marks "مرسلة" → updates "الرد" when reply arrives.**
diff --git a/salesflow-saas/docs/ops/FULL_OPS_LAUNCH_RUNBOOK.md b/salesflow-saas/docs/ops/FULL_OPS_LAUNCH_RUNBOOK.md
new file mode 100644
index 00000000..0d936a7b
--- /dev/null
+++ b/salesflow-saas/docs/ops/FULL_OPS_LAUNCH_RUNBOOK.md
@@ -0,0 +1,168 @@
+# Dealix — Full Ops Launch Runbook
+
+**Version:** 1.0.0
+**Date:** 2026-04-25
+
+---
+
+## Launch Status
+
+| System | Status | Evidence |
+|--------|--------|---------|
+| Backend (Railway) | LIVE | healthz=200, pricing=200, route/message/enrich=200 |
+| Landing (dealix.me) | LIVE | Returns 200 |
+| Frontend (Next.js) | DEPLOYED | Marketers page rewritten as sales page |
+| Manual Payment | READY | Bank transfer + STC Pay documented |
+| Automated Payment | BLOCKED | Moyasar returns 502 |
+| Outreach Templates | READY | 5 messages + 60 targets + automation endpoints |
+| Customer Onboarding | READY | Pilot template + success playbook |
+| Monitoring | PARTIAL | GitHub Actions healthcheck; Sentry not configured |
+
+## What Is Live
+- `/healthz` → 200
+- `/api/v1/pricing/plans` → 3 plans in SAR
+- `/api/v1/prospect/route` → rules-based lead classification
+- `/api/v1/prospect/message` → Arabic message generation
+- `/api/v1/prospect/enrich-tech` → real tech stack detection
+- `/api/v1/automation/email/generate` → personalized email + follow-ups
+- `/api/v1/automation/compliance/check` → compliance gate
+- `/api/v1/automation/reply/classify` → 12-category reply handler
+- `dealix.me` → landing page
+- `trial-signup.html` → lead capture form
+
+## What Is Not Live
+- LLM personalization (needs GROQ_API_KEY)
+- Web discovery (needs GOOGLE_SEARCH_API_KEY)
+- PostHog funnel tracking (needs POSTHOG_API_KEY)
+- Sentry error alerting (needs SENTRY_DSN)
+- Moyasar automated checkout (returns 502)
+- HubSpot CRM sync (needs HUBSPOT_API_KEY)
+- Gmail automated send (needs OAuth setup)
+
+## Manual Blockers (Sami-Only)
+
+| Blocker | Action | Time | Priority |
+|---------|--------|------|----------|
+| GROQ_API_KEY | Add in Railway env Dealix/web | 3 min | P0 |
+| GOOGLE_SEARCH_API_KEY + CX | Add in Railway | 5 min | P0 |
+| SENTRY_DSN | Add in Railway | 3 min | P0 |
+| POSTHOG_API_KEY + HOST | Add in Railway | 3 min | P0 |
+| Send first 5 messages | WhatsApp + LinkedIn | 30 min | P0 |
+| Book first demo | Calendly | Depends on replies | P0 |
+| Test Moyasar key | curl from terminal | 5 min | P1 |
+| Rollback drill | SSH to Hetzner Console | 15 min | P1 |
+| DB restore drill | SSH to Hetzner Console | 15 min | P1 |
+
+---
+
+## Daily Checklist (until first 3 paid clients)
+
+### Morning (08:00)
+- [ ] Check Railway healthz → 200?
+- [ ] Check Sentry for overnight errors (if configured)
+- [ ] Review replies from yesterday
+- [ ] Classify replies (use /api/v1/automation/reply/classify)
+- [ ] Select today's 5-10 outreach targets
+
+### Midday (12:00)
+- [ ] Send 5 outreach messages (WhatsApp warm + LinkedIn)
+- [ ] Follow up on Day +2 / +5 leads
+- [ ] Respond to all positive replies within 2 hours
+- [ ] Book demos for interested prospects
+
+### Afternoon (16:00)
+- [ ] Run demos (if scheduled)
+- [ ] Send pilot offers to demo'd prospects
+- [ ] Publish 1 LinkedIn/X post
+
+### Evening (20:00)
+- [ ] Update lead tracker
+- [ ] Count: sent / replied / demo'd / offered / paid
+- [ ] Plan tomorrow's targets
+- [ ] Handle late replies
+
+---
+
+## Payment Test Checklist (for Sami)
+
+### Manual Payment Test
+1. Ask a friend to transfer 1 SAR via bank or STC Pay
+2. Verify receipt in bank app
+3. Mark test as passed
+4. This is your production payment path until Moyasar works
+
+### Moyasar Diagnostic
+1. Open [dashboard.moyasar.com](https://dashboard.moyasar.com)
+2. Check Account Status (Active? Pending? Suspended?)
+3. Check API Keys → is `sk_live_` the current key?
+4. Test from terminal:
+ ```
+ curl -u "sk_live_YOUR_KEY:" https://api.moyasar.com/v1/invoices \
+ -d "amount=100" -d "currency=SAR"
+ ```
+5. If "authentication_error" → key mismatch, regenerate
+6. If "account_inactive" → complete KYC
+7. If success → Railway env has whitespace, re-paste carefully
+
+---
+
+## Outreach Checklist
+- [ ] 5 warm WhatsApp messages sent (from FIRST_5_OUTREACH.md)
+- [ ] 1 LinkedIn founder post published
+- [ ] 1 LinkedIn DM to agency partner
+- [ ] All follow-ups current (no lead older than 3 days without follow-up)
+- [ ] Tracker updated with today's activity
+
+## Demo Checklist
+- [ ] Prospect's website reviewed
+- [ ] enrich-tech run on their domain
+- [ ] 20-minute demo completed per DEMO_BOOKING_RUNBOOK.md
+- [ ] Follow-up sent same day
+- [ ] Pilot offer sent if positive
+
+## Rollback Checklist (if something breaks)
+1. Railway → Deployments → click previous green deployment → Redeploy
+2. If Railway is down: Hetzner Console → `systemctl restart dealix-api`
+3. If DB corrupted: restore from Railway Postgres auto-backup
+4. If code breaks: `git revert HEAD && git push origin main`
+
+## DB Restore Checklist
+1. Railway Postgres has automatic daily backups
+2. Railway → Postgres service → Backups → Restore
+3. After restore: verify `/api/v1/dashboard/metrics` returns data
+
+## Incident Checklist
+1. Check Railway logs → identify error
+2. If 502/503: restart deployment
+3. If code bug: revert last commit
+4. If env issue: check Variables tab
+5. If DNS: check GoDaddy/Cloudflare
+6. Notify any active pilot clients if downtime > 30 minutes
+
+---
+
+## Do Not Touch List
+- Do NOT add new LLM providers
+- Do NOT rebuild dashboard
+- Do NOT start v3.1
+- Do NOT implement Temporal/LangGraph/Qdrant
+- Do NOT build voice/webchat/mobile
+- Do NOT expand to UAE/Egypt
+- Do NOT hire before 3 paid clients
+- Do NOT spend on ads before manual outreach proves conversion
+
+---
+
+## Definition of Launch-Ready
+All of these must be true:
+- [ ] Railway healthz=200
+- [ ] 4 env keys added (GROQ, GOOGLE, SENTRY, POSTHOG)
+- [ ] 5+ outreach messages sent
+- [ ] 1+ demo booked
+- [ ] Manual payment path tested (1 SAR friend test)
+
+## Definition of Revenue-Live
+All of the above, plus:
+- [ ] 1+ pilot payment received (499 SAR)
+- [ ] 1+ customer onboarded
+- [ ] 1+ daily report sent to customer
diff --git a/salesflow-saas/docs/ops/MARKETERS_PAGE_PLAN.md b/salesflow-saas/docs/ops/MARKETERS_PAGE_PLAN.md
new file mode 100644
index 00000000..7ced2579
--- /dev/null
+++ b/salesflow-saas/docs/ops/MARKETERS_PAGE_PLAN.md
@@ -0,0 +1,140 @@
+# Dealix — Marketers Page: Service Sales Specification
+
+**Purpose:** Convert `/marketers` from a link hub (current: 131 lines, 8 links) into a page that sells Dealix as a service marketers resell to their clients.
+
+---
+
+## Positioning
+
+**Core message:** Dealix gives marketers a new revenue line — AI-powered lead response they sell as a service to their clients.
+
+**Not:** "Here are resources for marketers"
+**Instead:** "Here is a service you sell to your clients and earn recurring revenue"
+
+**One-liner:** "أضف خدمة رد ذكي + تأهيل leads لعملائك — setup fee + MRR شهري متكرر"
+
+---
+
+## Target Segments
+
+| Segment | Size | Pain | What They Buy |
+|---------|------|------|---------------|
+| **Performance agencies** (5-30 people) | ~200 in Saudi | Run ads, generate leads, but leads go cold — client blames the agency | A service they add on top of ads |
+| **Full-service digital agencies** (10-50) | ~100 | Deliver websites + ads but no post-lead follow-up — churn risk | Retention tool for their clients |
+| **Freelance marketers** | ~500+ | Want recurring income beyond project fees | Low-effort MRR add-on |
+| **CRM/automation consultants** | ~50 | Implement HubSpot/Zoho but client doesn't use it well | Lead response layer they manage |
+| **WhatsApp marketing specialists** | ~100 | Know WhatsApp works for Saudi but can't scale responses | AI response they configure |
+
+---
+
+## Page Sections (top to bottom)
+
+### 1. Hero
+- **Headline AR:** "حوّل وكالتك إلى ماكينة إيرادات متكررة"
+- **Subheadline:** "أضف خدمة الرد الذكي على leads عملائك — setup fee لك + MRR شهري متكرر"
+- **Primary CTA:** "احجز مكالمة شراكة" → calendly.com/sami-assiri11/dealix-demo
+- **Secondary CTA:** "شوف الباقات" → scroll to packages
+
+### 2. Problem Statement
+"عملاءك يصرفون 10,000+ ريال/شهر على إعلانات ويجيبون leads.
+لكن 70% من الـ leads تضيع لأن:
+- ما أحد يرد خلال أول ساعة
+- المتابعة يدوية وعشوائية
+- فريق العميل مشغول
+
+العميل يلوم الوكالة. الحقيقة: المشكلة مو في الإعلان — المشكلة في الرد."
+
+### 3. Solution
+"Dealix يعطيك الحل تبيعه لعملائك:
+- AI يرد بالعربي خلال 45 ثانية على كل lead
+- يسأل أسئلة التأهيل (ميزانية؟ جدية؟ موعد؟)
+- يحجز اجتماع أو يحوّل للمبيعات
+- يرسل تقرير يومي
+
+أنت تبيع الخدمة. نحن نشغّل النظام. تحصّل setup fee + نسبة شهرية."
+
+### 4. Packages Marketers Can Sell
+
+| الباقة | Setup Fee (لك) | شهري (العميل يدفع) | نصيبك الشهري | ما يحصل العميل |
+|--------|---------------|-------------------|-------------|---------------|
+| **الأساسية** | 3,000 SAR | 990 SAR | 297 SAR (30%) | رد AI على واتساب + تأهيل + تقرير أسبوعي |
+| **المتقدمة** | 7,000 SAR | 2,490 SAR | 622 SAR (25%) | + حجز مواعيد + CRM sync + follow-up |
+| **المؤسسية** | 15,000 SAR | مخصص | 20-30% | + تقارير executive + تكامل كامل |
+
+### 5. Workflows Marketers Can Run
+
+**Workflow 1 — Performance Agency:**
+1. تشغّل حملة إعلانية لعميل
+2. Leads تنزل → Dealix يرد فوراً
+3. Leads المؤهلة تتحول لمواعيد
+4. العميل يشوف ROI أعلى → يجدد → أنت تحصّل MRR
+
+**Workflow 2 — Freelance Marketer:**
+1. تقدم عرض "إدارة حملات + رد ذكي"
+2. تسعّر الخدمة كاملة (حملة + Dealix)
+3. Dealix يشتغل بالخلفية
+4. أنت تركز على الاستراتيجية والمحتوى
+
+**Workflow 3 — CRM Consultant:**
+1. تنفذ HubSpot/Zoho لعميل
+2. تضيف Dealix كطبقة response
+3. CRM يمتلئ بـ leads مؤهلة تلقائياً
+4. العميل يشوف قيمة CRM أسرع → retention
+
+### 6. Agency Use Cases
+
+| القطاع | المشكلة | Dealix يحل |
+|--------|---------|-----------|
+| عقارات | 500+ استفسار/شهر، رد بعد ساعات | رد فوري + تأهيل الميزانية + حجز معاينة |
+| مقاولات | طلبات عروض أسعار تحتاج فرز | فرز + تصنيف الجدية قبل تحويل للمهندسين |
+| فنادق/قاعات | استفسارات حجز يومية | رد + تأهيل التاريخ والعدد + حجز مبدئي |
+| عيادات | مرضى يسألون عن مواعيد وأسعار | رد تلقائي + حجز موعد |
+| تجارة إلكترونية | رسائل واتساب عن المنتجات | رد + توجيه للشراء |
+
+### 7. Freelancer Use Cases
+
+| السيناريو | كيف تربح |
+|-----------|---------|
+| "أنا أسوي سوشيال ميديا لـ 5 عملاء" | أضف Dealix لكل عميل = 5 × 990 = 4,950 SAR/شهر إضافي |
+| "أنا مستشار CRM" | أضف Dealix كخدمة after-implementation = retainer شهري |
+| "أنا أدير إعلانات" | أضف "الرد الذكي" كـ add-on = يميّزك عن المنافسين |
+
+### 8. FAQs
+
+| السؤال | الجواب |
+|--------|--------|
+| كم ياخذ الإعداد لكل عميل؟ | يوم واحد للأساسية، 3 أيام للمتقدمة |
+| هل أحتاج خبرة تقنية؟ | لا — نحن نسوي الإعداد والتشغيل |
+| كم أقدر أربح؟ | عميل واحد = 3,000 setup + ~990/شهر = 14,880 SAR/سنة |
+| هل فيه عقد طويل؟ | لا — شهري لك ولعميلك |
+| ماذا لو العميل ما عجبه؟ | 30 يوم ضمان استرداد |
+| هل يشتغل مع HubSpot/Zoho؟ | نعم — CRM sync متاح |
+| هل يفهم العربي السعودي؟ | نعم — مبني Arabic-first |
+| كم عميل أقدر أضيف؟ | بلا حدود — كل عميل بباقته |
+| هل بياناتي وبيانات عملائي آمنة؟ | نعم — PDPL compliant + بيانات منفصلة لكل عميل |
+| كيف أبدأ؟ | احجز مكالمة شراكة 20 دقيقة → نسوي pilot مجاني لأول عميل |
+
+### 9. Proof/Trust Blocks
+- "مبني للسوق السعودي — عربي أولاً"
+- "متوافق مع نظام حماية البيانات الشخصية (PDPL)"
+- "بيانات كل عميل منفصلة تماماً"
+- "ضمان استرداد 30 يوم"
+- "بدون عقد سنوي — شهري"
+- Dealix logo + dealix.me
+
+### 10. Final CTA
+"ابدأ بأول عميل — مجاناً"
+"نسوي pilot 7 أيام لأول عميل عندك بدون تكلفة. لو اشتغل → تبدأ تحصّل."
+→ calendly.com/sami-assiri11/dealix-demo
+
+---
+
+## Conversion Goals
+- Primary: Partner books Calendly call
+- Secondary: Partner submits contact form
+- Tertiary: Partner shares page with colleague
+
+## Implementation
+- File: `frontend/src/app/marketers/page.tsx`
+- No new dependencies — uses existing Tailwind + lucide-react
+- Arabic RTL primary, English secondary where needed
diff --git a/salesflow-saas/docs/ops/NEXT_24H_7D_30D.md b/salesflow-saas/docs/ops/NEXT_24H_7D_30D.md
new file mode 100644
index 00000000..3c19adb6
--- /dev/null
+++ b/salesflow-saas/docs/ops/NEXT_24H_7D_30D.md
@@ -0,0 +1,81 @@
+# Dealix — Execution Plan: 24h / 7d / 30d
+
+**Starting state:** 28/39 gates closed. Product live. 0 revenue. 0 messages sent.
+
+---
+
+## Next 24 Hours
+
+| # | Deliverable | Owner | Dependency | Definition of Done | Metric | Risk |
+|---|-------------|-------|------------|-------------------|--------|------|
+| 1 | Add GROQ_API_KEY in Railway env Dealix/web | Sami | console.groq.com (free) | `/api/v1/prospect/search-diag` shows GROQ set | LLM features activate | Key in wrong env (use Dealix, not Agent branch) |
+| 2 | Add GOOGLE_SEARCH_API_KEY + CX in Railway | Sami | Google Cloud Console | `/api/v1/prospect/search-diag` shows Google set | Web discovery active | Needs billing account for Maps |
+| 3 | Add SENTRY_DSN in Railway | Sami | sentry.io free plan | Sentry receives test error | Error alerting active | None |
+| 4 | Send 5 WhatsApp warm messages | Sami | Personal contacts who own businesses | 5 conversations started | pipeline > 0 | Low reply rate from cold — use warm only |
+| 5 | Publish Founder Launch Post on LinkedIn | Sami | LinkedIn account | Post visible + URL captured | Inbound starts | None |
+| 6 | Send 1 LinkedIn DM to agency partner | Sami | LinkedIn | Connection request sent | Partner pipeline > 0 | May take days for acceptance |
+
+**24h success = 3 keys added + 5 messages sent + 1 post published.**
+
+---
+
+## Next 7 Days
+
+| # | Deliverable | Owner | Dependency | Definition of Done | Metric | Risk |
+|---|-------------|-------|------------|-------------------|--------|------|
+| 1 | 30+ outreach messages across channels | Sami | Templates in COMMAND_CENTER | Messages logged in tracker | 30 touches | Fatigue — batch 5/day |
+| 2 | First demo booked via Calendly | Sami | At least 1 positive reply | Calendly notification received | 1 demo | Low conversion — try warm contacts first |
+| 3 | First pilot offered (499 SAR) | Sami | Completed demo | Payment request sent | 1 offer | Prospect may need time |
+| 4 | First partner contacted | Sami | AGENCY_PARTNER_OFFER.md | Agency responds | 1 partner conversation | Agency may want proof first |
+| 5 | Moyasar diagnostic completed | Sami | Moyasar dashboard access | Know if live key works or needs fix | Unblocks automated payment | KYC may take days |
+| 6 | PostHog receives 1+ event | Sami | POSTHOG_API_KEY added | Event visible in PostHog dashboard | Funnel measurement starts | Free plan sufficient |
+| 7 | 3 LinkedIn posts published | Sami | Content from COMMAND_CENTER | Posts visible | Inbound pipeline | Consistency matters more than perfection |
+
+**Week 1 success = 1 demo + 1 pilot offer + 3 posts + partner motion started.**
+
+---
+
+## Next 30 Days
+
+| # | Deliverable | Owner | Dependency | Definition of Done | Metric | Risk |
+|---|-------------|-------|------------|-------------------|--------|------|
+| 1 | 200+ outreach touches total | Sami | Sustained daily activity | Tracker shows 200+ rows | Volume | Burnout — keep to 10/day |
+| 2 | 5 demos completed | Sami | Positive replies | Calendly shows 5 past events | Demo rate | Need warm + cold mix |
+| 3 | 3 pilot payments received | Sami | Completed demos | Bank/STC Pay proof | 1,497 SAR MRR | Conversion uncertainty |
+| 4 | 1 agency partner active | Sami | Partner signs + brings client | Partner's client in pilot | Partner channel open | Agencies want proof |
+| 5 | Marketers page rewritten | Claude | MARKETERS_PAGE_PLAN.md | Page sells services, not just links | Page conversion | TSX rewrite needed |
+| 6 | Moyasar live checkout working | Sami | KYC + key fix | 1 SAR test transaction succeeds | Automated payment | May need new key |
+| 7 | Case study draft from first client | Sami | First pilot completed | 1-page results document | Social proof | Client may not agree to name |
+| 8 | Rollback drill completed | Claude/Sami | SSH access to server | Documented rollback in < 5 min | Recovery confidence | SSH currently blocked |
+| 9 | 10 LinkedIn posts total | Sami | Weekly posting cadence | Posts visible | Brand building | Consistency > perfection |
+| 10 | PostHog funnel visible | Sami/Claude | POSTHOG key + landing events | landing → signup → demo → paid visible | Data-driven decisions | Free plan limits |
+
+**Month 1 success = 3 paid pilots (1,497 SAR) + 1 partner + funnel visible + case study draft.**
+
+---
+
+## Revenue Trajectory (conservative)
+
+| Week | Touches | Replies | Demos | Pilots | Paid | MRR (SAR) |
+|------|---------|---------|-------|--------|------|-----------|
+| 1 | 30 | 3 | 1 | 0 | 0 | 0 |
+| 2 | 60 | 8 | 2 | 1 | 0 | 0 |
+| 3 | 90 | 12 | 3 | 2 | 1 | 499 |
+| 4 | 120 | 15 | 5 | 3 | 3 | 1,497 |
+| **Total** | **300** | **38** | **11** | **6** | **3** | **1,497** |
+
+Assumes: 13% reply rate, 29% demo-from-reply rate, 55% pilot-from-demo, 50% paid-from-pilot.
+
+---
+
+## What NOT to Do in 30 Days
+
+- Do NOT rebuild the dashboard
+- Do NOT add new LLM providers
+- Do NOT implement Temporal/LangGraph/Qdrant
+- Do NOT build voice receptionist or webchat
+- Do NOT start v3.1
+- Do NOT expand to UAE/Egypt
+- Do NOT build mobile app
+- Do NOT hire before first 3 paid clients
+- Do NOT spend on ads before proving manual outreach converts
diff --git a/salesflow-saas/docs/ops/RAILWAY_ENV_KEYS.md b/salesflow-saas/docs/ops/RAILWAY_ENV_KEYS.md
new file mode 100644
index 00000000..469b2fbf
--- /dev/null
+++ b/salesflow-saas/docs/ops/RAILWAY_ENV_KEYS.md
@@ -0,0 +1,55 @@
+# Dealix — Railway Environment Keys
+
+**Where to add:** Railway → Project → Service "web" → Variables tab → Review → Deploy
+
+---
+
+## P0 — Required for Launch
+
+| Variable | Source | Cost | Effect When Missing | Verification |
+|----------|--------|------|-------------------|-------------|
+| `GROQ_API_KEY` | [console.groq.com/keys](https://console.groq.com/keys) | Free tier available | LLM features degraded (rules-only) | `curl /api/v1/prospect/search-diag` shows `GROQ_API_KEY.set: true` |
+| `GOOGLE_SEARCH_API_KEY` | [Google Cloud Console → Credentials](https://console.cloud.google.com/apis/credentials) | 100 queries/day free | `/prospect/search` returns 503 | search-diag shows key set |
+| `GOOGLE_SEARCH_CX` | Fixed value: `75ae2277dfd754a1a` | Free | Same as above | search-diag shows CX set |
+| `SENTRY_DSN` | [sentry.io](https://sentry.io) → Create Python project → copy DSN | Free plan | No error alerting | Sentry receives test error |
+| `POSTHOG_API_KEY` | [posthog.com](https://posthog.com) → Project Settings → API Key | Free plan | No funnel tracking | PostHog dashboard shows events |
+| `POSTHOG_HOST` | `https://us.i.posthog.com` or `https://eu.i.posthog.com` | — | PostHog events go nowhere | Same as above |
+
+## P1 — Required for Automated Payment
+
+| Variable | Source | Cost | Effect When Missing |
+|----------|--------|------|-------------------|
+| `MOYASAR_SECRET_KEY` | [dashboard.moyasar.com](https://dashboard.moyasar.com) → API Keys | Per-transaction fee | Checkout returns 502 |
+| `MOYASAR_PUBLISHABLE_KEY` | Same dashboard | — | Frontend checkout broken |
+| `MOYASAR_WEBHOOK_SECRET` | Moyasar → Webhooks → Secret | — | Webhook signature validation fails |
+
+**Note:** Moyasar keys appear to be set but checkout returns 502. See REVENUE_READINESS_CHECKLIST.md → Moyasar Diagnostic Checklist.
+
+## P2 — Optional Enrichment (after first customer)
+
+| Variable | Source | Effect When Missing |
+|----------|--------|-------------------|
+| `GOOGLE_MAPS_API_KEY` | Google Cloud → Maps → Places API | Local discovery disabled |
+| `SENDGRID_API_KEY` | sendgrid.com | No automated email |
+| `WHATSAPP_ACCESS_TOKEN` | Meta Business | No WhatsApp API |
+| `HUBSPOT_API_KEY` | HubSpot → Private Apps | No CRM sync |
+| `TAVILY_API_KEY` | tavily.com | No web crawl enrichment |
+
+## Already Set (verified via search-diag)
+
+| Variable | Status |
+|----------|--------|
+| `MOYASAR_SECRET_KEY` | Set (48 chars, sk_liv...) |
+| `MOYASAR_WEBHOOK_SECRET` | Set (64 chars) |
+| `MOYASAR_PUBLIC_KEY` | Set |
+| `APP_URL` | Set |
+| `DATABASE_URL` | Set (via Railway Postgres) |
+
+---
+
+## Critical Reminders
+
+1. **Environment dropdown:** Make sure you're in environment "Dealix" (not "adventurous-tenderness" or other Agent branches)
+2. **Service:** Variables go in service "web" (not Postgres or Redis)
+3. **Deploy:** After adding variables, you MUST click Review → Deploy. Variables are staged until deployed.
+4. **No secrets in chat:** Never paste API keys in any chat window. Add them directly in Railway.
diff --git a/salesflow-saas/docs/ops/REVENUE_READINESS_CHECKLIST.md b/salesflow-saas/docs/ops/REVENUE_READINESS_CHECKLIST.md
new file mode 100644
index 00000000..742de080
--- /dev/null
+++ b/salesflow-saas/docs/ops/REVENUE_READINESS_CHECKLIST.md
@@ -0,0 +1,133 @@
+# Dealix — Revenue Readiness Checklist
+
+**Last verified:** 2026-04-25
+**Railway healthz:** 200 (verified)
+**Pricing API:** 200 (verified: Starter 999 / Growth 2999 / Scale 7999 SAR)
+
+---
+
+## 1. Pricing Path
+
+| Item | Status | Evidence |
+|------|--------|---------|
+| Plans in API | DONE | `/api/v1/pricing/plans` returns 3 plans, SAR currency |
+| Pilot offer defined | DONE | 499 SAR / 7 days / documented in COMMAND_CENTER.md |
+| Agency pricing defined | DONE | AGENCY_PARTNER_OFFER.md: setup 3K-15K + 20-30% MRR |
+| Public pricing page | NOT DONE | API exists but no public-facing pricing page on dealix.me |
+
+**Blocker:** None for manual sales. Pricing page is P1 for inbound.
+
+## 2. Invoice Path
+
+| Item | Status | Evidence |
+|------|--------|---------|
+| Manual invoice template | DONE | FIRST_REVENUE_ATTEMPT in revenue-activation/ |
+| Bank transfer details | DONE | Documented in COMMAND_CENTER.md |
+| STC Pay | DONE | Ready |
+| Moyasar live invoice | BLOCKED | `/api/v1/checkout` returns 502 — Moyasar-side issue |
+| Moyasar sandbox | NOT TESTED | No `MOYASAR_TEST_SECRET_KEY` in Railway env |
+
+**Workaround:** Manual invoice via bank transfer or STC Pay. Works today.
+
+## 3. Payment Path
+
+| Item | Status | Evidence |
+|------|--------|---------|
+| Bank transfer acceptance | READY | Account info available |
+| STC Pay acceptance | READY | Number available |
+| Manual proof workflow | READY | Customer sends screenshot → Sami confirms → onboarding starts |
+| Moyasar automated payment | BLOCKED | 502 on checkout — Moyasar dashboard issue (KYC or key) |
+| Moyasar test payment (1 SAR) | NOT TESTED | Needs `MOYASAR_TEST_SECRET_KEY` or Sami to test `sk_live_` via curl |
+| Payment → onboarding trigger | MANUAL | On payment proof → start onboarding checklist within 4h |
+
+**Manual payment path: FULLY OPERATIONAL.**
+
+## 4. Booking Path
+
+| Item | Status | Evidence |
+|------|--------|---------|
+| Calendly link | ACTIVE | calendly.com/sami-assiri11/dealix-demo |
+| Landing → Calendly | DONE | trial-signup.html redirects on form submit |
+| Email templates → Calendly | DONE | All email templates include Calendly link |
+| Outreach messages → Calendly | DONE | All DM templates include Calendly link |
+| Calendly → webhook | NOT TESTED | Code exists but no E2E booking verified |
+| Calendly → CRM | NOT TESTED | HubSpot key missing in Railway |
+
+**Booking path: OPERATIONAL for manual flow (link → booking → Sami sees in Calendly).**
+
+## 5. CRM Sync Path
+
+| Item | Status | Evidence |
+|------|--------|---------|
+| HubSpot connector code | EXISTS | `services/crm_sync_service.py` |
+| HubSpot API key in Railway | MISSING | Not set in Railway env Dealix/web |
+| E2E: lead → HubSpot contact | NOT TESTED | Cannot test without key |
+| Manual CRM (Google Sheet) | READY | Tracker template in ops docs |
+| Pipeline stages defined | DONE | new → sent → replied → demo → pilot → paid → churned |
+
+**Manual CRM: READY. HubSpot automated: BLOCKED on API key.**
+
+## 6. Follow-up Path
+
+| Item | Status | Evidence |
+|------|--------|---------|
+| Reply classifier | DONE | `POST /api/v1/automation/reply/classify` — 12 categories |
+| Follow-up templates (Day +2/+5/+10) | DONE | Generated per lead in email/generate endpoint |
+| Arabic response templates | DONE | Pre-written Khaliji response per category |
+| Gmail OAuth adapter | NOT BUILT | Needs Google Cloud OAuth setup |
+| Manual follow-up | READY | Copy-paste from templates |
+
+**Manual follow-up: READY. Gmail auto-send: BLOCKED on OAuth setup.**
+
+## 7. Test Payment Path
+
+| Test | Status | How to Test |
+|------|--------|-------------|
+| Moyasar sandbox | NOT TESTED | Add `MOYASAR_TEST_SECRET_KEY` (sk_test_...) in Railway → hit `/api/v1/checkout` |
+| Moyasar live | BLOCKED | sk_live_ exists in Railway but returns 502 |
+| Manual bank transfer | READY | Ask friend to transfer 1 SAR → verify receipt |
+| Manual STC Pay | READY | Ask friend to STC Pay 1 SAR → verify receipt |
+
+**Recommended first test:** Manual bank transfer from a friend. Zero technical dependency.
+
+## 8. Manual Fallback Summary
+
+| Function | Automated | Manual Fallback | Status |
+|----------|-----------|----------------|--------|
+| Lead response | AI endpoint exists | Sami responds manually | READY |
+| Qualification | Automation endpoint | Sami asks questions manually | READY |
+| Booking | Calendly auto | Sami proposes 2 times | READY |
+| Payment | Moyasar | Bank/STC Pay + proof | READY |
+| CRM | HubSpot | Google Sheet | READY |
+| Follow-up | Gmail OAuth | Copy-paste templates | READY |
+| Reporting | Dashboard | Manual WhatsApp/email to client | READY |
+
+**Every function has a working manual fallback. No function is truly blocked.**
+
+---
+
+## Definition of Done for "Revenue Live"
+
+| Gate | Status | What Proves It |
+|------|--------|---------------|
+| First 5 outreach messages sent | NOT DONE | Sami confirms SENT |
+| First demo booked via Calendly | NOT DONE | Calendly notification |
+| First pilot payment received (499 SAR) | NOT DONE | Bank/STC Pay screenshot |
+| First customer onboarded (Day 0 checklist) | NOT DONE | Checklist filled |
+| First daily report sent to customer | NOT DONE | WhatsApp/email sent |
+| PostHog receives at least 1 event | NOT DONE | PostHog dashboard check |
+| Manual payment log has 1 entry | NOT DONE | File updated |
+
+**0/7 done. Revenue is NOT live. Product is live. The gap is sales activity, not engineering.**
+
+---
+
+## Moyasar Diagnostic Checklist (for Sami)
+
+If 502 persists, check these in order:
+1. Moyasar dashboard → Account Status: Active? Pending KYC? Suspended?
+2. Moyasar dashboard → API Keys: Is `sk_live_` the latest generated key?
+3. Test from terminal: `curl -u "sk_live_...:" https://api.moyasar.com/v1/invoices -d "amount=100" -d "currency=SAR"`
+4. If "authentication_error" → key wrong or regenerated. Get new key.
+5. If "account_inactive" → KYC not complete. Finish KYC in Moyasar.
+6. If success → Railway env has whitespace or wrong value. Re-paste key carefully.
diff --git a/salesflow-saas/docs/ops/lead_machine/SAUDI_60_TARGETS.md b/salesflow-saas/docs/ops/lead_machine/SAUDI_60_TARGETS.md
new file mode 100644
index 00000000..27823bb1
--- /dev/null
+++ b/salesflow-saas/docs/ops/lead_machine/SAUDI_60_TARGETS.md
@@ -0,0 +1,81 @@
+# Dealix — 60 Saudi B2B Targets (Week 1)
+
+## Direct Customers (20)
+
+| # | Company | Sector | Website | Why Target | Message Angle |
+|---|---------|--------|---------|-----------|---------------|
+| 1 | Foodics | SaaS/F&B | foodics.com | HubSpot+WhatsApp detected | "شفت إنكم تستخدمون HubSpot — كم lead يضيع بسبب بطء الرد؟" |
+| 2 | Lucidya | SaaS/Social | lucidya.com | 4 tools detected | "شفت حجم الأدوات عندكم — Dealix يكمّلها بالرد الفوري" |
+| 3 | Lean Technologies | SaaS/Fintech | leantech.me | Saudi fintech scaling | "فريق مبيعات صغير + leads كثيرة = فرصة" |
+| 4 | Tamara | BNPL | tamara.co | 3 tools detected | "عندكم merchants كثيرة تحتاج متابعة" |
+| 5 | Rewaa | POS/Retail | rewaa.com | WhatsApp+GTM | "تجار التجزئة يسألون كثير — Dealix يرد بدالكم" |
+| 6 | Qoyod | Accounting | qoyod.com | HubSpot+WhatsApp | "عملاء المحاسبة يسألون نفس الأسئلة — AI يرد" |
+| 7 | Maqsam | Cloud Phone | maqsam.com | HubSpot+Framer | "أنتم تبيعون communications — جربوا AI sales" |
+| 8 | Hakbah | Savings | hakbah.com | WooCommerce+GTM+GA4 | "لاحظت forms كثيرة — كم يُتابع فعلاً؟" |
+| 9 | Sary | B2B Supply | sary.com | 3 tools | "توزيع B2B = leads كثيرة = Dealix يأهّل" |
+| 10 | BRKZ | Construction | brkz.com | 2 tools | "مقاولين يسألون عن الأسعار — AI يرد ويأهّل" |
+| 11 | Aqar | Real Estate | aqar.fm | High volume leads | "عقار = 500+ lead/شهر — كم يُتابع؟" |
+| 12 | Jarir Online | Retail | jarir.com | ecommerce B2B | "استفسارات الشركات على الموقع — AI يحوّلها لصفقات" |
+| 13 | Floward | Flowers/Corp | floward.com | Corporate orders | "طلبات B2B (تموين، هدايا) تحتاج متابعة سريعة" |
+| 14 | Nana | Grocery | nana.sa | Delivery+B2B | "طلبات المطاعم والشركات = pipeline ثابت" |
+| 15 | Morni | Roadside | morni.com | Service leads | "طلبات الشركات للعقود السنوية" |
+| 16 | Lendo | Lending | lendo.sa | Fintech B2B | "طلبات التمويل تحتاج تأهيل سريع" |
+| 17 | Unifonic | Communications | unifonic.com | 6 tools! Chili Piper+HubSpot+Zendesk | "أنتم في مجال التواصل — جربوا AI SDR" |
+| 18 | HyperPay | Payments | hyperpay.com | Payment gateway | "merchants تسأل عن التفعيل — AI يرد" |
+| 19 | Moyasar | Payments | moyasar.com | Payment gateway | "developers يسألون عن الـ API — AI يوجّه" |
+| 20 | Tap Payments | Payments | tap.company | Mixpanel detected | "عملاء جدد يسألون — AI يأهّل ويحوّل" |
+
+## Agency Partners (20)
+
+| # | Company | Type | Why Target | Offer |
+|---|---------|------|-----------|-------|
+| 1 | Peak Content | Content Marketing | Saudi top agency | Setup 5K + 25% MRR |
+| 2 | Digital8 | Digital Agency | Riyadh | Setup 3K + 20% MRR |
+| 3 | Brand Lounge | Branding+Digital | Dubai/Riyadh | Setup 5K + 25% MRR |
+| 4 | Wavy | Social Media | Saudi focused | Setup 3K + 20% MRR |
+| 5 | Bold | Performance | Ads focused | Setup 3K + 20% MRR |
+| 6 | Netaq Digital | Full service | Jeddah | Setup 3K + 20% MRR |
+| 7 | Jarwal | Marketing | Riyadh | Setup 3K + 20% MRR |
+| 8 | Zoomly | Growth Agency | Saudi | Setup 3K + 20% MRR |
+| 9 | UTURN | Content | Saudi media | Content collab |
+| 10 | Telfaz11 | Video Content | Saudi media | Content collab |
+| 11 | Techieons | Tech Consulting | IT services | Implementation partner |
+| 12 | Lean Business | Consulting | Strategy | Referral 10% |
+| 13 | 2P Agency | Creative | Saudi creative | Setup 3K + 20% MRR |
+| 14 | Shawaa | Performance | PPC/Social | Setup 3K + 20% MRR |
+| 15 | Crowd | PR + Digital | Communications | Setup 5K + 25% MRR |
+| 16 | Rab3i | Marketing | Saudi digital | Setup 3K + 20% MRR |
+| 17 | Pioneers | Digital Marketing | MENA | Setup 5K + 25% MRR |
+| 18 | Hayan | HubSpot Partner | CRM implementation | Implementation partner |
+| 19 | Digiata | Automation | Marketing automation | Implementation partner |
+| 20 | SocialEyes | Social Media | Riyadh/Dubai | Setup 3K + 20% MRR |
+
+## Strategic Partners (10)
+
+| # | Company | Type | Partnership Model |
+|---|---------|------|------------------|
+| 1 | Salla | E-commerce Platform | Integration + co-sell to Salla merchants |
+| 2 | Zid | E-commerce Platform | Integration + co-sell to Zid merchants |
+| 3 | Wafeq | Accounting SaaS | Cross-sell to Wafeq customers |
+| 4 | Unifonic | Communications | Unifonic customers → Dealix |
+| 5 | Tap Payments | Payment Gateway | Tap merchants → Dealix |
+| 6 | Misk Foundation | Startup Community | Misk ecosystem companies |
+| 7 | KAUST | Innovation Hub | KAUST startups |
+| 8 | STC Solutions | Enterprise IT | Enterprise referral |
+| 9 | Wamda Capital | VC | Portfolio companies |
+| 10 | Sanabil Investments | PIF VC | Portfolio companies |
+
+## Warm Network (10)
+
+| # | Relationship | Why | Ask |
+|---|-------------|-----|-----|
+| 1 | صديق يملك شركة SaaS | يفهم المنتج | Pilot مباشر |
+| 2 | صديق في وكالة تسويق | يبيع لعملائه | Partner |
+| 3 | زميل سابق مدير مبيعات | يعرف المشكلة | Referral |
+| 4 | قريب يملك متجر إلكتروني | واتساب كقناة | Pilot |
+| 5 | معرفة من مؤتمر تقني | شبكة واسعة | Intro |
+| 6 | صديق في عقارات | leads كثيرة | Pilot |
+| 7 | زميل دراسة في fintech | يعرف founders | Intro |
+| 8 | معرفة في F&B | سلسلة مطاعم | B2B pilot |
+| 9 | مستشار أعمال | يعرف SMEs | Referral |
+| 10 | Mentor من accelerator | شبكة founders | Intro |
diff --git a/salesflow-saas/frontend/src/app/marketers/page.tsx b/salesflow-saas/frontend/src/app/marketers/page.tsx
index 0f7d22d4..296caede 100644
--- a/salesflow-saas/frontend/src/app/marketers/page.tsx
+++ b/salesflow-saas/frontend/src/app/marketers/page.tsx
@@ -1,131 +1,426 @@
import Link from "next/link";
import {
MessageCircle,
- Download,
- FileText,
- CheckSquare,
- Presentation,
+ Users,
+ TrendingUp,
+ Zap,
+ Shield,
+ Clock,
+ CheckCircle,
ArrowLeft,
- ExternalLink,
- Compass,
+ Calendar,
+ DollarSign,
+ Briefcase,
+ HelpCircle,
} from "lucide-react";
export const metadata = {
- title: "Dealix — بوابة المسوّقين",
- description: "دخول مباشر، تحميلات، قوالب واتساب، وروابط العروض القطاعية.",
+ title: "Dealix — برنامج شراكة الوكالات والمسوّقين",
+ description:
+ "حوّل وكالتك إلى ماكينة إيرادات متكررة. أضف خدمة الرد الذكي + تأهيل leads لعملائك — setup fee + MRR شهري.",
};
-const links = [
+const packages = [
{
- title: "الخطة الاستراتيجية والمنافسة",
- href: "/strategy",
- desc: "لماذا Dealix، مراحل التنفيذ، وتحميل الوثيقة الكاملة.",
- icon: Compass,
+ name: "الأساسية",
+ setupFee: "3,000",
+ monthly: "990",
+ partnerShare: "30%",
+ features: [
+ "رد AI بالعربي على واتساب أو فورم",
+ "5 أسئلة تأهيل مخصصة",
+ "تصنيف leads: حار / دافئ / بارد",
+ "تقرير أسبوعي",
+ ],
+ cta: "ابدأ بأول عميل",
+ highlight: false,
},
{
- title: "مركز الموارد (كل الروابط)",
- href: "/resources",
- desc: "ZIP، العروض، والملفات التسويقية.",
- icon: Download,
+ name: "المتقدمة",
+ setupFee: "7,000",
+ monthly: "2,490",
+ partnerShare: "25%",
+ features: [
+ "كل مميزات الأساسية",
+ "حجز مواعيد تلقائي (Calendly)",
+ "ربط CRM ثنائي (HubSpot)",
+ "متابعة تلقائية Day +2 / +5 / +10",
+ "تقرير يومي",
+ ],
+ cta: "الأنسب للوكالات",
+ highlight: true,
},
{
- title: "فهرس الملفات الثابتة",
- href: "/dealix-marketing/index.html",
- desc: "نسخة HTML كاملة من بوابة الأصول.",
- icon: FileText,
- },
- {
- title: "قوالب واتساب (نسخ ولصق)",
- href: "/dealix-marketing/marketers/whatsapp-playbook-ar.txt",
- desc: "رسائل جاهزة — عدّل الاسم والرابط فقط.",
- icon: MessageCircle,
- },
- {
- title: "قائمة تحقق الدخول",
- href: "/dealix-marketing/marketers/entry-checklist-ar.txt",
- desc: "تأكد أنك غطيت الخطوات قبل التواصل مع العملاء.",
- icon: CheckSquare,
- },
- {
- title: "العروض القطاعية (10 قطاعات)",
- href: "/dealix-presentations/00-dealix-company-master-ar.html",
- desc: "ابدأ من ملف الشركة ثم اختر رقم القطاع.",
- icon: Presentation,
- external: false,
- },
- {
- title: "هيكل العمولة (Markdown)",
- href: "/dealix-marketing/Dealix_Marketing_Arsenal.md",
- desc: "Silver / Gold / Platinum — راجع العقد الرسمي للأرقام النهائية.",
- icon: FileText,
+ name: "المؤسسية",
+ setupFee: "15,000+",
+ monthly: "مخصص",
+ partnerShare: "20-30%",
+ features: [
+ "كل مميزات المتقدمة",
+ "قنوات متعددة (واتساب + إيميل + صوت)",
+ "لوحة تحكم تنفيذية",
+ "سير عمل موافقات",
+ "مدير حساب مخصص",
+ ],
+ cta: "تواصل معنا",
+ highlight: false,
},
];
+const workflows = [
+ {
+ icon: TrendingUp,
+ title: "وكالة أداء (Performance)",
+ desc: "تشغّل حملة → Leads تنزل → Dealix يرد فوراً → العميل يشوف ROI أعلى → يجدد → أنت تحصّل MRR",
+ },
+ {
+ icon: Briefcase,
+ title: "مسوّق مستقل (Freelancer)",
+ desc: 'تقدم عرض "إدارة حملات + رد ذكي" → Dealix يشتغل بالخلفية → أنت تركز على الاستراتيجية → دخل شهري متكرر',
+ },
+ {
+ icon: Users,
+ title: "مستشار CRM / RevOps",
+ desc: "تنفذ HubSpot لعميل → تضيف Dealix كطبقة response → CRM يمتلئ بـ leads مؤهلة → العميل يشوف قيمة CRM أسرع",
+ },
+];
+
+const faqs = [
+ {
+ q: "كم ياخذ الإعداد لكل عميل؟",
+ a: "يوم واحد للأساسية، 3 أيام للمتقدمة.",
+ },
+ {
+ q: "هل أحتاج خبرة تقنية؟",
+ a: "لا — نحن نسوي الإعداد والتشغيل بالكامل.",
+ },
+ {
+ q: "كم أقدر أربح من عميل واحد؟",
+ a: "عميل واحد = 3,000 setup + ~990/شهر = 14,880 ريال/سنة.",
+ },
+ {
+ q: "هل فيه عقد طويل؟",
+ a: "لا — شهري لك ولعميلك. بدون التزام سنوي.",
+ },
+ {
+ q: "ماذا لو العميل ما عجبه؟",
+ a: "30 يوم ضمان استرداد كامل.",
+ },
+ {
+ q: "هل يشتغل مع HubSpot / Zoho؟",
+ a: "نعم — CRM sync متاح في الباقة المتقدمة.",
+ },
+ {
+ q: "هل يفهم العربي السعودي؟",
+ a: "نعم — مبني Arabic-first بلهجة خليجية.",
+ },
+ {
+ q: "كيف أبدأ؟",
+ a: "احجز مكالمة شراكة 20 دقيقة → نسوي pilot مجاني لأول عميل عندك.",
+ },
+];
+
+const CALENDLY = "https://calendly.com/sami-assiri11/dealix-demo";
+
export default function MarketersPage() {
return (
-
-
-
Dealix Partner GTM
-
بوابة المسوّقين
-
- مسار واحد للدخول: افتح الروابط أدناه من نفس الموقع (لا حاجة لخادم 8000). انسخ قوالب
- الواتساب من الملف النصي وعدّل{" "}
-
- {`{الاسم}`}
- {" "}
- و
- رابط موقعك.
+
- احفظ رسالة واحدة كقالب في واتساب (الأجهزة المدعومة) أو استخدم ملاحظات سريعة. لا ترسل
- لعملاء نهائيين دون تنسيق مع فريق Dealix وحسب سياسة الاستخدام.
+ {/* ── Problem ── */}
+
+
+ {[
+ "AI يرد بالعربي خلال 45 ثانية على كل lead",
+ "يسأل أسئلة التأهيل (ميزانية؟ جدية؟ موعد؟)",
+ "يحجز اجتماع أو يحوّل للمبيعات",
+ "يرسل تقرير يومي عن كل lead",
+ ].map((text) => (
+