Skip to content

PRD: SMS campaigns

ModuleMarketingPRD IDPRD-CMP-001
StatusReady to devFEATCMP
EpicPlaneBANA-1546
Date2026-03-13Versionv0.1
Packages@nx/outreachURDCMP
SurfaceBackend
OwnerPhát Nguyễn

TL;DR

Stands up a real SMS delivery channel for Marketing - a VNPAY provider integration plus an SMS-sending capability in the sign-in flow - proven by its first consumer, OTP request/verify. Live OTP-over-SMS delivery is currently suppressed (non-prod uses a bypass code) pending provider readiness; this is the load-bearing send path the planned campaign-to-segment send (URD-CMP-001) will reuse.

1. Context & Problem

Marketing's Campaigns & Automation feature needs a way to actually deliver messages - and today there is no SMS send path at all. Nothing in the platform can hand a text message to a carrier, so neither authentication (OTP) nor any future campaign can reach a customer's phone. Per the module Non-Goals, BANA integrates an external provider rather than building its own delivery infrastructure.

This increment lays the groundwork - the external provider integration, the capability that uses it, and provider/credential configuration - and exercises it with the first concrete consumer, OTP. The campaign-to-segment send layers on top later; building the channel first unblocks both auth and marketing.

2. Goals & Non-Goals

Goals

  • Stand up an SMS provider integration wrapping the VNPAY SMS provider (request building, request/response shapes, a send entry point).
  • Add an SMS-sending capability to the sign-in flow and wire SMS sending into the application.
  • Load SMS provider configuration (credentials, environment) through central configuration, with idempotent seeding and tolerant migration.
  • Build the first consumer - OTP request and verification - with an SMS-backed OTP sender wired in. A log-only sender is implemented for non-prod; phone OTP currently suppresses SMS delivery and accepts a bypass code for testing.

Non-Goals

  • The campaign-to-segment send itself (URD-CMP-001) - layered on this channel, not in this increment.
  • Triggered / drip automation (URD-CMP-002) and A/B testing & reporting (URD-CMP-003).
  • Delivery infrastructure beyond integrating the external VNPAY provider (per module Non-Goals).
  • Email / other-channel delivery.

3. Success Metrics

MetricTarget / signal
Channel availabilityValid sends return provider acceptance; failures surface, never silently dropped
OTP deliverabilityOTP request → verify succeeds end to end (live OTP-over-SMS currently suppressed; non-prod uses a bypass code)
Config safetyMissing/invalid config never crashes startup; seeding is idempotent across restarts
Reuse readinessA future campaign sender can call the same channel with no provider rework

4. Personas & Use Cases

PersonaGoal in this feature
Customer (recipient)Receive an OTP (and later, campaign messages) on their phone
End user authenticatingRequest and verify an OTP to complete sign-in
Owner / Manager (Marketing)Eventually reuse this channel to reach segments via campaigns
Platform operatorConfigure VNPAY credentials/environment without redeploying code

Core scenario: configure the VNPAY SMS provider once → an application flow (OTP request) asks the channel to send → the channel builds the provider request (plaintext by default, or encrypted via the secure-send variant) and dispatches it → the recipient gets the text → OTP verify confirms the code.

5. User Stories

  • As an end user authenticating, I want to receive an OTP by SMS and verify it, to complete sign-in securely.
  • As a platform operator, I want SMS credentials and environment loaded from configuration, so I can switch providers/environments without a code change.
  • As a platform operator, I want missing SMS config tolerated at startup, so environments without SMS still boot.
  • As a Marketing owner, I want a reusable SMS channel, so the planned campaign-to-segment send can reach customers without new provider work.
  • As a developer, I want a log-only sender in non-prod, so I can exercise SMS flows without spending real messages.

6. Functional Requirements

#RequirementURD ref
FR-1Provide an SMS provider integration exposing an SMS send path (a send entry point plus request/response shapes)URD-CMP-001
FR-2Integrate the VNPAY SMS provider via provider-specific request buildingURD-CMP-001
FR-3Offer an encrypted secure-send variant (ECDH-derived shared key + AES-256-CBC) for SMS payloads; the default send path (used by OTP) dispatches plaintextURD-CMP-001
FR-4Expose an SMS-sending capability in the sign-in flow that the application calls to send SMSURD-CMP-001
FR-5Load SMS provider config (credentials, environment) via central configuration, seeded idempotentlyURD-CMP-001
FR-6Tolerate missing SMS configuration during migration (register an empty client instead of failing); the provider config row is always seeded so startup finds a configURD-CMP-001
FR-7Provide OTP request and verification on top of the SMS channel, with an SMS-backed OTP sender wired in; a log-only sender is implemented but not the active wiring - phone OTP currently suppresses SMS delivery and accepts a bypass code in non-prodURD-CMP-001
FR-8Centralize OTP configuration in central configuration; hash OTP codes with one-way hashingURD-CMP-001

Full requirement text and acceptance criteria live in the Marketing URD. This PRD references them rather than restating them.

7. Non-Functional Requirements

AreaRequirement
SecurityAn encrypted secure-send variant is available for SMS payloads (the default/OTP path is plaintext); OTP codes are one-way hashed, never stored in clear
ConfigurabilityProvider credentials/environment live in configuration, not code; switchable without redeploy
ResilienceMissing SMS config is tolerated during migration (an empty client is registered); failures surface rather than crashing the application
IdempotencyConfig seeding converges - repeated migrations settle to one record
ObservabilityA log-only sender is implemented for non-prod; in practice phone OTP suppresses delivery and a bypass code makes verify testable without real sends
i18nUser-facing OTP messaging is bilingual (English + Vietnamese)

8. UX & Flows

No merchant-facing screen in this increment; application flows invoke the channel (OTP today, campaign send later). Provider credentials are managed through configuration.

9. Data & Domain

ConceptRole
SMS request / response shapeShape of an outbound SMS and its provider acknowledgement
Provider client configurationProvider connection settings (credentials, endpoint)
SMS provider configPer-environment provider settings in the integrations configuration area, with an environment setting
OTP configCentralized OTP settings in central configuration
OTP recordThe issued/hashed OTP used by request + verify

Conceptual only - full schema and provider detail live in the developer outreach docs.

10. Dependencies & Assumptions

Depends on

  • VNPAY SMS provider - the external carrier integration the channel wraps.
  • Central configuration - provider/OTP config loading, idempotent seeding, migrations.
  • The sign-in service - hosts the SMS-sending capability and the OTP request/verify flow.

Assumptions

  • Valid VNPAY credentials and an environment are provided per deployment (or SMS is intentionally disabled).
  • One-way hashing is available for OTP secrets.
  • A future campaign sender will reuse this channel rather than integrate its own provider.

11. Risks & Open Questions

Risk / questionMitigation / status
Campaign-to-segment send (URD-CMP-001) is not yet built on this channelChannel + OTP shipped first; campaign send is the next increment - PRD stays planned until then
Single-provider (VNPAY) couplingThe channel abstracts the provider; a second provider can slot in behind the same capability
Missing SMS config could break bootMigration tolerates absent config (empty client); the config row is always seeded so startup finds one
Provider outage / delivery failureFailures surface to callers; non-prod relies on a bypass code with delivery suppressed
Cost of real sends during testingPhone OTP suppresses delivery and uses a bypass code in non-prod (a log-only sender is also implemented)

12. Release Plan & Launch Criteria

AspectPlan
PhaseP2 - SMS delivery channel for Campaigns & Automation
RolloutChannel available where VNPAY config is present; OTP consumer enabled in the sign-in flow
MigrationSMS provider config seeded into the integrations configuration area idempotently; environment setting added; tolerant of missing config
Launch criteriaOTP request → verify verified end to end (live OTP-over-SMS currently suppressed; bypass code in non-prod); seeding idempotent across migrations; provider config row seeded so startup has a config
MonitoringSMS send success/failure rate, OTP verify success rate, provider error responses

13. FAQ

Does this send marketing campaigns yet? No. This increment delivers the SMS channel and its first consumer (OTP). The campaign-to-segment send (URD-CMP-001) layers on top in a later increment - which is why this PRD is still planned.

Why is OTP in a Marketing PRD? OTP is auth-facing, but it is the first and load-bearing consumer of the exact channel campaigns will reuse. Proving the channel through OTP de-risks the marketing send.

Which provider does it use? VNPAY SMS, wrapped by the SMS provider integration so it can be swapped or extended.

How are credentials managed? Through central configuration (integrations area), with an environment setting and idempotent seeding - no redeploy to change them.

What happens in non-prod or when SMS isn't configured? Phone OTP suppresses real delivery and accepts a bypass code in non-prod (a log-only sender is also implemented). Missing provider config is tolerated during migration, and the config row is always seeded so startup finds one.

References

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.