// Inline "Talk to sales" form — POSTs to /api/enterprise-contact which // upserts a Person in Attio and attaches a Note with the submission context. // See apps/marketing-site/backend/app/main.py for the backend flow. function ContactForm() { const [values, setValues] = React.useState({ name: "", email: "", company: "", role: "", message: "", }); const [status, setStatus] = React.useState("idle"); // "idle" | "submitting" | "success" | "error" const [error, setError] = React.useState(null); const bind = (field) => ({ value: values[field], onChange: (e) => setValues((v) => ({ ...v, [field]: e.target.value })), disabled: status === "submitting", }); const submit = async (e) => { e.preventDefault(); if (status === "submitting") return; setStatus("submitting"); setError(null); try { const res = await fetch("/api/enterprise-contact", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(values), }); const data = await res.json().catch(() => ({})); if (res.ok) { setStatus("success"); // Fire a PostHog event on successful submission. Email lets us // link the lead back to the Attio Person; the other fields are // useful context on PostHog dashboards. PostHog may not be // loaded in some environments (e.g. local without the snippet), // so guard on window.posthog. if (typeof window !== "undefined" && window.posthog) { window.posthog.capture("enterprise_contact_submitted", { email: values.email, company: values.company, has_role: Boolean(values.role), has_message: Boolean(values.message), }); } } else { setStatus("error"); setError(data.error || "Something went wrong. Please try again."); } } catch (_err) { setStatus("error"); setError("Network error — please try again."); } }; if (status === "success") { return (

Got it

Thanks — we'll be in touch within a business day.

In the meantime, poke around the{" "} playground {" "} or the{" "} docs .

); } const submitting = status === "submitting"; return (

Talk to sales