SmileLineDocs
Lead capture

Embedding forms

Point a website form at a capture hook URL so submissions become leads automatically.

By the end of this page your website form will post straight into SmileLine — no backend code on your side.

Before you start

You need a capture hook and its endpoint URL. Create one under Settings → Capture hooks and copy the URL from the list — it looks like https://api.smileline.io/capture/YOUR_TOKEN. See Capture hooks.

The endpoint accepts application/json, application/x-www-form-urlencoded and multipart/form-data. URL query parameters are merged underneath the body, so pixel-style integrations can ride the query string alone. File uploads are ignored.

Point your form at the hook

A classic form post. The browser navigates to the endpoint's raw JSON response, so prefer the fetch variant to keep visitors on your page:

<form action="https://api.smileline.io/capture/YOUR_TOKEN" method="POST">
  <input name="first_name" placeholder="First name" />
  <input name="last_name" placeholder="Last name" />
  <input name="email" type="email" placeholder="Email" required />
  <input name="phone" type="tel" placeholder="Mobile" />
  <input name="treatment" type="hidden" value="invisalign" />
  <textarea name="message" placeholder="How can we help?"></textarea>
  <button type="submit">Request a consultation</button>
</form>

Submit in the background and show your own thank-you message. The endpoint allows cross-origin requests, so this works from any website:

const form = document.querySelector("#enquiry-form");
form.addEventListener("submit", async (event) => {
  event.preventDefault();
  const response = await fetch(
    "https://api.smileline.io/capture/YOUR_TOKEN",
    {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        first_name: form.first_name.value,
        last_name: form.last_name.value,
        email: form.email.value,
        phone: form.phone.value,
        treatment: "invisalign",
        message: form.message.value,
      }),
    },
  );
  if (response.ok) showThankYou();
});

Test from the command line before wiring up the website:

curl -X POST "https://api.smileline.io/capture/YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"first_name":"Amelia","email":"amelia@example.co.uk","phone":"+447700900123"}'

A successful capture answers 201 with {"ok":true} — and the lead appears on the Today page in real time.

Which fields are understood

Field names are set per hook in its Field mapping — the left column below shows the defaults a new hook starts with. Rename them in the hook to match whatever your form already sends.

Default field nameFills
first_namePatient first name
last_namePatient last name
emailPatient email
phonePatient mobile number
treatmentMatched against a treatment's slug (see Settings → Treatments); unknown values fall back to the hook's Default treatment
locationMatched against a location id, then a case-insensitive location name; unknown values fall back to the hook's Location — one form can serve every site
messageSaved as a note on the patient timeline

Rules worth knowing:

  • A valid email or phone is required. A submission with neither (after mapping) is rejected with 400.
  • Nothing is thrown away. Fields that aren't mapped are kept verbatim on the lead's capture record and shown under Additional information on the lead panel.
  • Bad values degrade, they don't fail. An unparseable email or phone is kept in the raw capture record while the other contact detail carries the lead.
  • Duplicates attach, they don't multiply. If the email or phone matches an existing patient, the enquiry is attached to them instead of creating a duplicate.

Attribution is automatic

Do not map marketing parameters — they're extracted automatically into the lead's attribution record: utm_* parameters, ad-platform click ids (gclid, fbclid, msclkid, ttclid and many more), landing_url, referrer and user_agent. Just make sure your form forwards the page's query parameters (or posts them alongside the fields). See Attribution.

Rich integrations (e.g. Zapier or a custom tracking script) can also post a structured _sl envelope with visitor context and an event_id idempotency key — re-posting the same event_id never creates a duplicate. The full envelope shape is documented in the API reference under the Capture group at /reference.

Responses

StatusMeaning
201Captured — body is {"ok":true}
400Invalid body, or no valid email/phone after mapping
404Unknown, paused or archived token
429Rate limited — retry after the number of seconds in the Retry-After header

A 404 from a URL that used to work usually means the hook was paused, archived, or its token was regenerated. Check Settings → Capture hooks.

On this page