Custom integrations
Give your own form, backend or third-party tool a SmileLine capture endpoint, with optional signed server-to-server delivery.
By the end of this page you'll have a Custom endpoint form — an intake URL your own code posts to — and you'll know how to rotate its credentials, switch its delivery mode, or retire it.
Most websites don't need this. Install the tracking script and SmileLine detects and captures your existing forms without any wiring. Reach for a custom integration when a backend, middleware tool or third-party product needs to deliver leads itself.
What a custom integration is
A custom integration is a website form of type Custom endpoint. It gets its own URL containing an unguessable routing token:
https://api-eu.smileline.io/capture/YOUR_TOKENThat is an EU example. US practices receive api-us.smileline.io. Always copy the complete URL from SmileLine; its hostname is part of the routing boundary.
When a submission arrives, SmileLine matches or creates the patient, opens a journey for the form's default treatment, and records an attribution touch (see Attribution).
Choose one delivery mode per form:
- Public browser form — a website can post directly without an API key or login. Use this with Post leads from your own code.
- Signed JSON webhook — a backend signs the exact JSON body with a separate secret. Use this for server-to-server integrations where the sender can protect a credential.
Only owners and managers can view or manage Settings → Website.
Create one
A custom endpoint is set up in three steps, shown as a Basics · Send a test submission · Confirm the mapping strip at the top of the page until the mapping is confirmed. The endpoint goes live after the first step, but nothing becomes a patient until you confirm the mapping in the third: submissions that arrive in between park safely and replay once you confirm.
Step 1, Basics. Go to Settings → Website, click New form and choose Custom integration. (Settings → Integrations → Inbound webhooks → New inbound webhook opens the same page with that choice made.) Give it a Name that identifies the sender, e.g. "Implants quiz — website". Leave Default treatment as None for general enquiries, or choose a fallback treatment. A matching submitted treatment takes precedence; with neither a match nor a default, a new contact arrives as an enquiry in Today. Optionally choose a Lead source if the submission has no attribution and a Location. Under What arrives here, keep Form submissions unless a phone system will post its calls to this endpoint — then choose Incoming calls (see Incoming calls from your phone system). Then choose Public browser form or Signed JSON webhook under Delivery authentication. Click Continue. If you chose signed delivery, copy the signing secret from Save the signing secret before closing it; Smileline never shows it again.
Step 2, Send a test submission. The form's page shows the Endpoint URL, its status reads Waiting for the first submission…, and the card lists what to do. Copy for AI puts a ready-made brief on your clipboard for whoever builds your website — paste it into ChatGPT, Claude, Cursor or an email to your developer; it contains the endpoint, the delivery mode, the accepted formats, the field names Smileline recognises and a reminder to forward marketing parameters untouched. A public endpoint also shows a Test request: copy the curl line into a terminal to send a sample submission yourself. Signed endpoints show the header recipe instead (see Send signed JSON). The page checks every few seconds; Check now asks straight away. If you can't send one yet, Skip and use conventional names activates the endpoint with the default field names (first_name, last_name, email, phone, telephone, date_of_birth, gender, address, address_line_2, city, county, postcode, country, preferred_contact_method, preferred_contact_time, treatment, location, message, additional_information, marketing_consent).
Step 3, Confirm the mapping. As soon as the submission arrives, SmileLine proposes a field mapping from its fields and lists the payload beside the boxes. A box that names a field turns green with a tick, and the field it names carries a green chip in the First submission panel, so what is still unmapped stands out. Click a field to drop its path into the focused box, adjust anything the proposal got wrong, check the defaults, and click Looks right — activate. The parked submission replays into the CRM and every later submission is captured live.
Marketing parameters need no box: the Attribution · captured automatically group under the mapping lists what Smileline reads on its own (campaign tags, ad click ids, landing page and referrer), and the submission panel groups any it received under Captured automatically, marked Automatic — they cannot be clicked into a box. See Attribution.
The form's page badges it Public form or Signed JSON so you can check its mode at a glance. Its Deliveries tab records everything it receives — see Delivery history.
Incoming calls from your phone system
A custom integration set to Incoming calls turns each post into a call the reception team sees straight away. Point your phone system, PBX or call-tracking tool at the endpoint and have it post one event per incoming call, as soon as the phone rings or as soon as the call ends:
{ "phone": "+447700900123", "first_name": "Sample", "last_name": "Caller" }The caller's number (phone) is the only required field; send the caller's name if the phone system knows it. Without one, the lead is created as Unknown caller — the same name a tracked or missed call gets — and staff fill in the real name from the card. Do not post outbound calls. The field names are mapped from the first event exactly as for a form, so a system that sends caller or from instead of phone works once you confirm the mapping.
What happens on each event:
- Smileline matches the number to an existing patient or creates a new lead, and opens a journey on the form's Default treatment (with none, the caller arrives as an enquiry to triage).
- The lead appears on Today as an Incoming call card, with a phone icon, at the top of New · Call now — above the newest form leads. A repeat caller who already has a card moves back to the top as one fresh card, never a second one.
- A new caller's Lead source is your practice's phone-call source ("Phone call" by default), unless the event carries campaign parameters — which is why the fallback lead-source picker is not offered for incoming calls. If your phone system knows how the caller found you (a tracking number tied to a campaign), forward the
utm_*, click-id,landing_urlandreferrervalues exactly as they arrive and they are read as for any other lead. - Whether the call was answered or missed is not part of the event. Staff record what happened on the card, exactly as for any other lead — see Record an outcome.
Copy for AI on the endpoint's page hands your developer or phone-system vendor a brief written for calls, and the Test request posts a sample call. You can switch an existing endpoint between Form submissions and Incoming calls on its Advanced tab.
Send signed JSON
A signed endpoint accepts application/json at the same /capture/{token} URL. It rejects query parameters and requires these headers:
| Header | Value |
|---|---|
X-SmileLine-Delivery-Id | A unique delivery ID, up to 200 characters. Reuse it only to retry the identical body. |
X-SmileLine-Timestamp | Unix time in seconds, within five minutes of SmileLine's current time. |
X-SmileLine-Signature | v1= followed by the lowercase or uppercase hexadecimal HMAC-SHA256 digest. |
Build the signed bytes as the timestamp, a full stop, the delivery ID, another full stop, and the exact raw request body:
timestamp.deliveryId.rawBodyThis command signs and submits one JSON body. Keep the secret in your backend's secret store; the example environment variable is only a placeholder.
SMILELINE_CAPTURE_URL="https://api-eu.smileline.io/capture/YOUR_TOKEN"
body='{"first_name":"Amelia","email":"amelia@example.co.uk"}'
timestamp="$(date +%s)"
delivery_id="lead-$(uuidgen)"
signature="$(printf '%s' "${timestamp}.${delivery_id}.${body}" | openssl dgst -sha256 -hmac "$SMILELINE_CAPTURE_SECRET" -hex | sed 's/^.* //')"
curl -X POST "$SMILELINE_CAPTURE_URL" \
-H "Content-Type: application/json" \
-H "X-SmileLine-Delivery-Id: ${delivery_id}" \
-H "X-SmileLine-Timestamp: ${timestamp}" \
-H "X-SmileLine-Signature: v1=${signature}" \
--data-raw "$body"A successful delivery answers 201 with {"ok":true}. The body must be a JSON object and is limited to 256 KiB. If the body contains _sl.event_id, it must equal X-SmileLine-Delivery-Id.
Sign the exact bytes you send. Reformatting JSON, changing whitespace, or adding a newline after calculating the HMAC makes the signature invalid.
The Advanced tab
A custom integration's Advanced tab holds its credentials and endpoint lifecycle:
- Endpoint URL — the complete regional URL, with a copy button.
- What arrives here — switch the endpoint between Form submissions and Incoming calls (see Incoming calls from your phone system). Events already received keep the lead they created.
- Enable signed delivery switches a public endpoint to signed JSON. Copy the one-time secret immediately; unsigned browser posts stop working as soon as the mode changes. Disable signing revokes both signing secrets and returns to public mode. Changing the mode keeps the endpoint URL, field mapping and delivery history.
- Rotate signing secret — save the new one-time secret and update the sender. The previous secret remains valid for 24 hours (the page shows when it expires), and SmileLine accepts either secret so you can deploy without an intake gap. Revoke previous secret ends the overlap early — use it once every sender is on the new secret, or when the old one may be compromised.
- Regenerate token — mints a fresh routing token if the URL leaks. Public form URLs are normally visible in website HTML, so treat them as semi-public; a signed endpoint still uses its separate secret for authentication.
After regenerating, the previous endpoint URL stops working immediately. Update every sender that posts to this form first, or submissions will be lost.
Pause, archive and restore
The same lifecycle as every website form — pausing or archiving makes the endpoint answer 404, and restoring keeps a previously paused form paused. See Pause, archive and restore.
Good to know
- Unknown, paused and archived tokens all answer the same
404, so outsiders can't probe which tokens exist. Those requests are never recorded, so they don't reach the delivery history either. - Each endpoint is rate limited independently, so a burst on one integration can't drown out the others.
- A delivery ID is unique within its form, not across the whole practice. Retrying the same ID with identical content returns the stored result and records nothing new. Reusing the same ID with different content is rejected, and that rejection is listed.
- Custom endpoints are exempt from domain pinning — they authenticate by token (and signature), not by origin.
- If a configured default location, treatment or lead source was archived before a submission arrived, SmileLine still preserves the lead and opens an intake-review item for staff. It never applies the archived definition or silently loses the form.
- The endpoint itself is documented in the API reference under the Capture group at /reference.