AI Agent for Prescription Refill Management: Automated Refill Requests and Pharmacy Coordination
Build an AI agent that detects when patients need medication refills, routes approval requests to providers, coordinates with pharmacies via NCPDP SCRIPT, and tracks prescription fulfillment end to end.
Why Prescription Refills Need Automation
Prescription refill requests account for a significant portion of inbound calls to medical practices. Each request triggers a multi-step workflow: verify the prescription, check remaining refills, get provider approval, and notify the pharmacy. When done manually, refill requests take 5 to 10 minutes each and are prone to communication delays.
An AI refill management agent handles this entire chain — from detecting that a patient needs a refill to confirming that the pharmacy has processed it.
Prescription Data Model
Start by modeling the prescription lifecycle, including refill counts, authorization status, and pharmacy details.
from dataclasses import dataclass, field
from datetime import date, datetime, timedelta
from typing import Optional
from enum import Enum
import uuid
class PrescriptionStatus(Enum):
ACTIVE = "active"
EXPIRED = "expired"
DISCONTINUED = "discontinued"
ON_HOLD = "on_hold"
class RefillRequestStatus(Enum):
PENDING = "pending"
APPROVED = "approved"
DENIED = "denied"
SENT_TO_PHARMACY = "sent_to_pharmacy"
FILLED = "filled"
PICKED_UP = "picked_up"
@dataclass
class Prescription:
id: str
patient_id: str
provider_id: str
medication_name: str
dosage: str
frequency: str
quantity: int
refills_authorized: int
refills_remaining: int
prescribed_date: date
expiration_date: date
pharmacy_id: str
status: PrescriptionStatus = PrescriptionStatus.ACTIVE
last_filled: Optional[date] = None
days_supply: int = 30
@dataclass
class RefillRequest:
id: str = field(
default_factory=lambda: str(uuid.uuid4())
)
prescription_id: str = ""
patient_id: str = ""
requested_at: datetime = field(
default_factory=datetime.utcnow
)
status: RefillRequestStatus = RefillRequestStatus.PENDING
provider_approved: bool = False
approved_by: Optional[str] = None
approved_at: Optional[datetime] = None
pharmacy_confirmation: Optional[str] = None
notes: str = ""
Refill Detection Engine
The agent proactively identifies patients who are running low on medication based on their fill history and days supply. This enables outreach before the patient runs out.
class RefillDetector:
def __init__(self, db):
self.db = db
async def find_patients_needing_refills(
self, lookahead_days: int = 7,
) -> list[dict]:
cutoff = date.today() + timedelta(days=lookahead_days)
rows = await self.db.fetch("""
SELECT
rx.id AS prescription_id,
rx.patient_id,
p.first_name || ' ' || p.last_name AS name,
p.phone, p.email,
rx.medication_name, rx.dosage,
rx.refills_remaining,
rx.last_filled,
rx.days_supply,
(rx.last_filled + rx.days_supply) AS runs_out,
rx.pharmacy_id
FROM prescriptions rx
JOIN patients p ON p.id = rx.patient_id
WHERE rx.status = 'active'
AND rx.refills_remaining > 0
AND (rx.last_filled + rx.days_supply) <= $1
AND NOT EXISTS (
SELECT 1 FROM refill_requests rr
WHERE rr.prescription_id = rx.id
AND rr.status IN (
'pending', 'approved',
'sent_to_pharmacy'
)
)
ORDER BY runs_out ASC
""", cutoff)
return [dict(r) for r in rows]
async def validate_refill_eligibility(
self, prescription_id: str,
) -> dict:
rx = await self.db.fetchrow("""
SELECT * FROM prescriptions
WHERE id = $1
""", prescription_id)
if not rx:
return {"eligible": False, "reason": "not_found"}
if rx["status"] != "active":
return {
"eligible": False,
"reason": f"prescription_{rx['status']}",
}
if rx["refills_remaining"] <= 0:
return {"eligible": False, "reason": "no_refills"}
if rx["expiration_date"] < date.today():
return {"eligible": False, "reason": "expired"}
return {
"eligible": True,
"refills_remaining": rx["refills_remaining"],
"medication": rx["medication_name"],
"dosage": rx["dosage"],
}
Provider Approval Workflow
Certain refills require explicit provider approval — especially controlled substances or when the prescription needs modification. The agent routes these requests through a structured approval queue.
See AI Voice Agents Handle Real Calls
Book a free demo or calculate how much you can save with AI voice automation.
class ProviderApprovalQueue:
def __init__(self, db, notification_service):
self.db = db
self.notify = notification_service
async def submit_for_approval(
self, refill_request: RefillRequest,
prescription: Prescription,
requires_review: bool = False,
) -> RefillRequest:
auto_approve = (
not requires_review
and prescription.refills_remaining > 0
and prescription.expiration_date > date.today()
)
if auto_approve:
refill_request.status = RefillRequestStatus.APPROVED
refill_request.provider_approved = True
refill_request.approved_by = "auto"
refill_request.approved_at = datetime.utcnow()
else:
refill_request.status = RefillRequestStatus.PENDING
await self.notify.send_to_provider(
provider_id=prescription.provider_id,
message=(
f"Refill request for "
f"{prescription.medication_name} "
f"{prescription.dosage} - "
f"Patient {refill_request.patient_id}. "
f"Refills remaining: "
f"{prescription.refills_remaining}"
),
action_url=(
f"/refills/{refill_request.id}/review"
),
)
await self.db.execute("""
INSERT INTO refill_requests
(id, prescription_id, patient_id, status,
provider_approved, approved_by, approved_at)
VALUES ($1, $2, $3, $4, $5, $6, $7)
""", refill_request.id, refill_request.prescription_id,
refill_request.patient_id,
refill_request.status.value,
refill_request.provider_approved,
refill_request.approved_by,
refill_request.approved_at)
return refill_request
async def process_provider_decision(
self, request_id: str, approved: bool,
provider_id: str, notes: str = "",
):
status = (
RefillRequestStatus.APPROVED if approved
else RefillRequestStatus.DENIED
)
await self.db.execute("""
UPDATE refill_requests
SET status = $2, provider_approved = $3,
approved_by = $4, approved_at = $5, notes = $6
WHERE id = $1
""", request_id, status.value, approved,
provider_id, datetime.utcnow(), notes)
Pharmacy Notification via NCPDP SCRIPT
Once approved, the agent sends the refill authorization to the pharmacy using the NCPDP SCRIPT standard, the electronic prescribing protocol used across US pharmacies.
class PharmacyNotifier:
def __init__(self, escript_client):
self.client = escript_client
async def send_refill_authorization(
self, prescription: Prescription,
refill_request: RefillRequest,
db,
) -> str:
message = {
"message_type": "REFRES", # refill response
"pharmacy_ncpdp": prescription.pharmacy_id,
"prescriber_npi": await self._get_npi(
prescription.provider_id, db
),
"medication": {
"drug_description": (
prescription.medication_name
),
"strength": prescription.dosage,
"quantity": prescription.quantity,
"days_supply": prescription.days_supply,
"refills_authorized": 1,
},
"patient_id": prescription.patient_id,
}
confirmation = await self.client.send(message)
await db.execute("""
UPDATE refill_requests
SET status = 'sent_to_pharmacy',
pharmacy_confirmation = $2
WHERE id = $1
""", refill_request.id, confirmation["tracking_id"])
await db.execute("""
UPDATE prescriptions
SET refills_remaining = refills_remaining - 1,
last_filled = CURRENT_DATE
WHERE id = $1
""", prescription.id)
return confirmation["tracking_id"]
async def _get_npi(self, provider_id, db):
row = await db.fetchrow(
"SELECT npi FROM providers WHERE id = $1",
provider_id,
)
return row["npi"]
End-to-End Refill Tracking
The agent monitors the complete refill lifecycle and notifies the patient at each stage.
class RefillTracker:
def __init__(self, db, sms_client):
self.db = db
self.sms = sms_client
async def check_and_notify(self, request_id: str):
req = await self.db.fetchrow("""
SELECT rr.*, p.phone, p.first_name,
rx.medication_name
FROM refill_requests rr
JOIN patients p ON p.id = rr.patient_id
JOIN prescriptions rx
ON rx.id = rr.prescription_id
WHERE rr.id = $1
""", request_id)
status = req["status"]
messages = {
"approved": (
f"Hi {req['first_name']}, your refill for "
f"{req['medication_name']} has been approved "
f"and sent to your pharmacy."
),
"denied": (
f"Hi {req['first_name']}, your provider "
f"needs to discuss your "
f"{req['medication_name']} refill with you. "
f"Please call the office."
),
"filled": (
f"Hi {req['first_name']}, your "
f"{req['medication_name']} is ready for "
f"pickup at your pharmacy."
),
}
if status in messages:
await self.sms.send(req["phone"], messages[status])
FAQ
How does the agent handle controlled substance prescriptions differently?
Controlled substances (Schedule II-V) always require explicit provider review — the auto-approval path is disabled. The agent flags these requests with the DEA schedule classification and presents additional verification fields to the provider, including the patient's prescription drug monitoring program (PDMP) report. Schedule II medications cannot be refilled at all and require a new prescription.
What happens when a patient requests a refill but has zero refills remaining?
The agent informs the patient that no refills are available and offers to send a new prescription request to their provider. It creates a "renewal request" instead of a refill request, which goes through the full provider review workflow. The provider can then issue a new prescription with a fresh refill count if clinically appropriate.
How does the agent coordinate with multiple pharmacies if a patient switches?
The agent maintains the patient's current preferred pharmacy and allows pharmacy changes at refill time. When a pharmacy change is detected, the agent sends a cancellation to the old pharmacy and routes the new fill to the updated pharmacy, ensuring no duplicate dispensing occurs.
#PrescriptionRefill #PharmacyIntegration #HealthcareAI #NCPDPSCRIPT #Python #AgenticAI #LearnAI #AIEngineering
CallSphere Team
Expert insights on AI voice agents and customer communication automation.
Try CallSphere AI Voice Agents
See how AI voice agents work for your industry. Live demo available -- no signup required.