- AI Productivity Insights
- Pages
- Design and Implement the n8n Workflow for Medication Refill Processing with GPT-5 Classification
Step-By-Step Implementation Walk Through:
Step 4: Design and Implement the n8n Workflow for Medication Refill Processing with GPT-5 Classification
Before you begin, ensure you have the following prerequisites and technical requirements in place to successfully implement and work with n8n:
npm is a quick way to get started with n8n on your local machine. You must have Node.js installed. n8n requires a Node.js version between 20.19 and 24.x, inclusive.
Prerequisites
Install Node.js: https://nodejs.org/en/download
After installing Node.js, try n8n without installing it using npx. Open Command Prompt and type:
npx n8n
This command downloads everything needed to start n8n. You can then access n8n and begin building workflows by opening http://localhost:5678 in your browser or by pressing "o" when prompted.

Architecture Overview
This workflow implements a robust medication refill processing system leveraging a microservices architecture. The process flow begins with an HTTP request ingestion via a Webhook endpoint. Subsequently, the system executes parallel operations: patient authentication through Microsoft Graph API integration and pharmaceutical classification via the GPT-5 natural language processing service. The dual data streams converge through a merge operation, where the system normalizes and validates the combined dataset before persisting it to Google Sheets via API integration. The system exposes its entry point through a RESTful interface at POST /medication-refill
.

Architecture Diagram

Automated backend workflow in n8n
Credentials
The essential credentials are needed for the n8n workflow to properly connect with the various external services used in the medication refill processing system. Specifically:
The Microsoft Graph OAuth2 credential named "Entra credential" enables authentication to verify user identity and retrieve patient demographic information.
The OpenAI credential named "OpenAI account" allows the workflow to connect to GPT-5 for medication classification and standardization.
The Google Sheets OAuth2 credential named "Google Sheets account" provides access to store the processed medication refill data in Google Sheets.
Without these credentials properly configured, the workflow wouldn't be able to authenticate with these external services, making it impossible to verify patients, classify medications, or store the final data.

1. Microsoft Graph OAuth2 — Entra credential


This is used in the Verify User node to call Microsoft Graph API.
Parameters to configure in n8n OAuth2:
Client ID →
CLIENT_ID
from your.env
(seeapp.py
)Client Secret →
CLIENT_SECRET
from.env
Authorization URL →
<https://login.microsoftonline.com/><TENANT_NAME>/oauth2/v2.0/authorize
(matches how
/login
buildsauthorize_url
in your Flask app)Access Token URL →
<https://login.microsoftonline.com/><TENANT_ID>/oauth2/v2.0/token
(matches how
/auth/callback
posts for tokens)Scope →
openid offline_access https://graph.microsoft.com/User.ReadWrite
Redirect URI → The n8n callback URL (displayed when creating the credential). Must also match your Entra External ID app config. → http://localhost:5678/rest/oauth2-credential/callback
2. OpenAI — OpenAI account

This is used in the Classify Medication (GPT-5) node.
Parameters to configure in n8n OpenAI credential:
API Key → Your OpenAI API key.
Base URL (optional) → Default is
https://api.openai.com/v1
. (You’re calling/v1/responses
, so default works.)Organization ID (optional) → Only if your account requires it.
3. Google Sheets OAuth2 — Google Sheets account

This is used in the Add to Google Sheets node.
Parameters to configure in n8n Google OAuth2:
Client ID → From your Google Cloud Console OAuth2 credentials.
Client Secret → From the same OAuth2 credentials.
OAuth Redirect URL → The n8n callback URL shown when creating the credential. Must also be added to your Google Cloud Console OAuth consent screen. → http://local:5678/rest/oauth2-credential/callback
Step-by-Step Node Implementation

1) Webhook (trigger)
Purpose: Entry point for the workflow.
This node listens on
POST /medication-refill
.When your Flask app (
app.py
) submits a refill request, the payload (DOB, HN, medication, user object) is sent here.That JSON is then available for downstream nodes.
Why needed: It’s how n8n “catches” requests from your external app. Without it, no workflow would start.

Parameters → Webhook URLs (Test)
You’ll see a Test URL like:
http://localhost:5678/webhook-test/medication-refill
(auto‑generated by n8n when you click “Listen for test event”).The path part must be
medication-refill
(we’ll set this below).
Parameters
HTTP Method:
POST
Path:
medication-refill
Authentication:
None
(leave as default; JSON has no auth config)Respond:
Immediately
(default)Response Code:
200
(default)Options: leave empty
{}
(matches JSON)
This node fans out to both branches in the diagram (Verify User + Classify Medication).
2) Verify User (HTTP Request → Microsoft Graph)
Purpose: Validates patient identity and retrieves official demographic info.
Calls Microsoft Graph:
<https://graph.microsoft.com/v1.0/users/>{{ $json["body"]["user"]["oid"] }} ?$select=displayName,mail,extension_DateOfBirth,extension_HospitalNumber,extension_Gender
Uses the Entra credential (OAuth2) to authenticate.
Pulls: display name, email, DOB, HN, gender.
Why needed: Ensures the refill request is tied to an authenticated patient identity in Entra. It prevents spoofing or wrong user data from the form.

Parameters
Authentication:
OAuth2
(select your Microsoft Graph OAuth2 credential)URL:
https://graph.microsoft.com/v1.0/users/{{ $json["body"]["user"]["oid"] }}?$select=displayName,mail,extension_3888d42ccd5f4582afa2f9b641643fac_DateofBirth,extension_3888d42ccd5f4582afa2f9b641643fac_HospitalNumber,extension_3888d42ccd5f4582afa2f9b641643fac_Gender
(This pulls Display Name, Mail, and the three custom attributes.)
Options: leave default
{}
Credentials (right side of node)
oAuth2Api → Name:
Entra credential
(must match exactly)
Connection
Output goes to Edit Fields.
3) Edit Fields (Set)
Purpose: Normalize and clean the patient data.
Builds a new object with:
SubmittedAt → timestamp in Bangkok timezone.
DisplayName / Email → from Graph, fallback
n/a
.DateOfBirth / HospitalNumber / VerifiedGender → from Graph custom extensions.
Medication → taken directly from webhook body.
Keeps other fields for reference.
Why needed: Provides a consistent, well-structured “patient + request” record before merging with AI classification.

Parameters
Mode:
raw
JSON Output: paste exactly:
{ "SubmittedAt": "{{ $now.setZone('Asia/Bangkok').toFormat('yyyy-MM-LL-MMM dd, yyyy HH:mm:ss') }}", "DisplayName": "{{ $json.displayName || $json.value?.[0]?.displayName || 'n/a' }}", "Email": "{{ $json.mail }}", "DateOfBirth": "{{ $json['extension_3888d42ccd5f4582afa2f9b641643fac_DateofBirth'] || $json.value?.[0]?.['extension_3888d42ccd5f4582afa2f9b641643fac_DateofBirth'] || 'n/a' }}", "HospitalNumber": "{{ $json['extension_3888d42ccd5f4582afa2f9b641643fac_HospitalNumber'] || $json.value?.[0]?.['extension_3888d42ccd5f4582afa2f9b641643fac_HospitalNumber'] || 'n/a' }}", "Medication": "{{ $node['Webhook'].json.body.medication || 'n/a' }}", "VerifiedGender": "{{ $json['extension_3888d42ccd5f4582afa2f9b641643fac_Gender'] || $json.value?.[0]?.['extension_3888d42ccd5f4582afa2f9b641643fac_Gender'] || 'n/a' }}" }
(Timezone = Asia/Bangkok; pulls medication from the Webhook payload.)
Advanced
Include Other Fields:
true
(as in JSON)
Connection
Output goes to Merge Classification (Input 1).
4) Classify Medication (GPT‑5) — HTTP Request → OpenAI Responses API
Purpose: Uses GPT-5 to interpret free-text medication input.
Calls
POST <https://api.openai.com/v1/responses
>.Input is the medication string from webhook.
Prompt forces GPT-5 to return only JSON with:
generic_name, strength, form, use_type, therapeutic_class, is_controlled_substance
.Restricts
use_type
to valid administration routes (oral, topical, injection, etc.).
Why needed: Patients type meds inconsistently (“Paracetamol 500mg tab” vs “Tylenol”). GPT-5 standardizes this into clean structured fields.

Parameters
Method:
POST
URL:
https://api.openai.com/v1/responses
Authentication:
Predefined credential type
Credential Type:
openAiApi
(select your OpenAI key)Send Body:
true
Body Content Type:
JSON
JSON Body: paste exactly:
{ "model": "gpt-5", "reasoning": { "effort": "minimal" }, "input": "You are a medical assistant. Classify the following medication string and return ONLY a valid JSON object with keys: generic_name, strength, form, use_type, therapeutic_class, is_controlled_substance. Rules: 'use_type' must indicate ONLY the route of administration ('oral','topical','injection','inhalation','sublingual','rectal','transdermal','unknown'). Do not use 'human' or 'veterinary'. If unclear, set 'use_type' to 'unknown'. If any other field is unknown, use 'unknown'. Input: '{{ $json.body.medication }}'" }
(This enforces a strict JSON schema and allowed values for
use_type
.)
Credentials (right side of node)
openAiApi → Name:
OpenAi account
Connection
Output goes to Extract GPT JSON String.
5) Extract GPT JSON String (Set)
Purpose: Parse the OpenAI response format.
OpenAI’s Responses API returns a nested structure (
output → message → output_text
).This node extracts just the JSON text returned by GPT-5.
If extraction fails, it outputs an empty string.
Why needed: Downstream nodes can’t work with OpenAI’s raw object; they need the pure JSON classification.

Parameters
Mode:
JSON
JSON Output: paste exactly:
={{ (function () { try { const msg = ($json.output || []).find(o => o.type === 'message') || {}; const block = (msg.content || []).find(c => c.type === 'output_text') || {}; return block.text || ''; } catch (e) { return ''; } })() }}
(This pulls the model’s plain JSON string out of the Responses API structure.)
Connection
Output goes to Merge Classification (Input 2).
6) Merge Classification (Merge)
Purpose: Combine patient data and medication classification.
Input 1 = From Edit Fields (patient info).
Input 2 = From Extract GPT JSON String (drug classification JSON).
Mode = Combine → Multiplex → produces a single object merging both.
Why needed: Now you have one enriched record: verified patient details + normalized medication info.

Parameters
Mode:
combine
Combination Mode:
multiplex
(1 item from each branch → 1 combined item)
Connections
Input 1: from Edit Fields
Input 2: from Extract GPT JSON String
Output goes to Build Final Row.
7) Build Final Row (Set)
Purpose: Format the final row for Google Sheets.
Maps all fields into fixed column names:
SubmittedAt, DisplayName, Email, DateOfBirth, HospitalNumber, Medication, VerifiedGender, GenericName, Strength, Form, UseType, TherapeuticClass, IsControlledSubstance
.
Why needed: Guarantees consistent schema for every row added to the spreadsheet.

Parameters
Mode:
JSON
JSON Output: paste exactly:
{ "SubmittedAt": "{{ $json.SubmittedAt }}", "DisplayName": "{{ $json.DisplayName }}", "Email": "{{ $json.Email }}", "DateOfBirth": "{{ $json.DateOfBirth }}", "HospitalNumber": "{{ $json.HospitalNumber }}", "Medication": "{{ $json.Medication }}", "VerifiedGender": "{{ $json.VerifiedGender }}", "GenericName": "{{ $json.generic_name }}", "Strength": "{{ $json.strength }}", "Form": "{{ $json.form }}", "UseType": "{{ $json.use_type }}", "TherapeuticClass": "{{ $json.therapeutic_class }}", "IsControlledSubstance": "{{ $json.is_controlled_substance }}" }
(Maps patient + GPT fields into final sheet columns.)
Connection
Output goes to Add to Google Sheets.
8) Add to Google Sheets
Purpose: Store the structured record.
Appends each completed refill request into
Data!A:M
of the linked Google Sheet.Uses the Google Sheets credential for OAuth2.
Options:
usePathForKeyRow=true
,valueInputMode=RAW
(no auto-formatting).
Why needed: Provides a persistent, queryable log of all refill requests for pharmacy or analytics.

Parameters
Authentication:
OAuth2
(select your Google Sheets OAuth2 cred)Operation:
Append
Your Spreadsheet ID:
1o1eH99Agsz4xyobax1rmQ5rsFjOPUnQYXVthsazHW54
Range:
Data!A:M
Options → Use Path for Key Row:
true
Options → Value Input Mode:
RAW
Credentials (right side of node)
googleSheetsOAuth2Api → Name:
Google Sheets account
Summary: Streamlining Medication Refill Workflows

Automated backend workflow in n8n
This n8n workflow creates an end-to-end automation system that processes patient medication refill requests through a secure pipeline. By integrating Microsoft Entra External ID for identity verification, n8n for workflow orchestration, GPT-5 for intelligent medication classification, and Google Sheets for data storage, the system transforms unstructured patient inputs into standardized, actionable medical records that can be easily reviewed by healthcare providers.
1. Webhook → Intake Gateway
The process begins when a patient submits a refill request through the application.
Data captured: Date of birth, hospital number, requested medication (free text), and the user’s Entra object ID.
Function: Acts as the trigger for the entire workflow, ensuring that every request reliably enters the automation pipeline.
2. Verify User → Identity Validation
To safeguard patient data integrity, the workflow queries Microsoft Graph API using the Entra user ID.
Attributes retrieved: Display name, email, date of birth, hospital number, and gender.
Function: Confirms the identity of the requester and ensures the refill is tied to a verified patient profile.
Value: Reduces errors and prevents spoofed or fraudulent requests.
3. Edit Fields → Data Normalization
The raw Microsoft Graph response is standardized into a clean internal schema.
Data prepared: Timestamp of submission, verified patient attributes, and medication string from the intake.
Function: Provides consistent, predictable structure for downstream nodes, regardless of input variability.
Value: Establishes a “single source of truth” for the request details.
4. Classify Medication → AI-Powered Structuring
Patients often describe medications inconsistently:
“Paracetamol 500mg tablet”
“PCM 500 tab”
“Tylenol”
Although all three refer to the same drug, lack of standardization complicates processing.
At this stage, GPT-5 is applied to:
Interpret patient free-text input.
Normalize entries into structured JSON with fields such as:
generic_name
(e.g., paracetamol)strength
(e.g., 500 mg)form
(e.g., tablet)use_type
(e.g., oral, injection, topical)therapeutic_class
(e.g., analgesic/antipyretic)is_controlled_substance
(yes/no)
Enforce strict rules, restricting
use_type
to valid administration routes.Handle uncertainty by returning
"unknown"
where classification cannot be confidently determined.
Value: GPT-5 functions as a clinical text normalization engine, converting inconsistent patient input into standardized, actionable medical data.
5. Extract GPT JSON String → Output Parsing
The OpenAI Responses API returns nested objects rather than plain JSON.
Function: This node isolates and extracts the structured JSON from GPT-5’s output.
Value: Ensures downstream processing works with clean, usable classification data.
6. Merge Classification → Data Consolidation
Two parallel data streams converge here:
Patient demographics and request details (from Microsoft Graph).
Medication classification (from GPT-5).
Function: Merges them into a single enriched record.
Value: Produces a complete, unified view of both who is making the request and what they are requesting.
7. Build Final Row → Record Preparation
The consolidated data is formatted into a schema aligned with the destination spreadsheet.
Columns include:
SubmittedAt, DisplayName, Email, DateOfBirth, HospitalNumber, Medication, VerifiedGender, GenericName, Strength, Form, UseType, TherapeuticClass, IsControlledSubstance
.Function: Guarantees every record matches the required spreadsheet format.
Value: Enables accurate reporting, analytics, and integration with other systems.
8. Add to Google Sheets → Secure Storage
The final structured record is appended into a designated Google Sheet (Data!A:M
).
Function: Preserves refill requests as permanent entries.
Value: Creates a secure, queryable log that pharmacists can review, analysts can visualize, and compliance teams can audit.
Conclusion
This workflow transforms a manual, error-prone process into a fully automated pipeline:
Intake via webhook
Verified identity through Microsoft Graph
Cleaned and normalized attributes
AI-powered medication classification with GPT-5
Consolidation into a structured record
Secure storage in Google Sheets
By orchestrating these steps in n8n, healthcare teams gain efficiency, accuracy, and scalability while maintaining strict identity verification and auditability.
The complete n8n workflow is available as a JSON file for download below. You can import this file directly into n8n to implement the workflow.
|