Capture & integration
Save the enquiry before handing it on.
It started with…
A visitor submits an enquiry. An unavailable downstream service should not erase it or force the visitor to start again.
So we built…
An enquiry saved in Supabase, followed by an HTTP notification with a stable shape for a CRM or orchestrator. The submission and its delivery are separate responsibilities.
- Vercel
Form
Server-side validation
- Supabase
Saved enquiry
The enquiry survives downstream failure
- Zapier
Possible hand-off
Qualification and routing to CRM
Capture and the webhook are documented; the Zapier hand-off is an integration option to validate against the actual Zap.
What we set out to change
Retain a reliable record of the enquiry and let the business follow its handling.
How it is builtData model, code, controls and team ownership
The user journey
- 01
Validate fields and save the submission.
- 02
Notify the configured destination with the form context.
- 03
Track delivery and business handling separately.
Data and hand-off structure
| Entity / table | Key fields | Relationships |
|---|---|---|
| Form configurations | Type · validation · destination | Define the submitted payload |
| Submissions | Form · fields · received time | Retain the original enquiry |
| CRM / delivery reference | Submission key · destination · outcome | Proposed operational follow-up |
Technical implementation
- The reviewed code saves the enquiry before the HTTP notification.
- The webhook uses a common shape across forms and a timeout.
- A durable outbox and retry mechanism are a recommended evolution for critical flows, not a guarantee of the current notifier.
Engineering decision
Bound notification time without losing the saved enquiry
Adapted from the reviewed webhook helper, called after the form submission has been saved. The request is aborted after five seconds, HTTP and transport failures return diagnostics, and the timer is always cleaned up. This does not provide a durable retry queue or prove downstream processing. A timeout can mean that the destination received the request but its response was lost.
type Delivery = | { ok: true } | { ok: false; reason: "disabled" | "timeout" | "http-error" | "network" };export async function notifySavedEnquiry( configuredUrl: string | null, payload: { form_type: string; data: Record<string, unknown> },): Promise<Delivery> { if (!configuredUrl) return { ok: false, reason: "disabled" }; const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), 5_000); try { const response = await fetch(configuredUrl, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(payload), signal: controller.signal, }); return response.ok ? { ok: true } : { ok: false, reason: "http-error" }; } catch (error) { return { ok: false, reason: error instanceof Error && error.name === "AbortError" ? "timeout" : "network", }; } finally { clearTimeout(timer); }}// URL comes from trusted server configuration, not the visitor's form.// The persisted submission remains the source of truth for follow-up.Adapted implementation excerpt
Controls and boundaries
- Send only the fields required by the recipient.
- A delivered webhook does not prove the enquiry has been handled.
- Validate the destination connector and its recovery behaviour before release.
Who can contribute
- Operations: qualification and assignment rules.
- Builders: approved forms and mappings.
- Developers: validation, access, delivery and recovery.
What supports this case
Capture and notification code reviewed. The payload supports a Zapier, n8n or CRM destination; no downstream Zap has yet been verified.