API docs
This service polls Leadflo for Implant leads every minute, POSTs each new lead to your webhook, then accepts an AI response and writes it back as a Leadflo note (test-named leads only by default).
Base URL https://your-app.azurewebsites.net
All JSON endpoints accept Content-Type: application/json.
Auth
- Dashboard + read APIs are open (no API key) unless you put the app behind Azure Easy Auth / network rules.
- Inbound AI webhook can require
INBOUND_WEBHOOK_SECRETvia headerX-Webhook-SecretorAuthorization: Bearer …. - Outbound webhooks are signed with
X-Signature(HMAC-SHA256 of body) whenWEBHOOK_SECRETis set.
Flow
- Poller scrapes Leadflo
/actions/duefor configured stages. - Filters to tracked treatment types (default: Implant).
- Unknown
patientId→ outboundlead.createdwebhook. - Your agent replies to
POST /api/webhooks/ai-response. - Service writes
POST /v3/patients/:id/noteson Leadflo when allowed.
Endpoints
/api/healthLiveness + basic config flags.
{
"ok": true,
"mode": "live",
"trackedTypes": ["implant"],
"pollIntervalMs": 60000,
"notesOnlyTestNames": true,
"webhookConfigured": true
}
/api/statusLeadflo ping, stats, latest poll run, runtime config.
/api/leadsTracked implant leads (newest first).
{
"leads": [
{
"patientId": "…",
"fullName": "asif test",
"phone": "07599 211739",
"email": "asif@smilefast.com",
"treatmentType": "Implant",
"source": "Practice Website",
"stage": "newLead",
"isTestName": true,
"status": "note_written",
"firstSeenAt": "2026-08-07T13:57:57.353Z",
"webhookSentAt": "…",
"noteWrittenAt": "…",
"aiNote": "…"
}
]
}
/api/eventsRecent activity log (polls, webhooks, notes).
/api/pollRun one scrape immediately (same work as the 1-minute timer).
{ "discovered": 2, "newLeads": 1, "leads": [/* NormalizedLead */] }
/api/webhooks/ai-responseInbound: AI / n8n posts the note to write into Leadflo.
{
"patientId": "leadflo-patient-id",
"note": "AI-written note content",
"title": "",
"force": false
}
Responses:
note_written— posted to Leadflonote_skipped— stored here, skipped Leadflo write (non-test name)note_failed/unknown_lead— error
/api/leads/:patientId/notesSame as inbound webhook; used by the dashboard Note dialog.
Outbound webhook (new lead)
When a new implant lead is discovered, we POST to WEBHOOK_URL:
{
"event": "lead.created",
"platform": "leadflo",
"trackedTreatmentFilter": ["implant"],
"lead": {
"patientId": "…",
"firstName": "asif",
"lastName": "test",
"fullName": "asif test",
"phone": "07599 211739",
"email": "asif@smilefast.com",
"treatmentType": "Implant",
"source": "Practice Website",
"stage": "newLead",
"dueDate": "2026-08-07",
"labels": ["Completed Implant Contact Form"],
"isTestName": true,
"scrapedAt": "2026-08-07T13:57:57.353Z"
},
"callback": {
"noteWebhook": "https://your-app.azurewebsites.net/api/webhooks/ai-response",
"description": "POST JSON { patientId, note, title? } when AI has a response."
}
}
Headers: X-Webhook-Secret, X-Signature (if WEBHOOK_SECRET set).
Inbound AI note
With NOTES_ONLY_TEST_NAMES=true (default), Leadflo notes are only written when
fullName contains test. Pass "force": true to override.
Examples
# Health
curl -s "$BASE/api/health" | jq
# Force a scrape
curl -s -X POST "$BASE/api/poll" | jq
# List tracked leads
curl -s "$BASE/api/leads" | jq '.leads[] | {fullName, status, isTestName}'
# AI note write-back (test lead)
curl -s -X POST "$BASE/api/webhooks/ai-response" \
-H 'Content-Type: application/json' \
-H "X-Webhook-Secret: $INBOUND_WEBHOOK_SECRET" \
-d '{"patientId":"…","note":"Thanks for your implant enquiry — we will call shortly."}' | jq