Open To Close (OTC) Webhook Handler

Receives real-time transaction lifecycle events from Open To Close — creation, updates, stage changes, closings, overdue checklist items, and document uploads. Syncs transaction data to Supabase otc_transactions table and fires Discord alerts to #betterfiles. Mounted on the quo-handler-enhanced Express app via mountOtcRoutes(app).

Endpoint

PathProviderConfigure in
https://discord-bot.reri.co/webhook/otcOpen To CloseOTC Dashboard → Settings → Integrations → Webhooks

Port: Shares 18792 with quo-handler-enhanced.js (mounted via mountOtcRoutes(app)) Not a standalone service — routes are added to the OpenPhone/Quo handler Express app.

Events to register in OTC:

  • transaction.created
  • transaction.updated
  • transaction.stage_changed
  • transaction.closed
  • checklist.item_overdue
  • document.uploaded

Auth method

HMAC-SHA256HMAC(OTC_WEBHOOK_SECRET, rawBody) hex digest, compared against the OTC-provided signature header.

Fail-open: If OTC_WEBHOOK_SECRET is not configured OR the signature header is absent, verification is skipped (handler logs warning and continues). Set OTC_WEBHOOK_SECRET in master.env to enforce.

Sources verified: workspace/knowledge-base/opentoclose/API.md (2026-03-04)

Payload shape

OTC sends JSON transaction objects. Key fields extracted:

FieldSource fieldDescription
otc_property_idtransaction_id / property_id / idOTC unique identifier
property_addressproperty_addressStreet address
property_citycity / property_cityCity
property_statestate / property_stateState abbreviation
property_zipzip / property_zipZIP code
countycountyCounty
contract_titlecontract_titleTransaction name/title
contract_statusstatus / contract_statusCurrent status

Raw payload also stored in raw_field_values JSONB column for agent access.

Downstream dispatch

otc-handler.js (mounted on quo-handler-enhanced)
 ├─ verifyOtcSignature(rawBody, signature) → HMAC check (fail-open)
 ├─ syncTransactionToSupabase(txnData) → supabase.from('otc_transactions').upsert()
 └─ Discord notify → DISCORD_BOT_TOKEN → #betterfiles channel (1473827960677994559)
     on: transaction.stage_changed, transaction.closed, document.uploaded

Supabase table: otc_transactions (upsert keyed on otc_property_id)

Dedup strategy

Upsert semantics on otc_property_id — repeated events for the same transaction overwrite the previous row rather than creating duplicates. For document/checklist events that don’t map to a transaction ID, events are inserted with a generated key.

Audit trail

  • Supabase: otc_transactions upsert serves as the primary audit trail.
  • Discord: #betterfiles channel (ID 1473827960677994559) receives alerts on stage changes, closings, and document uploads.
  • Logging: Handled via the parent quo-handler-enhanced application logging middleware.
  • Sources: workspace/knowledge-base/opentoclose/API.md (field names verified 2026-03-04).
  • webhook-architecture — Cross-handler governance; OTC handler is mounted on quo-handler-enhanced (port 18792), not standalone
  • _summary — BetterFiles agent consumes OTC transaction data for TC workflow and closing coordination
  • hubspot — HubSpot deals are associated with OTC transaction records via property address matching