Files
COMERCIAL-GTI/maquina-comercial-pessoal/docs/superpowers/specs/2026-07-29-whatsapp-verification-and-manual-send-design.md
GustavoandClaude Sonnet 5 417da56bd6 chore: import maquina-comercial-pessoal history via subtree
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

git-subtree-dir: maquina-comercial-pessoal
git-subtree-mainline: f5af038e19
git-subtree-split: a9ef4d776d
2026-09-28 15:50:17 -03:00

101 lines
6.6 KiB
Markdown

# WhatsApp Verification and Manual Send Design
## Goal
Make the visible manual phone field the authoritative send destination and distinguish a syntactically valid telephone from a number confirmed by WhatsApp.
## Constraints
- Work only in `MAQUINA COMERCIAL PESSOAL`.
- Keep the RFB telephone value as source data; do not claim it is a WhatsApp account without a real lookup.
- Verify numbers on demand. Do not scan the full RFB lead base.
- Automatic outreach may use only numbers with a persisted successful verification.
- Brazilian mobile variants with and without the ninth digit represent the same opt-out identity.
- A send timeout is an unknown delivery outcome, not proof of failure.
## Data Model
Add a `whatsapp_number_verifications` cache keyed by the normalized requested phone number. Each record stores:
- `numero`: digits submitted for verification.
- `status`: `confirmado`, `nao_cadastrado`, or `erro_temporario`.
- `numero_resolvido`: number returned in the registered WhatsApp JID, when confirmed.
- `variante`: `exata`, `sem_nono_digito`, or `com_nono_digito`, when confirmed.
- `erro`: temporary lookup error text, otherwise empty.
- `checked_at`: lookup timestamp.
No row for any equivalent variant means `nao_consultado`. Lead queries inspect all equivalent variants and use the most recent cached result. This sparse cache avoids updating every lead and allows repeated or equivalent lead phone values to reuse evidence.
`telefone_valido` keeps its existing meaning: the value has a valid Brazilian DDI, DDD, and phone shape. It must not be renamed in storage or treated as WhatsApp verification.
## Number Identity
Move Brazilian variant generation to a provider-independent phone utility shared by WhatsApp resolution and opt-out persistence. For a valid Brazilian mobile number, the utility returns the exact number first and then the equivalent with or without the ninth digit.
Opt-out lookup checks all equivalent variants. Adding an opt-out removes conflicting equivalent records and stores one normalized representation. Removing an opt-out removes all equivalent records. This closes both route-time and queue-time bypasses.
## Manual Send Flow
1. The browser submits `numero`, `mensagem`, and optional `leadId`.
2. The server validates that the optional lead exists, but uses it only for template context, product metadata, and send history linkage.
3. The server always validates and sends to `body.numero`. It never substitutes `lead.whatsapp`.
4. WhatsApp resolution checks the exact number and its Brazilian equivalent under a bounded timeout.
5. A successful lookup or definite non-registration updates the verification cache.
6. If the submitted number is equivalent to the selected lead's stored phone, the cached result describes that lead phone. An unrelated edited destination remains a separate phone-level result and does not relabel the stored lead phone.
7. The send reservation rechecks opt-out and daily limit inside the serialized send queue.
8. A successful `sendMessage` returns the provider message ID and resolved destination for normal ACK tracking.
The visible destination is therefore authoritative even while the selected lead continues to provide variables such as company and city.
## Automatic Outreach Flow
Automatic candidate selection matches each stored lead number against all equivalent cached variants and includes it only when the most recent matching state is `confirmado`. Syntactic `telefone_valido = 1` remains required but is not sufficient.
Numbers in `nao_consultado`, `nao_cadastrado`, or `erro_temporario` are skipped. The batch does not perform implicit bulk verification.
## Timeouts and Errors
- `onWhatsApp` has a configurable bounded timeout. Timeout produces `erro_temporario`, HTTP 504 for an explicit check, and no definitive registration claim.
- `sendMessage` has a separate configurable bounded timeout.
- When `sendMessage` times out after the send reservation, the local send becomes `desconhecido`. The API returns an accepted-style warning that the message may have been sent and instructs the operator not to resend without checking the phone.
- A definite non-registration returns HTTP 422 and stores `nao_cadastrado`.
- Other pre-send provider failures remain errors and are persisted through the existing send finalization path.
The underlying provider promise is observed after a timeout to avoid unhandled rejection, but a late result does not trigger an automatic retry.
## User Interface
- Replace labels that infer WhatsApp from `telefone_valido` with `Telefone valido`, `Com telefone`, or `Sem telefone`.
- Show a separate WhatsApp state in lead details: `Nao consultado`, `Confirmado`, `Nao cadastrado`, or `Erro temporario`.
- The treatment table phone column is labeled `Telefone`.
- The search placeholder refers to telephone rather than WhatsApp.
- Selecting a lead fills context and its phone, but later edits remain authoritative.
- Add a visible action to clear selected lead context without clearing the typed destination or message.
- While the settings dialog is open, connection polling continues in every state. Transitional states poll faster than stable states.
## API Behavior
`POST /api/whatsapp/check-number` accepts `numero` and optional `leadId`. It returns the persisted state, resolved number, variant, and timestamp. A lead ID is validation/context only; the checked number always comes from `numero`.
Lead list and detail responses expose a derived `whatsapp_status`, `whatsapp_numero_resolvido`, and `whatsapp_checked_at` from the cache for the lead's stored phone.
Existing outreach status and ACK APIs remain compatible.
## Testing
Automated regressions must prove:
- An edited manual number wins over the selected lead's stored phone.
- The selected lead still supplies template variables and `lead_id` history.
- Missing or invalid leads and destinations retain explicit errors.
- Verification persists confirmed, non-registered, and temporary-error states.
- An unrelated edited destination does not alter the selected lead's derived status.
- Automatic outreach selects only confirmed numbers.
- Either Brazilian variant triggers the same opt-out in manual and queued sends.
- Lookup timeout returns a temporary result without sending.
- Send timeout records an unknown outcome and warns against retry.
- UI copy no longer represents syntactic phone validity as WhatsApp confirmation.
- Polling continues after `READY` and selected lead context can be cleared.
Run focused tests after each change, then the complete Node test suite and dependency audit. Perform a browser-level manual send-flow check without sending an uncontrolled real message.