This note documents how form submissions on the Ownit Conveyancing website reach Salesforce, clarifies the role (and non-role) of third-party tooling such as N8N and the legacy Web-to-Lead integration, and describes the logging available for diagnosing intermittent failures.
The website forms do not use the classic Salesforce Web-to-Lead servlet. They post directly into the Salesforce org through the Experience Cloud (LWR) Apex runtime hosted on the customer's own Salesforce domain:
POST https://forms.ownitconveyancing.com/webruntime/api/apex/execute
?language=en-US&asGuest=true&htmlEncode=false
This is Salesforce's modern, supported replacement for Web-to-Lead on Experience Cloud sites. Records land directly in the Salesforce org via a configured Apex class, the Web-to-Lead reporting page will therefore show no entries by design; that integration is not part of this flow.
https://forms.ownitconveyancing.com/quote-request. That page is the previous public quote form, built directly on Salesforce Experience Cloud (LWR). The new ownitconveyancing.com.au site calls the same endpoint, with the same envelope, in the same way that page's own JavaScript does. We are simply invoking the existing Salesforce integration from a different front end, the back-end contract is owned and controlled by Salesforce.
Implication Any response the website receives from this endpoint is also the response Salesforce's own original form receives. The "empty 200" behaviour described in section 5 can be reproduced by submitting the same input combination on forms.ownitconveyancing.com/quote-request directly, the failure mode is on the Salesforce side and is not specific to the new website.
01p5g00000oJUoJcreateQuoteRequest: creates the Quote Request recordsubmitQuoteRequest: submits the created recordsubmitLead: used by the Contract Review formuploadFile: used for contract attachments
Note The Next.js API route is a thin server-side proxy. It does not transform business data; it wraps the payload in the standard Salesforce Apex envelope (namespace, classname, method, params) and forwards it. This is the same pattern Salesforce uses internally when a Lightning Web Component calls Apex on an Experience Cloud site.
In parallel with returning the Salesforce response to the browser, the API route writes a structured log entry to our Strapi instance (collection instant-quote-logs) capturing the inbound frontend request, the outbound Salesforce payload, and the Salesforce response. Logs are retained for 7 days and are the primary diagnostic source for the failures discussed in section 4. The log write is fire-and-forget, it does not block or affect the user-facing response, and a logging outage cannot cause a form submission to fail.
| Endpoint | Purpose | Salesforce Apex method |
|---|---|---|
/api/create-quote | Submits a new Quote Request | createQuoteRequest |
/api/submit-quote | Finalises an existing Quote Request by record ID | submitQuoteRequest |
/api/submit-contract-review | Submits a Contract Review lead | submitLead |
/api/upload-contract | Attaches contract files to a Quote / Lead | uploadFile |
Per team knowledge, N8N was used at an earlier stage of the integration to broker requests between the website and Salesforce. It has since been retired in favour of the direct Apex submission path described in section 2, which removes a hop from the chain, eliminates a third-party point of failure, and lets Salesforce receive the payload in the exact shape its Apex methods expect. This historical context comes from the delivery team rather than from evidence in the current code (which is consistent with the integration having been fully removed).
Practical implications:
undefined responsesEvery Salesforce request and response from the website is logged server-side for the last 7 days. Each log entry captures the following fields:
| Field | Contents |
|---|---|
frontendRequest | Endpoint hit and the raw body received from the browser. |
salesforceRequest | Exact URL and JSON payload sent to Salesforce. |
salesforceResponse | HTTP status code and full response body returned by Salesforce. |
createdAt | Timestamp. |
For any failed submission, this allows determination of whether:
undefined values surfacing in the UI.undefined behaviour observed in the UI.
The full Strapi log store was queried for the 7 days ending 2026-06-22 02:00 UTC. Total entries: 274 (250 to /api/create-quote, 24 to /api/submit-quote; the /api/submit-contract-review endpoint is not yet covered by the logger, see section 6.4).
Despite the clean HTTP status, 10 of the 250 createQuoteRequest responses returned a 200 with an empty body:
{"returnValue": {"statusCode": 200}, "cacheable": false}
No mainProduct, no fixedFeeProduct, no childProducts, no quoteRequest. The website expects these fields to render the quote, so the UI reads them as undefined. This is the symptom users have been reporting.
A clear pattern emerged across the 10 empty responses:
| Field | Affected values |
|---|---|
state | QLD (9 / 10), VIC (1) |
propertyType | Off-the-Plan Land (6), Off-the-Plan Unit or Townhouse (2), Commercial Building (2) |
transactionType | Sale (6), Transfer (4), never Purchase |
formType | form-old-version (10 / 10) |
Across all 250 createQuoteRequest calls the empty-response rate is 4 %. But when scoped to the specific property types involved, the picture matches the client's "15-20 %" report:
| Property type | Empty / Total | Rate |
|---|---|---|
| Off-the-Plan Land | 6 / 22 | 27 % |
| Off-the-Plan Unit or Townhouse | 2 / 5 | 40 % |
| Commercial Building | 2 / 6 | 33 % |
| House (most common) | 0 / 134 | 0 % |
| Unit | 0 / 59 | 0 % |
| Land | 0 / 21 | 0 % |
This also explains the "one minute it works, the next minute it doesn't" observation, the same user can test successfully on a House (Purchase) and then fail on Off-the-Plan Land (Sale) in QLD. The failure is not random; it is tied to the combination of (state, propertyType, transactionType).
One further entry (log id 3845, QLD Sale) returned a populated mainProduct but was missing fixedFeeProduct. Same root-cause family, a partial product configuration in Salesforce rather than a transport problem.
create-quote = 250, submit-quote = 24. The 9.6 % rate is user funnel drop-off after viewing the quote, not a system failure. Mentioning here so it is not misread as a high failure rate.
createQuoteRequest Apex method appears to silently return 200 with an empty body when no Product record matches the requested (state, transactionType, propertyType) combination. The website cannot distinguish this from a real success and falls back to rendering undefined values.
The website is faithfully relaying whatever Salesforce returns. The fix needs to happen in two places: data in Salesforce (so the matching products exist) and contract in Apex (so the website is told clearly when something is missing).
Product record exists for these combinations.createQuoteRequest) to return an explicit error response (e.g. statusCode: 404 with a message) when no product matches, instead of an empty 200.undefined fields. Small piece of work, flagged here as a recommendation rather than included as part of the investigation./api/submit-contract-review (optional). The Contract Review endpoint integrates with Salesforce via the same Apex executor (method submitLead, creating Lead records) but is not currently feeding the log store, so Contract Review failures are invisible. Adding the same logging treatment is a small piece of work and can be quoted separately if Ownit wants visibility extended across all forms.| Question | Answer |
|---|---|
| Why is Salesforce Web-to-Lead empty? | By design. The website uses Salesforce Experience Cloud Apex, not the legacy Web-to-Lead servlet. Records still land in the Salesforce org, through a modern, supported integration. |
| Is N8N involved in form submissions? | Not in the current production flow. The website code base contains no N8N references. The site posts directly to Salesforce via the Apex executor. Per team knowledge, N8N was used in an earlier integration stage and has since been retired; any legacy workflow found is no longer invoked. |
| What is causing the 15-20 % failures? | Likely a Salesforce-side product configuration gap. Specific combinations (mostly QLD + Off-the-Plan or Commercial property types + Sale/Transfer) return a 200 with no product data, which the UI renders as undefined. Common combinations (House, Unit, Land) are unaffected. See section 5 for the full breakdown. |