@molecule/api-sms-capture
Provider bond · sms · API (Node) · v1.0.3 · Apache-2.0
SMS capture provider for molecule.dev
npm install @molecule/api-sms-capturenpm · Source on GitHub · Implements @molecule/api-sms
How it works
@molecule/api-sms-capture is a provider bond on the API (Node) side: it implements the sms core interface (@molecule/api-sms) with a concrete vendor or library behind it.
Your code calls the core; you wire this provider once at startup. Swapping vendors later is one line in that wiring, not a rewrite.
import { setProvider } from '@molecule/api-sms'
import { provider } from '@molecule/api-sms-capture'
setProvider(provider) // intercept-only: nothing is actually sent
// Tee mode: really send AND record the real outcome
// import { createSMSCaptureProvider } from '@molecule/api-sms-capture'
// import { createProvider as twilio } from '@molecule/api-sms-twilio'
// setProvider(createSMSCaptureProvider(twilio()))Works with: @molecule/api-activity, @molecule/api-sms
Live demos
Running apps built with Molecule that install this package — open the app or study the template.
Reference
SMS capture provider for molecule.dev.
Records every send() / sendBulk() call as an activity event.
Intercept-only by default; delegates + tees when wrapping a real provider.
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
Quick Start
import { setProvider } from '@molecule/api-sms'
import { provider } from '@molecule/api-sms-capture'
setProvider(provider) // intercept-only: nothing is actually sent
// Tee mode: really send AND record the real outcome
// import { createSMSCaptureProvider } from '@molecule/api-sms-capture'
// import { createProvider as twilio } from '@molecule/api-sms-twilio'
// setProvider(createSMSCaptureProvider(twilio()))
Type
provider
Installation
npm install @molecule/api-sms-capture @molecule/api-activity @molecule/api-sms
API
Functions
createSMSCaptureProvider(realProvider)
Creates an SMS capture provider.
When realProvider is provided, each message is delivered through it and the
captured event records the real outcome (delegate + tee). When omitted (the
dev default), messages are intercepted and a synthetic SMSResult is
returned.
function createSMSCaptureProvider(realProvider?: SMSProvider): SMSProvider
realProvider— Optional real provider to delegate to and tee.
Returns: An SMSProvider that records activity for every send.
Constants
provider
Default SMS capture provider (intercept-only).
const provider: SMSProvider
Core Interface
Implements @molecule/api-sms interface.
Bond Wiring
Setup function to register this provider with the core interface:
import { setProvider } from '@molecule/api-sms'
import { provider } from '@molecule/api-sms-capture'
export function setupSmsCapture(): void {
setProvider(provider)
}
Injection Notes
Requirements
Peer dependencies:
@molecule/api-activity^1.0.1@molecule/api-sms^1.0.1
Runtime Dependencies
-
@molecule/api-activity -
@molecule/api-sms -
Two modes, and the choice decides whether the message is DELIVERED. INTERCEPT-ONLY (
provider, orcreateSMSCaptureProvider()with no argument) records the message and returns a synthetic success — nothing reaches the handset. DELEGATE + TEE (createSMSCaptureProvider(real)) sends through the real provider AND records the real outcome. Anywhere real messages must go out (production), wrap the real provider — never bond the intercept-only provider. -
Recording is best-effort: a bonded
ActivitySinkthat throws NEVER changes the outcome ofsend()— a successful real send still resolves and a failed one still rejects with the REAL provider error. -
Captured messages go to the bonded ACTIVITY SINK (
@molecule/api-activity— e.g. the sandbox's sink read by theread_activitytool). Without a sink bonded,record()is a silent no-op: intercept-onlysend()still returns a synthetic success and the message is visible nowhere. Wire an activity sink before relying on captures (OTP flows, the E2E checklist). -
Intercept-only mode returns synthetic results:
send()→status: 'sent'with idcaptured-<uuid>, andgetStatus()ALWAYS reports'sent'. In tee mode both reflect the wrapped real provider (whosegetStatus()support varies — see@molecule/api-sms). -
Events record
status: 'captured'when intercepting,'sent'/'failed'when teeing a real provider — so the activity feed never shows a delivery that didn't happen.
E2E Tests
Integration checklist — drive the real UI (live preview, no mocks), adapt each item to this app's actual screens/flows, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip:
- Each SMS-triggering flow (phone verification, OTP login, alerts the
app defines) confirms the send in the UI and a message actually reaches
the transport. The sandbox CAPTURES outbound SMS instead of sending — read
it with the
read_activitytool (filter type 'sms'); the code/link is in its payload. Never mock the flow or modify production code to expose it. - The OTP round-trip completes: request a code → read the captured message's code → enter it in the UI → the flow advances; a wrong or expired code is rejected with a visible error.
- Messages go only to the authenticated user's own verified number — no UI or endpoint lets a caller text an arbitrary number (spam/abuse vector).