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
Go to Settings → Website, click New form and choose Custom integration — "Your own form or system posts to a SmileLine endpoint."
Give it a Name that identifies the sender, e.g. "Implants quiz — website".
Pick a Default treatment. This is required — it's the treatment a journey is opened for when the submission doesn't say otherwise.
Optionally pick a Lead source if the submission has no attribution and a Location (for multi-site practices; leave unset to let the submission decide). Posts from your own code usually carry no attribution, so this source is what new patients get — unless the post includes utm_* parameters, in which case the source is worked out from them (see How the lead source is chosen).
Choose Public browser form or Signed JSON webhook under Delivery authentication.
Review the Field mapping. Each box holds the incoming field name that fills a patient field — the defaults (first_name, last_name, email, phone, treatment, location, message, marketing_consent) work for most senders. Leave a box blank to skip that field. You can refine it later against a real submission — see Field mapping.
Create the form, then copy the Endpoint URL from the form's page. If you chose signed delivery, also copy the signing secret from Save the signing secret before closing it. SmileLine never shows that secret again.
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.
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 64 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.
- 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.