- Shipped
- September 5, 2026 at 8:31 PM UTC
- Author
- Kamo
- Commit
- 1cb89df
`LeadVendorProduct` gains `applicationEnabled` and `webChatIntegrationId`. A product existing in the CRM is not a decision to publish a form the whole internet can submit to — it is an internal category. Deriving one from the other meant every mortgage product silently had a live public endpoint from the moment it was created, which nobody asked for and nobody could see they had. `webChatIntegrationId` is required to publish. A borrower filling in a mortgage application will have a question, and a form with no way to ask one is where they stop; making the chat part of switching the page on is what stops it being the thing nobody gets round to. A soft id, not a `@ManyToOne`: the integration lives in MediaService's half of the schema and an org may delete one, which a foreign key would make fail against a product in another service's aggregate. `setProductApplication` checks BOTH ids against the organization, not merely against each other — the vendor scoping alone would let anyone holding two matching ids edit another tenant's product — and verifies the chat integration belongs to the same org before storing it. An integration id is attacker-controlled, and a product pointing at another tenant's integration would route a borrower's conversation into that tenant's support queue. `WebChatIntegrationDTO` gains `hostedKeyReady`: the boolean, never the key. A settings screen needs to know whether attaching an integration to a page it does not host will work as-is; handing it a live credential for a list view would not help it do that.