Dental Practices / Technical Guide

Automate Patient Recall & Reactivation for Dental Practices with n8n + Claude HIPAA-Aware Step-by-Step Guide

A complete walkthrough for building an n8n + Claude pipeline that pulls inactive patients from Open Dental or Dentrix nightly, segments them by clinical priority, drafts a personalized recall message per patient, sends HIPAA-compliant SMS via TigerConnect or RingCentral, parses replies, books open hygiene slots automatically and writes everything to a 6-year audit log.

15 min read
Intermediate-Advanced
n8n + Claude API + Open Dental
Updated May 2026
What You’ll Build

Nightly PMS pull (Open Dental / Dentrix)

Claude segmentation (4 cohorts)

Personalized message draft

HIPAA SMS (TigerConnect / RingCentral)

Reply parse — book / question / opt-out

Auto-book into Open Dental slot

Encrypted 6-year audit log

1. The Problem — Why Recall Lists Quietly Bleed Revenue

Every general dentistry practice in the US has the same un-fixed leak: a recall list that grows faster than the front desk can work through it. Six-month hygiene comes due, the front desk pulls a report from Open Dental on Friday afternoon, an assistant calls the first 30 patients, leaves voicemail on 22 of them, books 4, and then Monday’s chaos buries the rest of the list until next month. The hygiene chair sits empty for 6 hours a week. Treatment plans presented in March quietly age out unfilled. The same patient who said “yes, I’ll come back for that crown” never gets a second nudge.

Real numbers from a 4-chair general practice (3,800 active patients)

Active patient base~3,800
Hygiene recall recovery rate (industry baseline)12%
No-show rate on hygiene appointments35%
Treatment plans presented but unscheduled at 90 days61%
Front-desk hours per week on recall outreach28
Estimated unrealized recall revenue per year~$80,000

The brutal arithmetic: at a typical $200 hygiene visit and ~$1,400 average treatment-plan close, even a 15-point lift on recall recovery and a meaningful dent in 90-day plan rot pays for the system in week one. The constraint is not patient willingness — most lapsed patients are genuinely surprised they fell off the schedule. The constraint is throughput at the front desk, which is already running on coffee and overtime.

What “recall automation” means here

Recall is not a blast SMS that says “It’s time for your cleaning!” to 800 people on the first of the month. That kind of campaign trains patients to mute the practice. The system in this guide segments patients into clinically meaningful cohorts, drafts a message that references each patient’s last visit, last hygienist and last treatment, and only escalates to a human at the front desk when the patient asks something the workflow cannot answer with confidence:

  • Overdue 6-month hygiene: standard recall, friendly tone, offer 2 specific open slots from the actual schedule.
  • Overdue 12-month+: warmer reactivation message, addresses the gap directly, offers a no-pressure check-in.
  • Treatment-plan-pending: references the specific procedure, the dentist’s name, and any insurance-year-end pressure.
  • High-value no-show: patient with a recent missed appointment plus open treatment plan. Flagged for a personal call by the office manager, not auto-SMS.
Insight
The single biggest lift comes from patients who lapsed at 9–14 months. They’re past the hygiene cadence but not yet in “I should find a new dentist” territory. A specific message that references their last hygienist by name books at roughly 3.5x the rate of a generic “we miss you” SMS. We use this same context-anchored messaging pattern across our dental practice automation deployments.

2. System Architecture

Eight components, each replaceable, each in scope of the practice’s HIPAA risk analysis. The orchestration layer is self-hosted n8n on a VM that lives inside the practice’s network or in a HIPAA-eligible cloud account, so PHI never traverses a third-party automation SaaS. Postgres holds the patient feature store, the message log, the audit trail and the consent ledger.

The stack

n8n (self-hosted)
Orchestration. Docker on a 4 vCPU / 8GB VM inside a HIPAA-eligible AWS or GCP account, with TLS-only ingress and queue mode.
Claude API (with BAA)
Sonnet for segmentation reasoning and message drafting, Haiku for cheap reply intent classification. BAA executed before any PHI touches the API.
Open Dental / Dentrix
Practice management. Open Dental via FHIR API (preferred) or read-only DB replica; Dentrix via the Dentrix Developer Program API.
TigerConnect / RingCentral
HIPAA-compliant SMS surface, both with executed BAAs. TigerConnect for in-network secure messaging; RingCentral for standard SMS to consumer phones with PHI-redacted message body.
Postgres (encrypted at rest)
Patient feature snapshot, message log, audit trail, consent and opt-out ledger. AES-256 disk encryption + column-level encryption on PHI fields.
Slack (PHI-redacted)
Front-desk handoff for messages the workflow flags as complex. Identifiers are tokenized; full PHI lives only behind a click-through into the practice intranet.

Cost estimate (3,800-patient practice, ~900 outbound recall messages/month)

Claude Sonnet (segmentation + drafting, ~900 patients, ~2,200 tok in / 280 tok out)~$32
Claude Haiku (reply intent classification, ~600 inbound messages)~$4
RingCentral HIPAA SMS (~900 outbound + ~600 inbound, $0.014/SMS blended)~$21
VM (n8n + Postgres on AWS HIPAA-eligible t3.large)~$74
Encrypted backup + audit log retention (S3 + Object Lock, 6 yr)~$18
Monitoring + alerting (Grafana Cloud + Healthchecks)~$24
Total / month~$173

Even one recovered $1,400 treatment-plan close per month covers the full stack with $1,200 left over. The ROI math is so favorable that the actual decision criterion is rarely cost; it is whether the practice trusts the workflow to handle PHI without creating a compliance incident. The same orchestration spine fits into our broader AI automation services.

Compliance
No vendor in this stack stores PHI without an executed BAA. That includes Anthropic (use the enterprise tier with a signed BAA and zero-data-retention), the SMS provider, the cloud host, and the monitoring vendor. Slack is in the architecture only because messages routed there are PHI-redacted at the n8n boundary; full PHI never enters Slack workspaces.
1

Nightly Patient Pull from Open Dental / Dentrix

The pipeline starts with a deterministic, idempotent nightly pull at 1:30 AM local time, when the practice management system is quiet and the front-desk computers are off. Open Dental publishes a FHIR R4 endpoint (since v22) which gives a clean, vendor-stable contract. Dentrix exposes a more limited Developer Program API; for older Dentrix installs, a read-only SQL Server replica is the most reliable option.

Data sources

  • Patient demographics: first/last name, preferred name, mobile phone, primary email, communication preferences, language preference, opt-out flags.
  • Appointment history: last completed visit, last hygiene visit, last hygienist of record, last provider, no-show count in last 24 months.
  • Treatment plans: any plan with status “presented” or “accepted, not scheduled” — procedure code, tooth number, presented date, presenting provider, fee.
  • Insurance: primary carrier, plan year end, remaining annual maximum (if available — most carriers publish via real-time eligibility).
  • Recall: next recall due date as scheduled in the PMS, recall type (perio, prophy, child).

n8n cron + FHIR pull

A single Cron node fires at 01:30 local. A Function node computes the lookback window. A loop of HTTP Request nodes paginates through Open Dental’s FHIR Patient and Appointment resources, normalizing into a flat row schema before insert into Postgres. Every PHI field stays inside the practice’s network or HIPAA-eligible VPC.

n8n HTTP Request — Open Dental FHIR Patient pullJSON
{
  "method": "GET",
  "url": "https://opendental.localnet/fhir/v2/Patient",
  "qs": {
    "_lastUpdated": "ge{{ $json.windowStart }}",
    "_count": "200",
    "_sort": "_lastUpdated"
  },
  "headers": {
    "Authorization": "Bearer {{ $credentials.opendental.token }}",
    "Accept": "application/fhir+json"
  },
  "options": {
    "timeout": 15000,
    "retry": { "attempts": 3, "backoffMs": 500 }
  }
}

Patient feature snapshot table

Postgres — patient_snapshot (encrypted PHI columns)SQL
CREATE TABLE patient_snapshot (
  patient_id              text PRIMARY KEY,
  pms_source              text NOT NULL CHECK (pms_source IN ('open_dental','dentrix')),
  preferred_name_enc      bytea,        -- pgcrypto pgp_sym_encrypt
  mobile_e164_enc         bytea,
  email_enc               bytea,
  language_pref           text DEFAULT 'en',
  consent_sms             boolean DEFAULT false,
  consent_recorded_at     timestamptz,
  opt_out_at              timestamptz,
  last_visit_date         date,
  last_hygiene_date       date,
  last_hygienist_name     text,
  last_provider_name      text,
  no_show_count_24mo      int DEFAULT 0,
  open_tx_plan            jsonb,        -- procedures + fees + presented_at
  insurance_year_end      date,
  remaining_annual_max    numeric(8,2),
  recall_due_date         date,
  snapshot_taken_at       timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX idx_snapshot_recall_due ON patient_snapshot (recall_due_date)
  WHERE opt_out_at IS NULL AND consent_sms = true;
Watch Out
Older Dentrix installs (G6 and earlier) ship without a clean API. The temptation is to scrape the Dentrix Reports module — don’t. Instead, use the Dentrix Developer Program key plus a read-only SQL Server account scoped to the specific tables you need. Document the access in your HIPAA risk analysis and rotate the credential quarterly.
2

Claude Segmentation into Recall Cohorts

A deterministic SQL query can identify “patients overdue for hygiene” trivially. The harder problem is sorting that list into cohorts that justify a different message, a different time of day to send, and a different escalation path. Claude reads each patient’s snapshot row, applies the practice’s documented recall policy, and returns a structured classification with a one-sentence rationale. This pattern shows up in our adjacent work for medical clinics and healthcare automations.

The 4 segmentation cohorts

overdue_6mo
Last hygiene 6–11 months ago, no future appointment, consented to SMS.
overdue_12mo_plus
Last hygiene 12+ months ago. Reactivation tone, longer message, no slot pressure.
tx_plan_pending
Open treatment plan presented >30 days ago, not scheduled, insurance year-end relevant.
high_value_no_show
2+ no-shows in 24 months AND open treatment plan >$500. Front-desk call only — no auto-SMS.

Claude segmentation system prompt

The prompt encodes the practice’s actual recall policy. Tunable per practice by editing the policy block. PHI in the user message is the minimum necessary — only the fields the prompt actually consumes.

Claude system prompt — recall segmenterTXT
You are a recall segmentation engine for a US general dentistry
practice operating under HIPAA.

PRACTICE RECALL POLICY:
- Hygiene cadence: 6 months for healthy adults, 3-4 months for
  active perio maintenance, 12 months for medically compromised
  with documented exception.
- Treatment-plan reactivation window: 30-180 days post-presentation.
  After 180 days, plan must be re-discussed with the dentist.
- High-value threshold: open plan total >= $500.
- High-no-show threshold: 2+ no-shows in trailing 24 months.

INPUT: a single patient_snapshot JSON object containing minimum
necessary PHI - preferred_name (do not echo back), last_hygiene_date,
last_hygienist_name, no_show_count_24mo, open_tx_plan summary,
insurance_year_end, remaining_annual_max, recall_due_date.

DISQUALIFY signals (do not classify into any send cohort):
- consent_sms = false
- opt_out_at is not null
- patient is a minor without parent on file
- mobile_e164 missing or invalid

OUTPUT - strict JSON, no prose, no PHI echoed:
{
  "cohort": "overdue_6mo" | "overdue_12mo_plus" | "tx_plan_pending"
          | "high_value_no_show" | "skip",
  "send_channel": "sms" | "front_desk_call" | "none",
  "priority": 1-5,
  "rationale": "one sentence, no patient name, no phone",
  "year_end_lever_applies": true | false
}

Rules:
- if no_show_count_24mo >= 2 AND open plan total >= 500,
  cohort = "high_value_no_show", send_channel = "front_desk_call".
- if multiple cohorts apply, choose the one with highest expected
  reactivation impact (tx_plan_pending > overdue_12mo_plus >
  overdue_6mo).
- year_end_lever_applies = true only if remaining_annual_max > 0
  AND insurance_year_end within 75 days.
- never invent fields not in the input.

Expected output for a typical row

Claude response — segmentation resultJSON
{
  "cohort": "tx_plan_pending",
  "send_channel": "sms",
  "priority": 4,
  "rationale": "Plan presented 47 days ago, ~$1,840 of insurance benefit unused, plan year ends in 61 days.",
  "year_end_lever_applies": true
}
Insight
Persist the rationale string into the audit log alongside the segmentation result. When a regulator or a patient asks “why did you contact me about this,” the answer is a complete record of the policy that fired and the data that justified it — without needing to re-run the model.
3

Personalized Message Drafting Per Cohort

A second Claude call drafts the actual message. The drafting prompt is constrained heavily — message length budget, no medical advice, no hard sell, no fee quotes, no insurance benefit numbers in the message body (the patient sees those in the practice portal, not in SMS), and a hard requirement to include the practice name and an opt-out path. The personalization comes from referencing the last hygienist by first name, the last visit timing in plain English, and the procedure type by lay term, not the CDT code.

Drafting constraints by cohort

CohortLengthSlots offeredSend window
overdue_6mo≤ 220 chars2 specific timesTue–Thu, 10:00–17:00 local
overdue_12mo_plus≤ 280 chars“Reach out and we’ll find a time”Tue–Thu, 11:00–16:00 local
tx_plan_pending≤ 280 chars1 specific time + “or a quick call”Wed–Thu, 10:00–15:00 local
high_value_no_shown/a (call)n/a — front desk onlyOffice manager handles personally

Drafting prompt

Claude system prompt — message drafterTXT
You draft SMS recall messages for {{ practice_name }}, a US
general dentistry practice operating under HIPAA.

ABSOLUTE RULES:
1. Never include diagnosis, procedure CDT codes, fee amounts,
   insurance balances, dental conditions, or anything that would
   constitute PHI beyond the patient's first name.
2. Always include the practice name.
3. Always include an opt-out instruction: "Reply STOP to opt out".
4. Maximum 280 characters total. Hard cap.
5. No emojis except a single optional smile or wave at the end.
6. Use the patient's preferred first name once, never the last name.
7. Reference the last hygienist by first name only and only if
   provided in the input.
8. Use lay language for procedures: "the crown we discussed",
   "your cleaning visit", never "D2740" or "perio maintenance".
9. Sign off with a real human name from the practice (provided in
   input) - never "the team" and never "automated message".

INPUT contains:
- patient first name (preferred), cohort, last hygiene timing
  in plain English ("about 9 months ago"), last hygienist first
  name (optional), procedure lay term (optional), two specific
  slot offers (cohort-dependent), practice phone, signer name.

OUTPUT - strict JSON:
{
  "message": "the SMS body, complete and sendable",
  "char_count": integer,
  "phi_audit": "none" | "review_required",
  "tone": "friendly" | "warm" | "gentle"
}

If the message would require any PHI beyond first name to be
useful, return phi_audit = "review_required" and a generic
message that asks the patient to call.

Sample outputs (PHI-redacted in this guide)

Sample drafts — overdue_6mo and tx_plan_pendingTXT
// overdue_6mo
"Hi Sarah, it's Maple Family Dental. Your cleaning with Jess
is due. We have Tue 11:30 or Thu 2:15 next week - reply with
the one that works, or call (614) 555-0142. Reply STOP to opt
out. - Megan, Front Desk"

// tx_plan_pending (year_end_lever_applies = true)
"Hi James, Maple Family Dental here. The crown we talked about
is still on your chart. We have a slot Wed 10:30 next week if
you'd like to wrap it up before your benefits reset. Reply YES
to confirm or call (614) 555-0142. STOP to opt out. - Megan"
Insight
“Reply YES to confirm” outperforms “Click here to book” by roughly 2.3x in our deployments. SMS is a conversational channel; patients answer questions but click links reluctantly. Save the link for the confirmation message after they’ve already said yes.
4

HIPAA-Compliant SMS Send

Standard Twilio is not HIPAA-eligible without specific configuration and a signed BAA, and even then it requires field-level care about what enters the message body. The cleanest path is a vendor explicitly built for healthcare messaging — TigerConnect for in-network secure messaging or RingCentral with a healthcare BAA for standard SMS to patient mobile phones. Either way, three rules drive the integration: BAA in place, message body free of any PHI beyond first name, and every send written to the audit log before the API call returns.

Send-time guardrails

  1. Quiet hours: never send between 21:00 and 09:00 in the patient’s local timezone, even if the workflow runs at 02:00 — queue and release at the local window opening.
  2. Day-of-week throttle: Tuesday/Wednesday/Thursday default; Monday and Friday are noisy and convert worse.
  3. Daily volume cap: 250 outbound recalls per day per practice number. Caps protect the carrier reputation score and prevent runaway sends if the segmenter misbehaves.
  4. Pre-send opt-out check: re-read the consent ledger inside the same transaction that issues the API call. Stale snapshots cause TCPA exposure.
  5. Idempotency key: the n8n workflow run ID + patient ID. Replay-safe sends are non-negotiable.

RingCentral SMS API call

n8n HTTP Request — RingCentral SMS sendJSON
{
  "method": "POST",
  "url": "https://platform.ringcentral.com/restapi/v1.0/account/~/extension/~/sms",
  "headers": {
    "Authorization": "Bearer {{ $credentials.ringcentral.token }}",
    "Content-Type": "application/json",
    "X-Idempotency-Key": "{{ $execution.id }}-{{ $json.patient_id }}"
  },
  "body": {
    "from": { "phoneNumber": "+16145550142" },
    "to":   [ { "phoneNumber": "{{ $json.mobile_e164 }}" } ],
    "text": "{{ $json.message }}"
  },
  "options": {
    "timeout": 8000,
    "retry": { "attempts": 2, "backoffMs": 1000 }
  }
}

Audit log write — same transaction as the API call

Postgres — message_log (append-only, encrypted body)SQL
INSERT INTO message_log (
  message_id, patient_id, direction, channel, vendor,
  cohort, message_body_enc, char_count,
  phi_audit_flag, segment_rationale,
  consent_state_at_send, sent_at, vendor_message_id
) VALUES (
  gen_random_uuid(), $1, 'outbound', 'sms', 'ringcentral',
  $2, pgp_sym_encrypt($3, current_setting('app.col_key')), $4,
  $5, $6,
  $7, now(), $8
)
RETURNING message_id;
-- Trigger app_audit_after_insert writes a tamper-evident row to
-- the immutable audit_trail table partitioned by year for 6yr
-- retention per HIPAA section 164.530(j).
Compliance
The audit log is the artifact a HIPAA auditor wants to see. Make it append-only at the database level (no UPDATE, no DELETE permitted on the table for any role except the retention purger), partition by year, and back it up to S3 with Object Lock for the full 6-year retention window. Loss of this log is the single hardest-to-defend issue in a HIPAA breach investigation.
5

Two-Way Reply Parsing

A recall SMS has three meaningful response shapes: a booking intent (“YES Tuesday works”), a non-booking question (“Can I bring my husband too?”), or an opt-out signal (“STOP” / “remove me”). The reply parser is a Claude Haiku call — cheap, fast, well-suited to short-text classification with high confidence — that returns a structured intent plus the slot the patient committed to, if any. Anything ambiguous routes to the front-desk handoff queue rather than guessing.

Reply intent classifier prompt

Claude Haiku system prompt — reply parserTXT
You classify SMS replies to a dental recall message. The original
message offered up to two appointment slots in plain English
(e.g., "Tue 11:30" and "Thu 2:15") and a phone number.

INPUT contains:
- the original outbound message
- the patient's reply text
- the two slot strings that were offered (if any)

OUTPUT - strict JSON, no prose:
{
  "intent": "book" | "question" | "opt_out" | "reschedule_request"
          | "wrong_number" | "ambiguous",
  "selected_slot": "slot_a" | "slot_b" | "neither" | null,
  "needs_human": true | false,
  "opt_out_phrase_detected": true | false,
  "confidence": 0.0-1.0
}

CRITICAL:
- Detect any STOP / UNSUBSCRIBE / REMOVE / DON'T TEXT ME variant
  with high recall. Set opt_out_phrase_detected = true and
  intent = "opt_out". TCPA non-negotiable.
- Any reply that asks a clinical question (pain, symptoms,
  medication, bleeding, swelling) is "question" with
  needs_human = true regardless of confidence.
- Confidence below 0.75 -> needs_human = true.
- Never invent a slot the patient did not name.

Routing matrix from parser intent

IntentActionFront desk?
bookAuto-create appointment in PMS, send confirmation SMS.Notification only
reschedule_requestHold the offered slots, draft 3 alternatives, send back.Notification only
questionAcknowledge, route to Slack handoff queue.Yes — within 1 business hr
opt_outSet opt_out_at, send confirmation, never re-include.Notification only
wrong_numberMark mobile invalid, suppress further SMS, flag PMS.Yes — verify on next call
ambiguousNo action by workflow.Yes — within 4 business hr
6

Auto-Book into Open Dental

When the parser returns intent = "book" with high confidence, the workflow re-checks slot availability against the live schedule (the offered slot may have been claimed by another booking in the interval) and creates the appointment via the Open Dental Appointment FHIR resource. If the slot is no longer available, the workflow falls back to drafting a 3-alternative response rather than booking the patient blind into something else.

Slot revalidation query

Open Dental — slot availability checkJSON
{
  "method": "GET",
  "url": "https://opendental.localnet/fhir/v2/Slot",
  "qs": {
    "schedule": "{{ $json.hygienist_schedule_ref }}",
    "start_ge": "{{ $json.slot_start_iso }}",
    "start_lt": "{{ $json.slot_end_iso }}",
    "status": "free"
  },
  "headers": {
    "Authorization": "Bearer {{ $credentials.opendental.token }}"
  }
}

Appointment create call

Open Dental FHIR — Appointment createJSON
{
  "method": "POST",
  "url": "https://opendental.localnet/fhir/v2/Appointment",
  "headers": {
    "Authorization": "Bearer {{ $credentials.opendental.token }}",
    "Content-Type": "application/fhir+json",
    "X-Idempotency-Key": "{{ $execution.id }}-{{ $json.patient_id }}-book"
  },
  "body": {
    "resourceType": "Appointment",
    "status": "booked",
    "appointmentType": {
      "coding": [{
        "system": "http://opendental.com/codes/apt-type",
        "code": "PROPHY_ADULT",
        "display": "Adult Prophy + Exam"
      }]
    },
    "start": "{{ $json.slot_start_iso }}",
    "end":   "{{ $json.slot_end_iso }}",
    "participant": [
      { "actor": { "reference": "Patient/{{ $json.patient_id }}" },
        "status": "accepted" },
      { "actor": { "reference": "Practitioner/{{ $json.hygienist_ref }}" },
        "status": "accepted" }
    ],
    "comment": "Booked via recall workflow. Source: SMS reply."
  }
}

Confirmation SMS draft (returns to step 4 send path)

Once the Appointment resource creates with HTTP 201, the workflow drafts a short confirmation SMS, runs it through the same PHI guardrails, and sends. The confirmation includes the date, time, hygienist first name, parking note (a short practice-defined string from a config table), and a “reply C to cancel” path so the patient never has to call to reschedule. The cancel handler is the same intent classifier in reverse.

Insight
The appointment-type code in the FHIR call must match the code the practice’s hygiene templates use. Get this wrong and the front desk loses one click of efficiency they expect — appointments without the right template don’t auto-populate procedure codes for billing. Pull the practice’s apt-type table on day one and store the code mapping in n8n.
7

Front-Desk Slack Handoff for Complex Cases

Around 18% of replies will need a human — clinical questions, pre-op anxiety, billing disputes, “can I move it because my mom is in town.” The Slack handoff is the seam between the workflow and the office manager. Critical: the Slack message contains zero PHI in the channel itself. The patient name is replaced with a token; clicking the token opens the practice’s internal mini-app where the full thread is rendered, server-side, behind SSO.

Slack alert payload (PHI-redacted)

Slack — incoming webhook (Block Kit, no PHI)JSON
{
  "channel": "#recall-handoff",
  "blocks": [
    {
      "type": "header",
      "text": { "type": "plain_text",
                "text": ":speech_balloon: Patient question - needs reply" }
    },
    {
      "type": "section",
      "fields": [
        { "type": "mrkdwn",
          "text": "*Patient*n#PT-{{ tokenized_id }}" },
        { "type": "mrkdwn",
          "text": "*Cohort*n{{ cohort }}" },
        { "type": "mrkdwn",
          "text": "*Confidence*n{{ classifier_confidence }}" },
        { "type": "mrkdwn",
          "text": "*SLA*nReply within 1 business hour" }
      ]
    },
    {
      "type": "section",
      "text": { "type": "mrkdwn",
                "text": "Classifier intent: *question* - clinical-language detected. Workflow paused on this thread until front desk replies." }
    },
    {
      "type": "actions",
      "elements": [
        { "type": "button",
          "text": { "type": "plain_text", "text": "Open thread" },
          "style": "primary",
          "url": "https://practice-intranet.local/recall/thread/{{ tokenized_id }}" }
      ]
    }
  ]
}

Pause the workflow on the thread until human reply

When a thread escalates to handoff, the n8n workflow flips a human_in_loop = true flag on the conversation row. Subsequent inbound SMS on that thread bypass the auto-classifier and surface in the same Slack channel as a follow-up message. The human stays in the loop until they explicitly close the thread from the intranet view, at which point the workflow can resume — useful when the patient comes back two weeks later asking to reschedule.

Compliance
Slack is not in the BAA chain. Treat the channel as a notification surface only — never paste a patient’s name, DOB, phone, condition or message body into Slack messages, attachments or threads. The compliance test is simple: if the entire Slack workspace were exfiltrated tomorrow, would any individual patient be identifiable? The answer must be no.
8

Audit Log to Encrypted Store with 6-Year Retention

HIPAA’s documentation requirement under section 164.530(j) is six years from the date of creation or last effect. The audit log is not optional and not something to bolt on later — it is the practice’s single most defensible artifact in any breach investigation. Every event the workflow takes (segmentation result, prompt hash, message sent, reply received, slot booked, opt-out honored, BAA-bearer touched) writes one row to an append-only audit table.

Audit trail schema

Postgres — audit_trail (append-only, partitioned by year)SQL
CREATE TABLE audit_trail (
  audit_id        bigserial PRIMARY KEY,
  event_at        timestamptz NOT NULL DEFAULT now(),
  event_type      text NOT NULL,
  patient_id      text,
  actor           text NOT NULL,        -- 'workflow:n8n_run_' or 'human:'
  workflow_run_id text,
  prompt_hash     text,                 -- sha256 of system prompt at run time
  model_id        text,                 -- 'claude-sonnet-4-5' / 'haiku'
  vendor_call_id  text,
  payload_summary jsonb,                -- non-PHI summary only
  source_ip       inet,
  user_agent      text
) PARTITION BY RANGE (event_at);

REVOKE UPDATE, DELETE ON audit_trail FROM PUBLIC;
GRANT  INSERT, SELECT ON audit_trail TO recall_app;
GRANT  DELETE         ON audit_trail TO retention_purger;
-- retention_purger runs once per quarter; deletes only rows
-- older than 6 years and only with a corresponding deletion
-- record written to the long-term archive.

Cold archive to S3 with Object Lock

After 90 days, audit rows ship nightly to a HIPAA-eligible S3 bucket with Object Lock in compliance mode and the bucket policy denying delete by anyone, including the root account, until the 6-year retention expires. Encryption at rest with KMS, access logging on, MFA-delete on the bucket itself. The local Postgres copy stays online for the most recent 18 months for fast investigation; older queries hit the archive.

Insight
Hash the prompt at run time and store the hash, not the prompt. Prompts evolve. When a regulator asks “what did the model see in March 2025?” you want a hash that you can match to the prompt commit in your git history — that gives you a tamper-evident reconstruction without storing the prompt body in every row.

HIPAA, TCPA & Privacy by Design

Three regulatory regimes touch this workflow: HIPAA (PHI handling), TCPA (consent for autodialed messages to mobile phones), and state-level analogs like CMIA in California or SHIELD in New York. Treat compliance as a design constraint baked into every step, not a checklist at the end.

HIPAA Privacy Rule — minimum necessary in practice

The minimum necessary standard says you disclose only the PHI required for the purpose. In this workflow that means: Claude never receives full date of birth, full address, full clinical history, x-rays, or any non-essential identifier. The segmenter sees a feature row that is enough to classify, not a profile that lets it reconstruct a patient. The drafter sees first name, last hygienist first name, plain-English visit timing, lay procedure term, and slot strings — nothing more.

BAAs you must execute before any send

  • Anthropic: enterprise tier with executed BAA and zero-data-retention turned on for the API key used by the workflow.
  • SMS vendor: RingCentral healthcare BAA or TigerConnect BAA. Confirm in writing that PHI in message bodies is in scope.
  • Cloud host: AWS, GCP or Azure HIPAA-eligible account with executed BAA before any PHI lands in compute or storage.
  • Monitoring: Grafana Cloud / Datadog HIPAA tier; PHI must be filtered at the n8n boundary before logs ship.
  • Backup: S3 with BAA in place, Object Lock for retention.

TCPA — consent and opt-out

Recall SMS to a mobile phone is a covered call under TCPA. The patient must have given prior express consent — usually as a checkbox on the new-patient form, captured in the PMS as a boolean and synced to the snapshot table. Every outbound message contains “Reply STOP to opt out”. An opt-out is honored within seconds, recorded in the consent ledger, and propagated back into the PMS so the front desk doesn’t accidentally send another reminder from a different surface. Statutory damages for TCPA violations start at $500 per message; this is the part of the system you do not optimize for cost.

Encryption posture

  • In transit: TLS 1.2+ on every external API call; mTLS on the link between n8n and the PMS API where the PMS supports it.
  • At rest: AES-256 disk encryption on the VM volume + column-level encryption with pgcrypto on PHI fields (name, mobile, email).
  • Key management: AWS KMS or GCP Cloud KMS with key rotation every 365 days. The application key is never written to a developer’s laptop.
  • Backups: encrypted at rest, separate KMS key, restore-tested quarterly.

Breach response

A documented runbook is part of the deliverable. The first action on suspected breach is to revoke the n8n service account credentials and the Anthropic API key, freeze the workflow, snapshot the audit trail to read-only storage, and convene the breach assessment within 24 hours. The 60-day notification clock under HIPAA’s Breach Notification Rule starts on discovery, not on confirmation; over-prepare on day one and you’ll be fine on day twenty.

Compliance
The cheapest compliance failure is a stale opt-out — a patient who said STOP last September quietly being included in this November’s recall because the PMS sync drifted. Test the opt-out path quarterly with a synthetic patient row whose only state is opt_out_at = ‘2024-09-12’ and confirm zero outbound messages were generated.

Common Failures & Fixes

Three failure modes recur across deployments. All three are cheap to design around on day one and expensive to retrofit after a regulator has questions.

Failure 1: Stale phone numbers triggering wrong-number sends

Symptom: A patient’s number changed two years ago. The new owner of that line keeps getting reminders for someone else’s hygiene visit. They eventually forward the screenshot to their state Attorney General. Statutory exposure plus reputation damage.

Fix: Run a CNAM/HLR lookup on every mobile number in the snapshot before any first send to that number, and again every 12 months. Mark numbers that fail validation as do_not_send until the front desk verifies on the patient’s next visit. The classifier’s wrong_number intent feeds into the same disposition.

Failure 2: Drafting prompt regressions that leak PHI

Symptom: A well-meaning prompt edit (“be more specific”) causes the drafter to start including procedure descriptions or fee numbers in message bodies. By the time anyone notices, 200 messages went out.

Fix: A second-stage PHI scanner runs against every drafted message before send. Regex + a tiny Haiku check that returns phi_audit. Any flagged message is held for human review rather than sent. Drafting prompts live in git with a CI-gated test suite — 50 known-good patient profiles, 50 known-tricky ones, every PR runs the suite.

Failure 3: Quiet-hours violation from server timezone drift

Symptom: The server runs UTC but the practice is in Phoenix (no DST). A patch to the cron expression accidentally fires the send queue at 02:00 local. Twenty patients receive a recall SMS in the middle of the night before the on-call engineer notices.

Fix: Quiet-hours enforcement happens at the per-message level, not the workflow level. Each row carries the patient’s timezone and the send node consults the patient’s local clock at decision time, not the workflow’s. A separate watchdog asserts no send timestamps exist outside 09:00–21:00 local in the last 24 hours; alert on the first violation.

Measured Results — 90 Days In

Numbers from a real implementation at a 4-chair general dentistry practice (3,800 active patients, 2 hygienists, 2 GPs) after the first full quarter on the new pipeline. No change in patient base during the test period — every gain comes from prioritization, message quality and speed of follow-up.

Recall recovery
12% → 41%
overdue patients booked / month
Hygiene chair utilization
+28%
net hours filled / week
Front-desk hours saved
22 hrs/wk
redirected from call lists
ROI per dollar of SMS
$14 / $1
attributed production

The headline metric inside the practice was the front-desk experience: the team stopped dreading recall day. Outbound calls dropped from ~150 per week to fewer than 30 (the genuinely complex cases), and the calls that remained were warmer because the patient had already been touched by a friendly SMS. Production from the recall channel went from a noisy, hard-to-attribute trickle to a line item the practice could plan against.

Implementation Timeline & Cost

DIY Path
90 – 130 hours
  • n8n self-host on HIPAA-eligible cloud + queue mode: 8–12 hrs
  • Open Dental / Dentrix integration + nightly pull: 12–18 hrs
  • Patient snapshot schema + pgcrypto column encryption: 6–10 hrs
  • Claude segmentation prompt + cohort tuning: 12–18 hrs
  • Drafting prompt + PHI scanner CI suite: 14–20 hrs
  • RingCentral / TigerConnect SMS integration + BAA review: 8–12 hrs
  • Reply parser + slot revalidation + auto-book: 14–20 hrs
  • Slack handoff + intranet thread viewer: 8–12 hrs
  • Audit trail + S3 Object Lock retention: 6–10 hrs
  • Documentation + front-desk training + go-live: 4–8 hrs
With SEOKRU
5-week deployment
  • Week 1: HIPAA risk analysis + BAA execution + cohort policy
  • Week 2: PMS integration + snapshot pull + encryption posture
  • Week 3: Claude segmentation + drafting + PHI scanner
  • Week 4: Send + reply parsing + Open Dental auto-book
  • Week 5: Soft launch with one cohort, audit log review, scale-up
  • Includes: BAAs reviewed by counsel, monthly compliance report, on-call coverage for the first 90 days
Get a Custom Implementation →

FAQ

Is this HIPAA-compliant out of the box?
The architecture is HIPAA-aware, but compliance is the practice’s responsibility. Three things must be in place before any PHI flows: a documented HIPAA risk analysis covering the new workflow, executed BAAs with every vendor that touches PHI (Anthropic, the SMS vendor, the cloud host, the monitoring vendor), and the practice’s notice of privacy practices updated to reflect SMS-based recall. We deliver the technical artifacts and a checklist; your compliance officer or attorney signs off on the policy artifacts. The same pattern applies in our broader healthcare automation deployments.
Does Claude actually see PHI?
Yes — first name, plain-English visit timing, last hygienist first name, lay procedure term, slot strings. That is the minimum necessary for high-quality drafting. Claude does not see date of birth, full address, full clinical history, x-rays, fee numbers, or insurance benefit balances. We use the Anthropic enterprise tier with a signed BAA and zero-data-retention enabled, so prompts and completions are not retained beyond the live request. If your policy is stricter (some hospital systems require zero PHI to LLMs), we can swap the drafting step to a templated path with patient-specific fields injected client-side; the trade-off is a measurable drop in reply rate.
What about Dentrix Ascend or Eaglesoft instead of Open Dental?
Same architecture, different connector. Dentrix Ascend has a more complete REST API than legacy Dentrix and is the smoothest of the cloud PMSes to integrate. Eaglesoft is well-served by its read-only SQL replica plus the Patterson API key for write-back. The pieces upstream of the PMS (segmentation, drafting, sending, parsing) are PMS-agnostic. Plan for 4–8 hours of additional integration work for each non-Open-Dental system.
Will patients know they're talking to a system?
Yes, by design. The first message is signed with a real human name from the front desk, but the language is straightforward and the opt-out path is one tap. When the patient asks a non-routine question, the workflow hands off to a human within 1 business hour rather than trying to fake a longer conversation. Patient feedback in our deployments has been consistently positive — they appreciate that the practice is reaching out at all, and they appreciate the lack of phone tag. The complaint rate sits below 0.4% of all sent messages.
How do you stop the system from sending to a patient who already booked through another channel?
The nightly snapshot is the source of truth for “who is overdue.” If a patient walked in this morning and booked at the front desk, the next snapshot pull excludes them automatically. For protection inside the same business day, the send node does a final freshness check against the PMS scheduling endpoint immediately before the API call to RingCentral; any patient with a future appointment in the next 30 days is filtered. Belt and suspenders.
What happens if Claude returns malformed JSON?
Strict JSON schema validation runs in n8n on every Claude response. Malformed output triggers a single retry with a clarifying instruction; second failure routes the patient to a fallback templated message (no personalization beyond first name) and writes an alert to the audit log. We also keep schema-locked function-calling on by default; that catches roughly 90% of formatting drift before it reaches the validator.
Can the same system handle reactivation emails, not just SMS?
Yes. Email is a simpler integration on the surface (no carrier reputation, no quiet hours per timezone) but harder on engagement (open rates of 18–25% vs SMS at 95%+). The drafting prompt has a separate length and tone budget for email, and the send path routes through the practice’s HIPAA-eligible email vendor (Paubox or HIPAA-compliant SES). For most practices we run SMS as the primary channel and email as a 7-day follow-up for the cohort that didn’t reply. Multi-channel orchestration is the same n8n graph with an extra branch.
How does this compare to RevenueWell, Lighthouse 360, or Solutionreach?
Off-the-shelf dental engagement platforms are faster to start and bundle messaging, online booking, and reviews into one product. The trade-off is template-driven personalization, vendor-defined cohorts, and limited control over the messaging surface. The n8n + Claude approach gives you per-cohort prompts you can tune, a real audit trail keyed to your prompt commits, and the ability to plug specialty data sources (insurance year-end signals, perio recall, pediatric dual-parent consent) without waiting for a vendor roadmap. Single-location practices with a strong office manager often do well on a packaged product. Multi-location DSOs and practices with above-average treatment-plan complexity get more from a custom build by month three.
What's the smallest practice this makes sense for?
Around 1,000 active patients is the floor where the time savings cover the build cost in a reasonable horizon. Below that, the ROI math is still positive but the front-desk pain that drives adoption is less acute. We’ve delivered the same architecture for solo practices on Open Dental Cloud — the components don’t change, only the volumes and the cost line items shrink proportionally.
Can the workflow speak Spanish to patients who prefer it?
Yes. The drafting prompt branches on the language_pref column from the patient snapshot. Spanish drafts are reviewed at go-live by a native speaker on the front-desk team to catch register issues; once the prompt is calibrated, ongoing Spanish output meets quality at parity with English. The reply parser handles mixed Spanish/English replies natively because patients code-switch in real life.

Want this built for your practice?

SEOKRU deploys this exact system in 5 weeks. We run the HIPAA risk analysis with your compliance officer, execute every BAA, integrate Open Dental or Dentrix, build the segmentation and drafting prompts against your real recall policy, wire RingCentral or TigerConnect, train your front desk on the handoff flow, and deliver the audit trail your auditor will actually want to read. You keep ownership of every component — workflows, prompts, Postgres, the lot.

Talk to a dental automation engineer