π Shared WABA MVP Guide: Mid-May Deadline
This document is the strict, hyper-focused Sprint Guide to deliver the Shared WABA MVP by Mid-May. It incorporates the Phase 0 & Phase 1 goals from the master roadmap, strictly dropping "Dedicated WABA" features (like Meta Embedded Signup) to guarantee we hit the deadline.
In the Shared WABA Model, Kloyst owns the WhatsApp Business API (WABA). Clients preload a wallet, we send messages on their behalf using our WABA, and we deduct their wallet based on Meta Webhook delivery status.
ποΈ 1. File Structure & Design Patternsβ
To ensure rapid development without incurring technical debt, stick to NestJS standard conventions:
Clean Architecture Pipelineβ
- Controllers (
*.controller.ts): ONLY parse incoming HTTP payloads and return responses. No database calls. - Services (
*.service.ts): The core engine. Execute all business logic and Prisma queries here. ALWAYS injectvendorIdintowhereclauses. - DTOs (
*.dto.ts): Data Transfer Objects defining the exact JSON shape.
Code & Security Practices (Non-Negotiable)β
- Strict Validation: Use
class-validatorandclass-transformeron every single incoming DTO. If a client sends garbage data, reject it before it hits the Service. - Tenant Isolation: Do NOT rely on the frontend to pass
vendorId. Extract it from the JWT via the@CurrentUser()decorator. - Webhook Integrity: Meta signs webhooks with a SHA256 signature (
X-Hub-Signature-256). Always verify this signature in a Guard/Interceptor to prevent malicious wallet deductions. - Transaction Locking: Wallet deductions MUST use
BEGIN; SELECT ... FOR UPDATE; UPDATE; COMMIT;to prevent race conditions when 1,000 webhooks hit in 2 seconds.
πΊοΈ 2. The Mid-May Sequential Sprint Workflowβ
Build the APIs exactly in this sequence. This order ensures no developer gets blocked waiting on database structures from another feature.
Sprint 1: Identity, Auth & Vendor Setup (Foundation)β
Goal: Secure onboarding. Since we will be emailing invoices and handling wallet recharges, email verification is strictly mandatory.
-
Auth & Registration APIs (OTP Flow):
POST /auth/register(Accepts Email/Password. Creates user inPENDING_VERIFICATIONstate).POST /auth/send-otp(Triggers an email via SendGrid/AWS SES with a 6-digit OTP stored temporarily in Redis with a 5-minute TTL).POST /auth/verify-otp(Validates OTP, marks email as verified, creates theVendorroot profile, and returns the JWT).POST /auth/login(Standard login returning JWT withvendorIdandrole).
-
Authorization & Role Checks:
JwtAuthGuard: Protects all routes ensuring the user is logged in.RolesGuard: Ensures only users withADMINrole can access billing and wallet recharge APIs (preventing standard staff from making purchases).
-
Vendor Profile APIs:
GET /vendor/me(Fetch vendor profile, company details, tax info for billing).PATCH /vendor/me(Update business/billing info).
Sprint 2: Audience & Contact Managementβ
Goal: Vendors can build an audience list to message.
- Kloyst APIs:
POST /contacts/bulk(Accept CSV or Array, validate E.164 phone numbers)GET /contacts(Fetch paginated contact list)DELETE /contacts/:id
- Code Practice: Strip all formatting (
+,,-) from phone numbers before saving to PostgreSQL. Meta requires strict integer strings.
Sprint 3: Meta Templates (The First Meta API Integration)β
Goal: Vendors submit templates for approval. Kloyst forwards them to Meta.
- Meta APIs Used:
POST /v18.0/{KLOYST_WABA_ID}/message_templates - Kloyst APIs:
POST /templates-> Parses input, savesPENDINGto DB, fires POST to Meta Cloud API.GET /templates-> Fetch list of local templates.
- Meta Webhook Setup:
POST /webhook/meta-> Listen formessage_template_status_update. Update local DB when Meta approves/rejects.
Sprint 4: Pre-Paid Wallet (Monetization MVP)β
Goal: Vendors must have credits before sending messages.
- Kloyst APIs:
GET /wallet/balancePOST /wallet/recharge/init(Creates Razorpay/Stripe Order ID)
- Payment Webhook:
POST /webhook/payment(Listens forpayment.captured. Verifies signature, securely ADDS to Postgres wallet balance).
Sprint 5: The Campaign Engine & Bulk Dispatch (The Core Engine)β
Goal: Dispatch 10,000 messages safely without hitting Meta API rate limits.
- Meta APIs Used:
POST /v18.0/{KLOYST_PHONE_NUMBER_ID}/messages - Kloyst APIs:
POST /campaigns/launch(Accepts Template ID & Contact List ID)
- Internal Workflow (Crucial for Scaling):
- API checks if
Wallet Balance > (Contacts * Margin Cost). If false, reject402 Payment Required. - Insert
Campaignrecord. - Chunk contacts into individual "Jobs" and push to Redis Queue (BullMQ).
- Return
200 OKto frontend immediately. - Background Workers pop jobs from Redis at a strict rate limit (e.g., 50/sec), fire the Meta
messagesAPI, and insert aMessageLoginto Postgres with the returnedwamid.XYZ.
- API checks if
Sprint 6: Webhooks & Financial Deductionsβ
Goal: Process message deliveries and deduct the wallet.
- Meta Webhook Handling:
POST /webhook/meta - Workflow:
- Meta sends a
statusesarray (e.g.,status: delivered,pricing.category: marketing). - Match the
wamidin PostgreSQLMessageLogand mark asDELIVERED. - IF Meta indicates a charge occurred:
- Open Postgres Row Lock on Wallet.
- Calculate
Meta Cost + Kloyst Margin. - Deduct balance.
- Insert
Ledgertransaction history. - Release Lock.
- Respond to Meta with
200 OKwithin 3 seconds. (If math is too slow, push Webhook to a Redis queue and resolve it asynchronously).
- Meta sends a
π¦ Future Scaling Considerations (Post-May)β
- Redis Overload: For campaigns >100k, passing full contact data into Redis will exhaust memory. Later, we will pass just
campaignIdandoffset, letting the worker fetch contacts from Postgres natively. - Dead-Letter Webhooks: Webhooks that fail processing will need a separate DLQ (Dead Letter Queue) so we can replay them and not lose revenue data.