Skip to content
Learn Agentic AI13 min read0 views

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

Share this article
C

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.