תחום מרפאות שיניים / מדריך טכני

אוטומציית recall והפעלה מחדש במרפאת שיניים עם n8n ו-Claude מודעות HIPAA, שלב אחר שלב

מדריך מלא לבניית pipeline על n8n עם Claude שמושך מטופלים לא פעילים מה-PMS שלך (Open Dental, Dentrix, Eaglesoft, Curve, Dolphin), בודק יתרת הטבות ביטוח, מנסח הודעת recall אישית, וקובע תור ב-SMS דו-כיווני. כל זה עם יומן ביקורת HIPAA מאחורי כל שלב.

קריאה של 14 דקות
בינוני – מתקדם
n8n + Claude (Bedrock)
עודכן: מאי 2026
מה תבנה

שליפה מ-PMS (Open Dental, Dentrix…)

פילוח לא פעילים (6/12/18 חודשים)

בדיקת יתרת הטבות (Vyne)

Claude מנסח הודעה מותאמת

שליחה SMS / מייל / קולית (Weave)

קביעת תור דו-כיוונית (NexHealth)

יומן ביקורת HIPAA ל-S3

1. הבעיה (למה רשימות recall מתפוררות מהר ממה שמרפאות מודות)

לכל מרפאת שיניים כללית עם יותר משני כיסאות יש את אותה בעיה. בערך 11% מה"מטופלים הפעילים" שיושבים ב-PMS לא הגיעו ל-hygiene יותר מחצי שנה, ולמזכירות פשוט אין זמן לעבוד על הרשימה הזאת באמת. גלויות מודפסות, נשלחות בדואר, מתעלמים מהן. הכיסא של ההיגייניסטית עם עמודות פנויות. ומטופלים שעוברים את ה-18 חודשים פשוט מפסיקים לחשוב עליך כעל "הרופא שלהם" (אתה סתם מישהו שיש לו את הצילומים הישנים).

מספרים אמיתיים ממרפאה כללית עם 3 רופאים (4,200 מטופלים פעילים)

מטופלים פעילים ב-PMS 4,200
לא פעילים מעל 6 חודשים (בלי hygiene) ~460 (11%)
תגובה היסטורית ל-recall (גלויה + SMS bulk אחד) 8%
מטופלים עם יתרת הטבות 2025 שתפוג ב-31/12 ~310
production ממוצע שנאבד למטופל hygiene לא פעיל בשנה $640

החשבון פשוט. 460 מטופלים לא פעילים, כפול 14 נקודות עליה בשיעור התגובה, כפול שווי לאורך חיים ממוצע של מטופל. זה מחזיר את העלות של כל ה-stack בתוך הרבעון הראשון, ועוד לפני שאתה סופר את ה-production מהמטופלים שעמדו להפוך ללא פעילים בחודש הבא ועכשיו לא. האילוץ הוא לא רצון. האילוץ הוא bandwidth. למזכירות יש 20 דברים בוערים והרשימה של ה-recall אף פעם לא הכי רועשת.

מה זה "AI recall" כאן

זה לא SMS bulk אחד שאומר "הגיע הזמן לניקוי". מתעלמים מזה אחרי החודש הראשון, וזה גם שורף את הסבלנות של מטופלים שמקבלים את אותו תבנית ארבע פעמים. החלק של ה-AI עושה שלושה דברים שטמפלייט סטטי לא יודע לעשות:

  • פילוח לפי עומק חוסר הפעילות: 6 חודשים, 12 חודשים, 18 חודשים. לכל אחד טון אחר, הצעה אחרת, ומקצב ערוצי שונה.
  • פרסונליזציה מתוך הכרטיס: תאריך ה-hygiene האחרון, הבדיקה האחרונה, פריטי תוכנית טיפול שעדיין פתוחים, ההיגייניסטית שאהבו, השעה ביום שהם מעדיפים.
  • תזמון ליתרת הטבות: מטופלת עם $890 הטבות לא מנוצלות שתוקפן יפוג ב-31/12 מקבלת הודעה אחרת באוקטובר ואחרת בפברואר.
תובנה
יעד של 22% תגובה ל-recall זה ריאלי עם פרסונליזציה ועם תזמון להטבות. מרפאות שעבדנו איתן ב-פתרונות האוטומציה למרפאות שיניים שלנו עוברות באופן עקבי מתגובה חד-ספרתית על דיוורים לעשרים-ומשהו אחוזים בתוך 60 יום של רצף מולטי-ערוצי.

2. ארכיטקטורת המערכת

שבעה רכיבים. כל אחד ניתן להחלפה. כל אחד מכוסה ב-BAA לפני שטיפת מטופל אחת נוגעת בו. שכבת ה-orchestration היא n8n self-hosted על תשתית AWS HIPAA-eligible. זה אומר EC2 ב-VPC פרטי, EBS מוצפן ב-KMS, בלי כניסה ציבורית חוץ מ-webhook לתשובות SMS (שגם הוא רץ דרך API Gateway עם WAF). PHI לא יוצא מהאזור שהמרפאה פועלת בו.

ה-stack

n8n (self-hosted, VPC HIPAA)
Orchestration. EC2 t3.large ב-subnet פרטי, queue mode, audit logging על כל node.
Claude דרך AWS Bedrock
Sonnet לטיוטות פרסונליזציה, Haiku לסיווג. BAA דרך AWS.
חיבור ל-PMS
Open Dental ישירות דרך API, Dentrix דרך DDP, Eaglesoft דרך גשר SQL, Curve / Dolphin דרך REST.
Eligibility (Vyne / DentalXChange)
יתרת הטבות בזמן אמת, סטטוס deductible, מגבלות תדירות.
Messaging (Weave / RingCentral)
SMS, מייל, voicemail drop. BAA קיים. ניתוב keywords ב-SMS דו-כיווני.
Online scheduler
NexHealth, LocalMed או Modento. המטופל בוחר slot, נכתב חזרה ל-PMS.
יומן ביקורת (S3 + Glacier)
Bucket עם Object Lock לא ניתן לשינוי, שמירת 7 שנים, KMS, CloudTrail על גישה ל-bucket.

הערכת עלות (4,200 פעילים, ~460 לא פעילים, מחזור חודשי)

Claude על Bedrock (~460 קריאות פרסונליזציה + סיווג) ~$24
בדיקות eligibility (Vyne Trellis, ~310 שאילתות) ~$95
SMS / voicemail (חבילת Weave) ~$80
תשתית AWS (EC2 + RDS + S3 Object Lock + KMS) ~$140
מנוי NexHealth או LocalMed ~$249
Monitoring (CloudWatch + Healthchecks) ~$22
סך הכל לחודש ~$610

מטופלת אחת שחזרה לעבור hygiene ועוד quadrant של SRP מחזירה עלות של חודשיים. שכבת ה-orchestration נכנסת גם ל-שירותי האוטומציה ה-AI הרחבים שאנחנו מריצים למרפאות.

1

שליפה מה-PMS ופילוח לא פעילים

ה-pipeline מתחיל בתוך ה-PMS. המקור הכי נקי ל"לא פעיל" הוא תאריך last_hygiene_visit ברשומת המטופל, joined לרשומת recall_due בתוכנית ה-recall. Open Dental חושף את שניהם דרך REST API; Dentrix צריך את ה-middleware של DDP; Eaglesoft בדרך כלל ניגשים אליו ב-SQL ישיר על המסד SQL Anywhere; Curve ו-Dolphin שניהם מספקים endpoints של REST.

שלוש רמות של חוסר פעילות

  • לא פעילים 6-12 חודשים: warm. עדיין חושבים עליך כעל הרופא שלהם. נגיעה ידידותית, לשים דגש על יתרת ההטבות ועל נוחות.
  • לא פעילים 12-18 חודשים: מתרחקים. להכיר בפער, להציע חלון ספציפי, להזכיר פריטים פתוחים בתוכנית הטיפול.
  • 18+ חודשים: בסיכון לעבור למרפאה אחרת. פנייה אישית, רצוי חתומה על שם הרופא או ההיגייניסטית שראו אצלם אחרון.

שאילתת Open Dental לקוהורט הלא פעילים

cron לילי ב-n8n רץ ב-02:00 שעון מקומי, פוגע ב-endpoints של Open Dental (/patients ו-/recalls), וכותב את הקוהורט לטבלת staging ב-Postgres בתוך אותו VPC. השאילתה למטה היא הצורה הקנונית לחיבורי PMS עם SQL ישיר (Eaglesoft, Dentrix on-prem), מותאמת לסכמה של Open Dental.

PMS — lapsed patient cohortSQL
-- Nightly pull: active patients who have lapsed hygiene
-- Tier by months since last hygiene visit
-- PatStatus = 0 means active; exclude deceased, archived, prospect

SELECT
  p.PatNum,
  p.LName, p.FName, p.Birthdate,
  p.WirelessPhone, p.Email,
  p.PreferContactMethod,        -- 0=none 2=phone 3=email 4=sms
  p.PreferRecallMethod,
  r.DateDueCalc       AS recall_due,
  r.DatePrevious      AS last_hygiene,
  EXTRACT(month FROM age(now(), r.DatePrevious)) AS months_since_hygiene,
  CASE
    WHEN r.DatePrevious < now() - interval '18 months' THEN 'cohort_18m'
    WHEN r.DatePrevious < now() - interval '12 months' THEN 'cohort_12m'
    WHEN r.DatePrevious < now() - interval '6 months'  THEN 'cohort_6m'
    ELSE 'current'
  END AS lapse_tier,
  ip.CarrierName, ip.GroupNum, ip.SubscriberID
FROM   patient p
JOIN   recall r          ON r.PatNum  = p.PatNum
                        AND r.RecallTypeNum IN (1,2)   -- prophy / perio
LEFT JOIN inssub i       ON i.Subscriber = p.PatNum AND i.DateTerm IS NULL
LEFT JOIN insplan ip     ON ip.PlanNum   = i.PlanNum
WHERE  p.PatStatus = 0
  AND  r.DateDueCalc < now()
  AND  r.DatePrevious < now() - interval '6 months'
  AND  p.DoNotContact = 0;
שים לב
לכל PMS יש דגל "אל תיצור קשר" ודגל נפרד "נפטר/ה". אף פעם לא לסמוך על אחד בלבד. ב-Open Dental: DoNotContact על המטופל בנוסף ל-PatStatus = 4 לנפטרים. ב-Dentrix דגלים נפרדים בבלוק המטופל. להוציא את שניהם זה משפט AND אחד, ומונע את סוג האסון התדמיתי שמסיים קריירות.
2

בדיקת יתרת הטבות ביטוח

הודעת recall שמזכירה סכום דולרי ספציפי של הטבות שתוקפן יפוג בסוף שנה ממירה בערך פי 2.4 מהודעה שלא מזכירה. החומר הוא eligibility בזמן אמת דרך Vyne Trellis או DentalXChange. לשניהם יש BAA, שניהם מחזירים את המקסימום השנתי שנותר, סטטוס deductible, ומגבלות תדירות על קודים נפוצים (D1110, D0150, D0274, D0210). אותו דפוס בדיקת eligibility חוזר בעבודה שלנו על אוטומציות AI למרפאות רפואיות.

מה מחזירה קריאת ה-eligibility

  1. מקסימום שנתי שנותר. השדה הכי שימושי. הוא מניע את שורת הדחיפות.
  2. סטטוס deductible. אם כבר נפרע, המטופל משלם פחות. שווה להגיד.
  3. מגבלות תדירות. D1110 בדרך כלל פעמיים בשנה. אם עברו אחד בפברואר, אולי לא מגיע להם עד אוגוסט.
  4. תאריך איפוס שנת תוכנית. רוב התוכניות מתאפסות ב-1/1, אבל חלק לא מבוטל רץ על שנת הטבות שקשורה למועד הצטרפות.
  5. כיסוי על D0274 (bitewings) ו-D0210 (FMX). מחליטים אם לצרף צילומים ל-recall.

SQL: cache לתשובות eligibility לפי carrier ותוכנית

Postgres — eligibility cache (per-patient, 30 day TTL)SQL
-- Eligibility responses are PHI; column-level KMS encryption applied
-- Cache TTL 30 days OR until plan_year_end-7 days, whichever sooner

CREATE TABLE eligibility_cache (
  patient_pms_id      text NOT NULL,
  carrier_id          text NOT NULL,
  plan_id             text NOT NULL,
  annual_max_total    numeric(8,2),
  annual_max_used     numeric(8,2),
  annual_max_remaining numeric(8,2) GENERATED ALWAYS AS
                       (annual_max_total - annual_max_used) STORED,
  deductible_total    numeric(6,2),
  deductible_met      numeric(6,2),
  plan_year_end       date,
  freq_d1110_used     int,
  freq_d1110_limit    int,
  raw_payload_kms     bytea,                 -- KMS-encrypted JSON
  fetched_at          timestamptz DEFAULT now(),
  PRIMARY KEY (patient_pms_id, plan_id)
);

CREATE INDEX idx_elig_year_end
  ON eligibility_cache (plan_year_end)
  WHERE annual_max_remaining > 200;
תובנה
לא להריץ eligibility על כל מטופל לא פעיל כל לילה. להריץ רק על הקוהורט שעומד לקבל הודעה ברצף הזה. רוב ה-clearinghouses גובים לפי בדיקה בזמן אמת. cache לפי תוכנית עם TTL אגרסיבי חותך 50% ויותר מהחשבון בלי לאבד דיוק על המטופלים שאתה באמת מתקשר אליהם.
3

שכבת פרסונליזציה (Claude)

טמפלייט סטטי מתייחס לאמא לשלושה ילדים שלא הגיעה 7 חודשים עם $620 הטבות שנותרו, בדיוק אותו דבר כמו לגמלאי שלא הגיע 18 חודשים על תוכנית אחרת. הם לא אותו מטופל ולא מגיבים לאותה הודעה. Claude לוקח את התקציר מהכרטיס בנוסף לתמונת ה-eligibility, מפעיל את כללי הטון של המרפאה, ופולט payload מובנה עם גוף ההודעה, ערוץ מועדף, וחשוב מכל, הסיבה המפורשת למה ההודעה הזאת נוסחה ככה. זה מה שהופך את הפלט ל-auditable.

5 הצירים של הפרסונליזציה

lapse_depth
רמה 6 / 12 / 18 חודשים. קובעת רגיסטר טון.
benefits_urgency
דולרים שנותרו, כפול חודשים עד איפוס שנת התוכנית.
tx_plan_pending
פריטי טיפול שלא אושרו: SRP, כתר, מגן חסימה.
preferred_provider
ההיגייניסטית האחרונה, הרופא המטפל האחרון. חתום בשמם.
channel_preference
שדה PreferContactMethod ב-PMS, plus היסטוריית engagement.

System prompt של ה-recall

ה-prompt דעתני לגבי מה לא לכלול ב-SMS. גוף הודעת SMS לעולם לא יכלול אבחנה או קוד הליך. זאת חשיפת HIPAA שאף מרפאה לא צריכה לקחת על עצמה בערוץ לא מאובטח. ה-prompt אוכף את הכלל בזמן יצירת ההודעה, ככה שהמרפאה לא צריכה לסנן בזמן השליחה.

Claude system prompt — recall personalizerTXT
You are a recall message author for a US dental practice.
The practice voice is warm, direct, never salesy. Real people
tone, not marketing copy. The patient is a real person who
has been seen before by this office.

PRACTICE CONTEXT (variables filled at runtime):
- Practice name, address, phone, after-hours line
- Doctor names + hygienist names the patient has seen
- Years patient has been with the practice

HARD RULES — NEVER VIOLATE:
1. SMS body NEVER includes a clinical diagnosis, procedure
   code, or specific treatment item. "Time for a cleaning" is
   OK. "You need SRP on UR/UL" is NOT OK.
2. Email body MAY reference treatment items in general
   language only ("the work we discussed at your last visit").
3. Voicemail script NEVER leaves a message that names the
   patient's condition. First-name only is OK.
4. NEVER promise insurance coverage outcomes. Use phrases
   like "your benefits may cover" not "your benefits will cover".
5. Always include opt-out text in SMS: "Reply STOP to opt out".
6. Always include the practice phone number for human contact.

OUTPUT — strict JSON, no prose:
{
  "channel":          "sms" | "email" | "voicemail" | "letter",
  "subject":          string|null,         // email only
  "body":             string,
  "personalization_signals": [
    "7mo_lapsed", "benefits_790_expiring_oct", "prefers_dr_kim"
  ],
  "tone":             "warm" | "personal" | "urgent",
  "expected_response_window_days": int,
  "reasoning":        "2-3 sentences, why this body for this patient"
}

If any HARD RULE would be violated, return:
{ "channel": "human_review", "reason": "..." }

קריאת n8n ל-Bedrock (Claude Sonnet)

n8n HTTP Request — Bedrock InvokeModelJSON
{
  "method": "POST",
  "url": "https://bedrock-runtime.us-east-1.amazonaws.com/model/anthropic.claude-sonnet-4-5/invoke",
  "auth": "aws-sigv4 (n8n AWS credential, IAM role bedrock:InvokeModel)",
  "headers": { "content-type": "application/json" },
  "body": {
    "anthropic_version": "bedrock-2023-05-31",
    "max_tokens": 700,
    "system": "{{ $json.recallSystemPrompt }}",
    "messages": [
      {
        "role": "user",
        "content": "Patient abstract:n{{ JSON.stringify($json.patientAbstract) }}nnEligibility:n{{ JSON.stringify($json.eligibility) }}nnPreferred channel from PMS: {{ $json.preferContactMethod }}"
      }
    ]
  }
}
תובנה
תשמור את ה-reasoning string בחזרה ל-PMS כהערה בכרטיס מצורפת למטופל. המזכירות קוראת "נשלחה SMS ב-14/10, מציינת $790 הטבות לא מנוצלות, חתומה על שם ד"ר Kim כי היא עשתה את הסתימה האחרונה" ומיד יודעת עם איזה הקשר המטופל נכנס למרפאה. האמון במערכת בנוי דרך ההערה הזאת לבד.
4

שליחה מולטי-ערוצית (SMS, מייל, קולית)

אף ערוץ אחד לא עובד על כל הקוהורט. SMS נוחת הכי מהר, אבל יש לו את הכללים הכי מחמירים על תוכן. מייל נותן מקום למכתב אמיתי, אבל מאבד 30%+ ל-spam. voicemail-drop עם הודעה מוקלטת מאיש צוות אמיתי ממיר הכי טוב על ה-tier של ה-18 חודשים, אבל גם הכי יקר לנגיעה. ה-pipeline בוחר ערוץ ראשי אחד לפי שדה ההעדפה של ה-PMS ועוד fallback אחד, עם פער של 72 שעות בין ניסיונות.

כללי ערוץ לפי קוהורט

קוהורט ראשי Fallback (72ש) מוצא אחרון (יום 14)
6 חודשים SMS (העדפת PMS) מייל אין. הקוהורט מתבגר ל-12ח
12 חודשים מייל (גוף ארוך יותר) SMS Voicemail drop בקול ההיגייניסטית
18+ חודשים Voicemail drop בקול הרופא מכתב אישי (נייר) משימת שיחה חיה למזכירות

שליחת SMS דרך Weave (BAA קיים)

Weave API — secure messages sendJSON
{
  "method": "POST",
  "url": "https://api.weavehq.com/v2/messages",
  "headers": {
    "authorization": "Bearer {{ $credentials.weave.token }}",
    "x-location-id": "{{ $env.WEAVE_LOCATION_ID }}",
    "content-type": "application/json"
  },
  "body": {
    "type": "sms",
    "to":   "{{ $json.patient.phoneE164 }}",
    "body": "{{ $json.message.body }}",
    "external_ref": "skru_recall_{{ $json.patient.pms_id }}_{{ $json.cycleId }}",
    "tags": ["recall", "{{ $json.cohort }}", "ai_drafted"],
    "send_window": { "start": "09:00", "end": "19:00", "tz": "America/Los_Angeles" }
  }
}

שעות שקטות וכבוד ל-TCPA

שני קווים אדומים. SMS לסלולרי ביתי בארה"ב נופל תחת TCPA. שעות שקטות הן 20:00 עד 8:00 בשעון מקומי של הנמען. ה-send_window ב-payload למעלה אוכף את זה ברמת vendor המסרים. הקו השני: כל תשובת STOP חייבת להיות מכובדת בתוך 24 שעות, נכתבת חזרה ל-PMS כ-DoNotContact = 1, ומופיעה ביומן הביקורת. STOPs לא רק מסוננים בשליחה הבאה. הרצף שבתור מבוטל.

אבטחה
תוכן גוף מייל הוא PHI כשהוא מתייחס לטיפול. שלח דרך ספק עם BAA (Mailgun ב-tier HIPAA, Paubox, Postmark עם תוסף BAA), אף פעם דרך SMTP transactional כללי. ה-pipeline של ה-recall לא שולח דרך מסלול SMTP גנרי.
5

קביעת תור ב-SMS דו-כיווני

העלייה הכי גדולה בהמרה באה מביטול שלב ה-call back. מטופלת שעונה "כן, יום שלישי אחר הצהריים מתאים" צריכה לנחות ישירות על slot אמיתי בלוח זמנים אמיתי ב-PMS, בלי שמישהו במרפאה יעשה בכלל משהו. n8n עושה את זה עם webhook לתשובות מ-Weave, מסווג intent קטן, וקריאת חיפוש slot של NexHealth (או LocalMed / Modento).

מסווג intent לתשובות (Claude Haiku)

תשובות של מטופלים קצרות, מבולגנות, ומלאות עמימות של שפה טבעית ("בכל זמן אחרי 3 שבוע הבא", "השבוע של ה-14", "בוקר רע לי"). Haiku מספיק זול כדי להריץ על כל הודעה נכנסת ומספיק מובנה כדי לפלוט slot-query.

Claude system prompt — reply intent classifierTXT
You classify inbound SMS replies from dental patients responding
to a recall message. Output strict JSON, no prose.

INTENT TAXONOMY:
- "wants_to_book"      patient is saying yes, give a slot
- "wants_to_book_specific" patient named a window — extract it
- "wants_to_reschedule_existing"  has appt, wants to move it
- "stop"               opt-out (also any "STOP", "REMOVE", "UNSUBSCRIBE")
- "human_handoff"      complex question, complaint, clinical concern
- "not_interested"     polite no, do not stop
- "noise"              autoreplies, bounces, gibberish

OUTPUT:
{
  "intent": "...",
  "confidence": 0.0-1.0,
  "extracted_window": {
    "earliest": "ISO datetime|null",
    "latest":   "ISO datetime|null",
    "weekdays_ok":  ["mon","tue",...],
    "time_of_day":  ["morning"|"afternoon"|"evening"]|null
  },
  "human_handoff_reason": string|null
}

Bias toward human_handoff if any of:
- Pain, swelling, bleeding, broken tooth mentioned
- Insurance dispute or billing question
- Phrasing indicates patient confusion about who is texting

חיפוש slot בקריאת NexHealth API

NexHealth — appointment slot searchJSON
{
  "method": "GET",
  "url": "https://nexhealth.info/appointment_slots",
  "headers": {
    "authorization": "Bearer {{ $credentials.nexhealth.token }}",
    "accept": "application/vnd.Nexhealth+json;version=2"
  },
  "qs": {
    "subdomain":   "{{ $env.NEX_SUBDOMAIN }}",
    "location_id": "{{ $env.NEX_LOCATION_ID }}",
    "start_date":  "{{ $json.window.earliest_date }}",
    "days":        14,
    "operatory_ids": "{{ $json.hygiene_op_ids }}",
    "appointment_type_id": "{{ $json.recall_appt_type }}"
  }
}

ה-pipeline עונה עם 3 ה-slots המועמדים הראשונים שמתאימים לחלון של המטופל. המטופל עונה "יום שלישי בשתיים", קריאת Haiku נוספת מבהירה ל-slot יחיד, וענף n8n שולח POST של NexHealth ל-/appointments שכותב את ההזמנה חזרה ל-PMS בזמן אמת. סך הזמן משליחת ה-recall ההתחלתית ועד תור מאושר ביומן, כשהכל עובד: מתחת ל-4 דקות. אותו דפוס שיחה לקביעת תור חוזר אצלנו ב-אוטומציות נדל"ן, איפה שלקבוע סיור על דירה יש פרופיל דחיפות דומה.

6

כתיבה חזרה ל-PMS ויומן ביקורת HIPAA

ה-PMS נשאר מקור האמת ללוח הזמנים, לכרטיס, ולספר התקשורת עם המטופל. כל פעולה שה-pipeline עושה (הודעה נוצרה, הודעה נשלחה, תשובה התקבלה, תור נקבע) מקבלת שתי כתיבות: אחת להערה בכרטיס ב-PMS (כך שהמזכירות רואה את העקבות בזרימה הרגילה שלה) ואחת ל-bucket לא ניתן לשינוי ב-S3 (כך שאחראי הציות יכול למשוך 7 שנות היסטוריה ביקש).

כתיבה חזרה להערת כרטיס ב-Open Dental

Open Dental — commlog.createJSON
{
  "method": "POST",
  "url": "https://api.opendental.com/api/v1/commlogs",
  "headers": {
    "authorization": "ODFHIR {{ $credentials.opendental.devKey }}/{{ $credentials.opendental.customerKey }}"
  },
  "body": {
    "PatNum":      "{{ $json.patient.PatNum }}",
    "CommType":    603,                   // text message
    "Mode_":      5,                     // SMS
    "Note":        "AI recall: {{ $json.message.tone }} tone. Signals: {{ $json.message.signals.join(', ') }}. Sent via Weave at {{ $json.sentAt }}. Audit ref: {{ $json.auditId }}.",
    "UserNum":     "{{ $env.OD_AUTOMATION_USER }}",
    "SentOrReceived": 1,
    "DateTimeEnd": "{{ $now.toISOString() }}"
  }
}

יומן ביקורת ל-S3 (Object Lock, 7 שנות שמירה)

S3 PutObject — immutable HIPAA audit recordJSON
{
  "Bucket": "skru-dental-audit-prod",
  "Key":    "audit/{{ $env.PRACTICE_ID }}/{{ $now.format('YYYY/MM/DD') }}/{{ $json.auditId }}.json",
  "Body": {
    "audit_id":           "{{ $json.auditId }}",
    "occurred_at":        "{{ $now.toISOString() }}",
    "actor":              "n8n_workflow:dental_recall@v3",
    "patient_pms_ref":    "{{ $json.patient.tokenized_id }}",
    "action":             "recall_message_sent",
    "channel":            "sms",
    "message_hash_sha256": "{{ $json.bodyHash }}",
    "prompt_version":     "recall_v3.1",
    "model":              "anthropic.claude-sonnet-4-5",
    "model_via":          "bedrock",
    "vendor_send_id":     "{{ $json.weaveExternalRef }}",
    "iam_principal":      "{{ $env.IAM_ROLE }}"
  },
  "ObjectLockMode":           "GOVERNANCE",
  "ObjectLockRetainUntilDate": "{{ $now.plus({years: 7}).toISOString() }}",
  "ServerSideEncryption":     "aws:kms",
  "SSEKMSKeyId":              "{{ $env.KMS_KEY_AUDIT }}"
}
קריטי
רשומת הביקורת שומרת hash SHA-256 של גוף ההודעה, לא את הגוף עצמו. הגוף המלא חי בהערת התקשורת ב-PMS (כבר רשומה בכרטיס תחת טיפול ה-PHI הקיים של המרפאה) וגם אצל ספק המסרים (תחת ה-BAA שלו). שמירה כפולה ב-S3 רק מכפילה את משטח החשיפה במקרה של פרצה, בלי שום תועלת לציות. Hash = הוכחת אי-שינוי. ה-PMS = מקור האמת.

כשלים נפוצים והתיקונים

שלוש תקלות חוזרות כמעט בכל פריסה דנטלית. תכננו מולן ביום הראשון. הן זולות לעצב סביבן ויקרות מאוד לשפץ אחרי הסקירה הראשונה של הציות.

תקלה 1: SMS לאדם שנפטר

תסמין: מטופל ותיק נפטר במרץ. ה-PMS עודכן ל-PatStatus = 4 על ידי איש צוות אחד אבל תוכנית ה-recall אף פעם לא הושבתה. ה-pipeline מושך אותו כ-8 חודשים לא פעיל ושולח SMS למספר של המשפחה ביום שלישי בבוקר.

תיקון: ה-SQL של הקוהורט חייב לסנן על PatStatus = 0 בנוסף ל-DoNotContact = 0 בנוסף לכך שאין commlog מסוג "deceased_notification" בשנה האחרונה. שלוש שכבות הגנה, לא אחת. זאת התקלה עם המחיר התדמיתי הכי גבוה. תתייחסו אליה כמו ל-rate-limit על מערכת תשלומים.

תקלה 2: דליפת אבחנה בגוף SMS

תסמין: טיוטה של Claude מזכירה "הטיפול שלך ב-periodontitis" בתוך SMS. SMS לא מוצפן בהובלה ברמת המוביל הסלולרי. זאת חשיפה לא מורשית של מידע קליני.

תיקון: ה-system prompt אוסר על מונחים קליניים ב-SMS בזמן יצירה. שכבה נוספת רצה על הפלט: regex / התאמת embedding מול לקסיקון של ICD-10 וקודי הליכים דנטליים, plus deny-list של תארים קליניים. כל מה שתופס באחד מהם נוחת ב-human_review במקום להישלח. שתי שכבות, אף אחת מהן לא מספיקה לבדה.

תקלה 3: גל סוף-שנה של הטבות מפיל את ספק ה-eligibility

תסמין: דחיפת "תנצל לפני שיפוג" בנובמבר-דצמבר יוצרת eligibility על כל מטופל לא פעיל בתוך יומיים. Vyne מטיל rate-limit, התור של n8n נתקע, החצי השני של הקוהורט מקבל הודעות באיחור של 5 ימים.

תיקון: פזרו את הדחיפה של סוף השנה על פני 14 יום, לא יומיים. ערכו pre-warm ל-cache של ה-eligibility בשבוע השני של אוקטובר. הריצו n8n ב-queue mode עם לפחות 3 workers. עטפו קריאות eligibility ב-retry-with-jitter. ה-pre-warm לבד חותך את העומס על ה-API בשיא של דצמבר ב-70%+.

פרטיות: HIPAA, TCPA וחוקים מדינתיים

מרפאות שיניים בארה"ב הן Covered Entity תחת HIPAA. כל מערכת חיצונית שנוגעת בנתוני מטופל היא Business Associate. ה-pipeline למעלה מערב לפחות חמישה Business Associates (AWS, Anthropic-דרך-Bedrock, ספק ה-API של ה-PMS, ה-clearinghouse של ה-eligibility, וספק המסרים), והמרפאה חייבת BAA תקף בתיק עם כל אחד לפני שטיפת PHI אחת זורמת. בלי יוצא מן הכלל. בלי "נסדר את ה-BAA אחר כך". (להקשר ישראלי, חוק הגנת הפרטיות והנחיות משרד הבריאות מחייבים אותך באופן דומה למפות BAA-equivalents מול ספקים, אבל המסגור בעמוד הזה ארה"ב כי ה-PMS-ים והספקים שמוזכרים פה כולם US.)

מה Claude רואה (ומה לא)

  • רואה: שם פרטי, ראשי תיבות שם משפחה, טווח גיל, תאריך hygiene אחרון, תאריך בדיקה אחרונה, תג קוהורט נוכחי, סיכום eligibility (יתרת מקסימום שנתי, תאריך סוף שנת תוכנית), ערוץ קשר מועדף מה-PMS, שמות הרופא וההיגייניסטית, דגל בוליאני בלבד אם יש פריט פתוח בתוכנית הטיפול.
  • לא רואה: SSN, תאריך לידה מלא, כתובת מלאה, צילומים, צילומי רנטגן, ערכי perio chart, קודי הליך ספציפיים, יתרות פיננסיות, שום הערת טקסט חופשי בכרטיס. tokenized PMS ID משמש כ-cross-reference key.

הערות יישום ספציפיות ל-HIPAA

הגישה ל-Claude היא רק דרך AWS Bedrock עם BAA קיים ברמת AWS. אף פעם דרך ה-API הציבורי של Anthropic, שאין לו כיסוי BAA. PHI לא חוצה את גבול אזור ה-AWS. ה-EC2, ה-RDS, ה-S3, וקריאות ה-Bedrock כולם חיים ב-us-east-1 (או באזור שהמרפאה פועלת בו). מינימליזציה של נתונים נאכפת בשלב בניית ה-prompt. תקציר המטופל נבנה עם whitelist של שדות, לא עם עותק של הרשומה המלאה. שמירת הביקורת היא 7 שנים לפי דרישות תיעוד HIPAA. Object Lock במצב GOVERNANCE מונע מחיקה או שינוי בתוך החלון הזה.

TCPA וחוקים מדינתיים

  • TCPA: המטופל נתן הסכמה מראש בקבלה כששם תיוג על opt-in ל-SMS. ה-pipeline חייב לשמור את רשומת ההסכמה ולשלוח רק למספרים עם opt-in פעיל. שעות שקטות 20:00 עד 8:00 בשעון מקומי.
  • CCPA / CPRA (קליפורניה): מסלול מתועד למחיקת נתונים שמטהר את המטופל מ-Postgres ה-staging ומ-cache ה-eligibility בעסקה אחת.
  • FTPA פלורידה, MyHealth MyData וושינגטון: סטנדרטי הסכמה מחמירים יותר מ-HIPAA על תקשורת בריאותית. אם המרפאה פועלת באחת מהמדינות האלה, תתייחס לכל הפניה קלינית בהודעה יוצאת כ-opt-in-only במינימום.
  • יומן ביקורת: כל hash של prompt, גרסת מודל, וגוף הודעה נרשמים עם חותמת זמן ו-IAM principal. תאמין שתפיק את זה בכל ביקורת.
אבטחה
השתמשו ב-AWS Bedrock עם BAA קיים. ה-API הציבורי של Anthropic לא מכוסה ב-BAA נכון לכתיבת המדריך. שליחת PHI אליו זאת חשיפה לא מורשית. אם ספק טוען "אנחנו משתמשים ב-Claude" בלי לפרט את שרשרת ה-BAA, תבקשו בכתב אם כל ה-PHI עובר דרך Bedrock עם AWS כ-Business Associate. התשובה חשובה.

תוצאות נמדדות (90 יום אחרי)

מספרים מיישום אמיתי במרפאה כללית עם 3 רופאים (4,200 מטופלים פעילים, ~460 לא פעילים בהתחלה, 6 כיסאות, 4 היגייניסטיות) אחרי הרבעון הראשון המלא. בתקופת המבחן לא רצו קמפיינים חדשים לרכישת מטופלים. כל ההרמה היא מעבודה טובה יותר על הספר הקיים.

תגובה ל-recall
8% → 23%
על קוהורט הלא פעילים
מטופלים שחזרו
112
90 יום ראשונים
production שהוחזר
$84K
פיילוט 90 יום, hygiene + tx
שעות מזכירות שנחסכו
~28 ש'/חודש
בלי דיוורי recall, בלי שיחות

המדד הראשי בתוך המרפאה הוא לא הסכום הדולרי. זה ניצול הכיסא של ה-hygiene. עמודות פנויות ירדו מממוצע של 11% ריקות ל-4% ריקות תוך הרבעון. היגייניסטית אחת שמריצה עמודה מלאה יותר מייצרת עוד ~$2,200 לחודש ב-production לפי טבלת המחירים הרגילה של המרפאה, בלי לעבוד יותר קשה. ההצטברות מתבטאת הכי ברור בחודשים 4 ו-5.

לוח זמנים ועלות יישום

מסלול DIY
110 – 160 שעות
  • שרשרת BAA וסקירת סיכון HIPAA: 14-20 שעות
  • תשתית AWS HIPAA-eligible, KMS, VPC, S3 Object Lock: 14-20 שעות
  • חיבור PMS (Open Dental / Dentrix / Eaglesoft): 16-24 שעות
  • אינטגרציית eligibility (Vyne / DentalXChange) + cache: 12-18 שעות
  • Prompt של Claude + guardrails של deny-list + ולידציה: 18-26 שעות
  • שליחה מולטי-ערוצית + שעות שקטות TCPA + טיפול ב-STOP: 14-20 שעות
  • קביעת תור ב-SMS דו-כיווני + אינטגרציית NexHealth / LocalMed: 14-22 שעות
  • יומן ביקורת ל-S3 + דוח ציות חודשי: 8-10 שעות
עם SEOKRU
פריסה ב-4 שבועות
  • שבוע 1: שרשרת BAA, סקירת סיכון HIPAA, חיבור PMS
  • שבוע 2: אינטגרציית eligibility, הקמת Bedrock, ניסוח prompt
  • שבוע 3: שליחה מולטי-ערוצית, SMS דו-כיווני, חיבור NexHealth
  • שבוע 4: השקה רכה על קוהורט של 50 מטופלים, ביקורת, כיוון
  • כולל: דוח ציות חודשי, כיוון prompt, סקירת KPI של ה-hygiene
קבל יישום מותאם ←

שאלות נפוצות

הארכיטקטורה היא HIPAA-aware בעיצוב, אבל אף תוכנה לא "תואמת HIPAA" לבדה. תאימות היא תכונה של הפריסה plus ה-BAAs plus הנהלים התפעוליים. ה-pipeline כפי שמתואר משתמש בספקים עם BAA בכל שלב (AWS לחישוב ול-Bedrock, ספק ה-API של ה-PMS, Vyne או DentalXChange ל-eligibility, Weave או RingCentral למסרים). המרפאה שלך עדיין צריכה לחתום על כל BAA, להשלים סקירת סיכון, ולתעד את ה-workflow במדיניות וההליכים של ה-HIPAA. אנחנו מספקים את החלקים הטכניים ואת תבנית התיעוד; ה-HIPAA officer של המרפאה חותם.
לא ל-PHI. ל-Anthropic נכון להיום אין הסכם BAA על ה-API הציבורי, מה שאומר ששליחת נתוני מטופל ל-api.anthropic.com היא חשיפה לא מורשית תחת HIPAA. AWS Bedrock הוא המסלול עם BAA. AWS היא ה-Business Associate, AWS מארחת את מודלי Claude בתוך תשתית HIPAA-eligible, ו-AWS מתחייבת חוזית לטפל ב-PHI לפי חוקי ה-Privacy וה-Security. שימושים בלי PHI (FAQ ציבורי באתר שלא רואה נתוני מטופל) יכולים להשתמש ב-API של Anthropic בשקט, אבל ה-pipeline של ה-recall חייב לרוץ דרך Bedrock.
כן. הארכיטקטורה אגנוסטית ל-PMS. רק שכבת החיבור משתנה. ל-Open Dental יש את ה-REST API הכי נקי first-party. Dentrix רץ דרך middleware ה-DDP (Henry Schein's developer program) או דרך SQL ישיר על ה-Dentrix Microsoft SQL Server. Eaglesoft בדרך כלל ניגשים אליו ב-SQL ישיר על ה-SQL Anywhere DB עם service account לקריאה בלבד. Curve Dental ו-Dolphin שניהם מספקים endpoints של REST. ה-SQL של הקוהורט מסתגל בכמה שעות. כל שאר ה-pipeline אחרי טבלת הקוהורט זהה.
מסווג ה-intent בשלב 5 מוטה ל-human_handoff על כל תשובה שמזכירה כאב, נפיחות, דימום, שן שבורה, או כל דבר עם ניחוח דחיפות. תשובות כאלה לעולם לא עוברות בקביעת תור אוטומטית. הן נוחתות בערוץ סלאק (או מה שהמרפאה משתמשת בו פנימית) למזכירות לטריאז' בזמן אמת, כשהמטופל מקבל אישור SMS מהיר שאומר "קיבלנו את ההודעה שלך, מישהו מהמרפאה יחזור אליך תיכף". המערכת היא ל-recall שגרתי, לא לטריאז' קליני.
שלושה דברים. ראשית, ה-system prompt קובע רגיסטר טון ספציפי ("אנשים אמיתיים, לא marketing copy") וכולל הערות קול ספציפיות למרפאה (חתימה של הרופא וההיגייניסטית שראו אותם בפועל). שנית, אורך ההודעה תחום: SMS ב-1-2 משפטים plus opt-out, מייל עד 5-6 משפטים מקסימום. טקסט אוטומטי ארוך נקרא כאוטומטי; טקסט תמציתי בטון אנושי נקרא כאנושי. שלישית, אותות הפרסונליזציה אמיתיים: הודעת recall ל-12 חודשים שאומרת "שמנו לב שד"ר Kim לא ראתה אותך מאז הסתימה בשן 19" מנצחת כל דבר גנרי. מטופלים מגיבים לפרטים שהם מזהים.
ה-pipeline מטפל ביחסי משפחה דרך שאילתה על רשומת ה-responsible-party (guarantor) וגלגול קטינים תחתיה. משק בית עם אמא + אבא + שני ילדים מקבל הודעה מאוחדת אחת ל-responsible party, מתוזמנת סביב התלוי שהכי באיחור, עם הערה שגם האחרים בתור אם זה מתאים. ה-PMS כבר ממדל את היחסים המשפחתיים (שדה Guarantor ב-Open Dental, מקבילים באחרים), וה-pipeline מכבד אותו. ילדים בוגרים עם מנוי משלהם מקבלים הודעה משלהם, ממוענת אליהם.
לא. זה מחליף את שכבת הגלויות וה-SMS bulk, שרוב המרפאות מריצות בלית ברירה כי למזכירות אין זמן לשיחות recall פרטניות. ה-tier של 18 חודשים עדיין מנותב למשימה אנושית בתיבה של המזכירות. המטופלים האלה מגיעים לשיחה אמיתית ממישהו שהם מכירים. מה שהמערכת עושה זה לשחרר את המזכירות מעבודה ידנית על ה-tiers של 6 ו-12 חודשים, איפה ש-80% מהנפח חי, כדי שלמזכירות יהיה זמן לעשות את השיחות בעלות הערך הגבוה על ה-tier של 18 החודשים.
כלים מהמדף מהירים יותר להתחיל ונועלים אותך לתבניות של ספק אחד ולתמהיל ערוצים אחד. Weave ו-Modento מצוינים בשכבת המסרים. ל-RevenueWell ספריית תבניות חזקה out-of-the-box. גישת n8n + Claude נותנת פרסונליזציה ברמת מטופל שהכלים המוכנים פשוט לא יודעים לעשות (ה"פרסונליזציה" שלהם זה mail-merge של שם פרטי, לא reasoning שמודע לכרטיס), נימוק auditable לכל הודעה, והיכולת לחבר את ה-PMS הספציפי שלך, ספק ה-eligibility, וה-scheduler. למרפאה קטנה שרוצה משהו turnkey, המוצר מהמדף בסדר. ל-DSO מרובת מיקומים או למרפאה high-end שמתחרה על חוויית המטופל, פער הפרסונליזציה משנה כבר מחודש שני.

רוצה שזה ייבנה למרפאה שלך?

SEOKRU פורסת בדיוק את מערכת ה-recall וההפעלה מחדש הזאת ב-4 שבועות, על תשתית AWS HIPAA-eligible עם שרשרת BAA חתומה. אנחנו מחברים את ה-PMS שלך, מחווטים את ה-eligibility ואת המסרים, מאמתים את ה-prompt מול דגימה אמיתית של הקוהורטים שלך, ומריצים את דוח הציות החודשי. אתם שומרים על הבעלות על כל רכיב (workflows, prompts, יומן ביקורת, הכל).

דבר עם מהנדס אוטומציה למרפאות שיניים