Dispo Email Review API

Express sub-router that serves the backend for the HubSpot UI Extension (DispoEmails card) and the Lovable backup interface. Manages the dispo email approval queue — listing, previewing, approving, skipping, and bulk-approving queued emails for deal pipelines. Also serves CRMLS pipeline progress and the verification modal for gate-approval workflows. Mounted inside hubspot-handler.js at base path /webhook/hubspot/api/dispo.

Endpoint

MethodPathDescription
GET/webhook/hubspot/api/dispo/emails?dealId=XList queued emails for a deal
POST/webhook/hubspot/api/dispo/approveApprove + send a queued email
POST/webhook/hubspot/api/dispo/skipMark email as skipped
GET/webhook/hubspot/api/dispo/preview/:idRendered HTML preview (for iframe)
POST/webhook/hubspot/api/dispo/approve-allBulk approve all pending for a deal
GET/webhook/hubspot/api/dispo/:dealId/pipelineCRMLS pipeline progress JSON
GET/webhook/hubspot/api/dispo/screenshot/:dealIdMap screenshot PNG
POST/webhook/hubspot/api/dispo/:dealId/pipeline-verifyWrite gate file (approved/rejected)
GET/webhook/hubspot/api/dispo/:dealId/pipeline-uiVerification HTML page (iframe modal)

Port: 18790 (sub-router on hubspot-handler.js, same Express app) Registered FUNNEL path: https://webhook.reri.co/webhook/hubspot/api/dispo (approved in FUNNEL-REGISTRY)

Auth method

Inherits HubSpot HMAC verification from the parent hubspot-handler.js Express application — no separate auth layer in this router. CORS is configured for HubSpot UI Extensions:

Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Methods: GET, POST, OPTIONS

OPTIONS preflight requests return 200 OK immediately.

Payload shape

GET /emails — query param dealId required.

POST /approve:

{ "emailId": "uuid", "dealId": "string" }

POST /approve-all:

{ "dealId": "string" }

POST /:dealId/pipeline-verify:

{ "status": "approved" | "rejected" }

Downstream dispatch

dispo-api.js (sub-router)
 ├─ GET /emails → supabase.from('dispo_email_queue').select() filtered by dealId
 ├─ POST /approve → dispo-email-sender.js (sendQueued) + Discord notify
 ├─ POST /skip → supabase update (status='skipped') + Discord notify
 ├─ GET /preview/:id → fetch template HTML → return rendered preview
 ├─ POST /approve-all → bulk loop over pending → sendQueued() for each
 ├─ GET /:dealId/pipeline → CRMLS pipeline progress query → JSON response
 ├─ GET /screenshot/:dealId → serve PNG from data/screenshots/
 ├─ POST /:dealId/pipeline-verify → write gate file (approved/rejected) to filesystem
 └─ GET /:dealId/pipeline-ui → serve verification HTML page

Supabase table: dispo_email_queue (state tracking for queued emails) Discord channel: #dispo (channel ID 1473827948791463936) via DiscordWebhookSender

Dedup strategy

State managed via dispo_email_queue Supabase table. Approved/skipped emails have status updated — subsequent approve calls on the same emailId are idempotent (upsert/status check). Pipeline-verify gate file writes are idempotent (file overwrite).

Audit trail

  • Logger: log tagged dispo-api via scripts/lib/logger.js.
  • Discord: Approval/skip actions trigger notifications to #dispo channel for real-time visibility.
  • Supabase: All queue state transitions persisted in dispo_email_queue rows.
  • Gate files: pipeline-verify writes to local filesystem as a gate artifact for the verification modal flow.
  • webhook-architecture — This is a sub-router; auth and dedup governance apply from parent hubspot-handler.js
  • hubspot — HubSpot UI Extension (DispoEmails card) calls these endpoints; parent handler HMAC applies
  • _summary — BetterFiles TC workflow uses this API to manage dispo email queue and CRMLS pipeline gates
  • _summary — Acquisition deals move through dispo pipeline tracked here