The Walgreens Prescription API's allows users of third-party applications to quickly order refills of prescriptions originally filled at Walgreens pharmacies. Your app users can order refills in seconds to their nearest Walgreens to be notified when their prescription order is ready for pick up.
The Walgreens Prescription API's also allow for users of third-party applications to quickly order transfers of prescriptions originally filled at other pharmacies. Your app users can order trasnfers in seconds to their nearest Walgreens to be notified when their prescription order is ready for pick up.
The healthcare focused Prescription API is designed to increase prescription compliance and aid in personal health management through streamlining the refill process. By offering easy prescription refills or transfers through this health management API, Walgreens hopes to further increase health care adherence by reaching a wider audience through third party health care apps.
Below are the technical steps you will need to go through in order to get your integration configured and branded correctly:
The following API endpoints can be used to programmatically integrate all functions of the Prescription API's to Refill or Transfer:
This service is used to fetch the "landingURL" used for the Rx Refill API.
Sandbox: https://services-qa.walgreens.com/api/pharmacy/fetch/v1
Production: https://services.walgreens.com/api/pharmacy/fetch/v1
Request format | JSON |
Response format | JSON |
Authentication? | Yes |
Rate limited? | No |
Name | Optionality | Description | Example |
---|---|---|---|
apikey | required | Your API Key. | "AbCdEfGhIjKlMnOpQrStUvWxYz" |
affId | required | Your affiliateID. | "AAAAAAAAAA" |
transaction | required | The transaction is a value that invokes the refill service. The value must be "refill". | "refill" |
rxNumber | optional | The rxNumber is the number either scanned/manually entered by the customer, or from your system. | Sample Prescription Numbers |
callbackURL | required | The URL you want us to callback to at the end of any of the user flows. | "https://www.example.com/callback" |
devInf | optional | The device manufacturer and version. | "iPhone,13.0" |
appVer | optional | The build version of your application. | "1.0" |
curl --request POST \
--url 'https://services-qa.walgreens.com/api/pharmacy/fetch/v1' \
--header 'Content-Type: application/json' \
--data '{
"apikey":"YOUR_API_KEY",
"affId":"YOUR_AFFILIATE_ID",
"transaction":"refill",
"rxNumber":"#######-#####",
"callbackURL":"https://locahost:3000/callback",
"devInf":"DEVICE,##.#",
"appVer":"#.#"
}'
{
"landingUrl": "CHECKOUT LANDING URL",
"errCode": "ERROR CODE NUMBER",
"errMsg": "ERROR Message",
}
This service is used to open the "landingUrl" used for the Rx Refill API.
Sandbox: {landingUrl}
Production: {landingUrl}
Request format | HTML |
Response format | HTML |
Authentication? | No |
Rate limited? | No |
Name | Optionality | Description | Example |
---|---|---|---|
affiliateID | required | Your AffiliateID. | "AAAAAAAAAA" |
callbackURL | required | The callbackURL you asked us to callback to at the end of any of the user flows from the obtain refill request. | "https://www.example.com/callback" |
token | required | The token is a value that secures the checkout to the landingUrl and callbackURL. The value must be re-obtained for every checkout. | "token" |
rxNumber | optional | The rxNumber you asked us to refill from the obtain refill request. | Sample Prescription Numbers |
curl --location --request GET '{{RX_LANDING_URL}}'
{
"The website for the checkout of the Rx Refill API, that should be made in the browser window or WebView"
}
Once the user has completed their checkout on our website there are some user actions that must be handled by your callbackURL passed in obtaining the Refill API:
Value of callbackURL passed in the request + ? + value of status in the query string + = + the callback action value
Example of a call from the Successful completion callback:
"YOUR_CALLBACK_URL?affiliateID=YOUR_AFFILIATE_ID&action=refill&status=DONE"
//Make sure to implement the WKWebView delegates as needed
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
// Get the URL from the navigation action
if let url = navigationAction.request.url {
print(url)
// Inspect the URL
inspectURL(url)
// Allow navigation to proceed
decisionHandler(.allow)
} else {
// If there is no URL, deny the navigation
decisionHandler(.cancel)
}
}
private func inspectURL(_ url: URL) {
// Parse the query parameters from the URL
let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
let queryItems = components?.queryItems
//Check for a specific query parameter of action="refill"
if let action:String = queryItems?.first(where: { $0.name == "action" })?.value, action.lowercased() == "refill" {
print("Query parameter 'action' has value: \(action)")
//Check for a specific query parmater of status= one of the various callback status values
if let status = queryItems?.first(where: { $0.name == "status" })?.value?.lowercased() {
print("Query parameter 'status' has value: \(status)")
switch (status)
{
case "back":
print("Open the Error View Controller, customer backed out of their refill")
self.dismiss(animated: true)
break
case "cancel":
print("Open the Error View Controller, customer cancelled out of the checkout")
self.dismiss(animated: true)
break
case "done":
print("Open the Success View Controller, their Rx is on its way")
self.dismiss(animated: true)
break
case "error":
print("Open the Error View Controller, some error happened in the checkout")
self.dismiss(animated: true)
break
default:
print("Open the Error View Controller, some unknown callback happened")
print(status)
self.dismiss(animated: true)
break
}
return
}
}
}
//Make sure to implement the WebView overrides as needed
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
val url = request.url.toString()
println(url)
inspectUrl(request.url.toString())
return false // Allow WebView to handle the URL
}
private fun inspectUrl(url: String) {
//Parse the URI string to extract the query string
val uri = Uri.parse(url)
val queryParams = uri.queryParameterNames
//Check for a specific query parameter of action="refill"
if (queryParams.contains("action")) {
val action = uri.getQueryParameter("action")
if (action.equals("refill", ignoreCase = false)) {
println("Query parameter 'action' has value: $action")
//Check for a specific query parmater of status= one of the various callback status values
val status = uri.getQueryParameter("status")?.toLowerCase()
if (status != null) {
println("Query parameter 'status' has value: $status")
when (status) {
"back" -> {
println("Open the Error View, customer backed out of their refill")
finish()
}
"cancel" -> {
println("Open the Error View, customer cancelled out of the checkout")
finish()
}
"done" -> {
println("Open the Success View, their Rx is on its way")
finish()
}
"error" -> {
println("Open the Error View, some error happened in the checkout")
finish()
}
else -> {
println("Open the Error View, some unknown callback happened")
println(status)
finish()
}
}
}
}
}
}
//Route in your express router or server.js file, for the callbackURL which was passed when obtaining the checkout in your callbackURL value.
app.get('/callback', (req, res) => {
//Parse the URI string to extract the query string
const action = req.query.action ? req.query.action.toLowerCase() : '',
status = req.query.status ? req.query.status.toLowerCase() : '';
console.log(`Query parameter 'action' has value: ${action}`);
console.log(`Query parameter 'status' has value: ${status}`);
//Check for a specific query parameter of action="refill"
if(action !== "refill")
{
console.log(`Unknown Callback Action: ${action}`)
return res.render('pages/callback', { type: "Error", message: 'Some unknown action happened' });
}
//Check for a specific query parmater of status= one of the various callback status values
switch (status) {
case 'back':
res.render('pages/callback', { type: 'Warning', message: 'Backed out of refill' });
break;
case 'cancel':
return res.render('pages/callback', { type: 'Warning', message: 'Cancelled refill' });
break;
case 'done':
return res.render('pages/callback', { type: 'Success', message: 'Success, refill is on its way!' });
break;
case 'error':
return res.render('pages/callback', { type: "Error", message: 'Some error happened when refilling' });
break;
default:
console.log(`Unknown Callback Status: ${status}`)
return res.render('pages/callback', { type: "Error", message: 'Some unknown callback happened' });
break;
}
});
//We use then use EJS to render content for the appropriate callback message value in the callback.ejs page (you can use any view rendering engine)
<%= type %>, <%= message %>
This service is used to fetch the "landingURL" used for the Rx Transfer API.
Sandbox: https://services-qa.walgreens.com/api/pharmacy/fetch/v1
Production: https://services.walgreens.com/api/pharmacy/fetch/v1
Request format | JSON |
Response format | JSON |
Authentication? | Yes |
Rate limited? | No |
Name | Optionality | Description | Example |
---|---|---|---|
apikey | required | Your API Key. | "AbCdEfGhIjKlMnOpQrStUvWxYz" |
affId | required | Your affiliateID. | "AAAAAAAAAA" |
transaction | required | The transaction is a value that invokes the transfer service. The value must be "transfer". | "transfer" |
callbackURL | required | The URL you want us to callback to at the end of any of the user flows. | "https://www.example.com/callback" |
devInf | optional | The device manufacturer and version. | "iPhone,13.0" |
appVer | optional | The build version of your application. | "1.0" |
curl --request POST \
--url 'https://services-qa.walgreens.com/api/pharmacy/fetch/v1' \
--header 'Content-Type: application/json' \
--data '{
"apikey":"YOUR_API_KEY",
"affId":"YOUR_AFFILIATE_ID",
"transaction":"transfer",
"rxNumber":"#######-#####",
"callbackURL":"https://locahost:3000/callback",
"devInf":"DEVICE,##.#",
"appVer":"#.#"
}'
{
"landingUrl": "CHECKOUT LANDING URL",
"errCode": "ERROR CODE NUMBER",
"errMsg": "ERROR Message",
}
This service is used to open the "landingUrl" used for the Rx Transfer API.
Sandbox: {landingUrl}
Production: {landingUrl}
Request format | HTML |
Response format | HTML |
Authentication? | No |
Rate limited? | No |
Name | Optionality | Description | Example |
---|---|---|---|
affiliateID | required | Your AffiliateID. | "AAAAAAAAAA" |
callbackURL | required | The callbackURL you asked us to callback to at the end of any of the user flows from the obtain refill request. | "https://www.example.com/callback" |
token | required | The token is a value that secures the checkout to the landingUrl and callbackURL. The value must be re-obtained for every checkout. | "token" |
curl --location --request GET '{{RX_LANDING_URL}}'
{
"The website for the checkout of the Rx Transfer API, that should be made in the browser window or WebView"
}
Once the user has completed their checkout on our website there are some user actions that must be handled by your callbackURL passed in obtaining the Transfer API:
Value of callbackURL passed in the request + ? + value of status in the query string + = + the callback action value
Example of a call from the Successful completion callback:
"YOUR_CALLBACK_URL?affiliateID=YOUR_AFFILIATE_ID&action=transfer&status=DONE"
//Make sure to implement the WKWebView delegates as needed
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
// Get the URL from the navigation action
if let url = navigationAction.request.url {
print(url)
// Inspect the URL
inspectURL(url)
// Allow navigation to proceed
decisionHandler(.allow)
} else {
// If there is no URL, deny the navigation
decisionHandler(.cancel)
}
}
private func inspectURL(_ url: URL) {
// Parse the query parameters from the URL
let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
let queryItems = components?.queryItems
//Check for a specific query parameter of action="transfer"
if let action:String = queryItems?.first(where: { $0.name == "action" })?.value, action.lowercased() == "transfer" {
print("Query parameter 'action' has value: \(action)")
//Check for a specific query parmater of status= one of the various callback status values
if let status = queryItems?.first(where: { $0.name == "status" })?.value?.lowercased() {
print("Query parameter 'status' has value: \(status)")
switch (status)
{
case "back":
print("Open the Error View Controller, customer backed out of their transfer")
self.dismiss(animated: true)
break
case "cancel":
print("Open the Error View Controller, customer cancelled out of the checkout")
self.dismiss(animated: true)
break
case "done":
print("Open the Success View Controller, their Rx is on its way")
self.dismiss(animated: true)
break
case "error":
print("Open the Error View Controller, some error happened in the checkout")
self.dismiss(animated: true)
break
default:
print("Open the Error View Controller, some unknown callback happened")
print(status)
self.dismiss(animated: true)
break
}
return
}
}
}
//Make sure to implement the WebView overrides as needed
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
val url = request.url.toString()
println(url)
inspectUrl(request.url.toString())
return false // Allow WebView to handle the URL
}
private fun inspectUrl(url: String) {
//Parse the URI string to extract the query string
val uri = Uri.parse(url)
val queryParams = uri.queryParameterNames
//Check for a specific query parameter of action="transfer"
if (queryParams.contains("action")) {
val action = uri.getQueryParameter("action")
if (action.equals("transfer", ignoreCase = false)) {
println("Query parameter 'action' has value: $action")
//Check for a specific query parmater of status= one of the various callback status values
val status = uri.getQueryParameter("status")?.toLowerCase()
if (status != null) {
println("Query parameter 'status' has value: $status")
when (status) {
"back" -> {
println("Open the Error View, customer backed out of their transfer")
finish()
}
"cancel" -> {
println("Open the Error View, customer cancelled out of the checkout")
finish()
}
"done" -> {
println("Open the Success View, their Rx is on its way")
finish()
}
"error" -> {
println("Open the Error View, some error happened in the checkout")
finish()
}
else -> {
println("Open the Error View, some unknown callback happened")
println(status)
finish()
}
}
}
}
}
}
//Route in your express router or server.js file, for the callbackURL which was passed when obtaining the checkout in your callbackURL value.
app.get('/callback', (req, res) => {
//Parse the URI string to extract the query string
const action = req.query.action ? req.query.action.toLowerCase() : '',
status = req.query.status ? req.query.status.toLowerCase() : '';
console.log(`Query parameter 'action' has value: ${action}`);
console.log(`Query parameter 'status' has value: ${status}`);
//Check for a specific query parameter of action="refill"
if(action !== "transfer")
{
console.log(`Unknown Callback Action: ${action}`)
return res.render('pages/callback', { type: "Error", message: 'Some unknown action happened' });
}
//Check for a specific query parmater of status= one of the various callback status values
switch (status) {
case 'back':
res.render('pages/callback', { type: 'Warning', message: 'Backed out of transfer' });
break;
case 'cancel':
return res.render('pages/callback', { type: 'Warning', message: 'Cancelled transfer' });
break;
case 'done':
return res.render('pages/callback', { type: 'Success', message: 'Success, transfer is on its way!' });
break;
case 'error':
return res.render('pages/callback', { type: "Error", message: 'Some error happened when transfering' });
break;
default:
console.log(`Unknown Callback Status: ${status}`)
return res.render('pages/callback', { type: "Error", message: 'Some unknown callback happened' });
break;
}
});
//We use then use EJS to render content for the appropriate callback message value in the callback.ejs page (you can use any view rendering engine)
<%= type %>, <%= message %>
Obviously we don't want any errors to happen, but sometimes they do and as a result we respond the following error codes. The table below helps explain why each of the error codes could occur:
Code | Type | Message |
---|---|---|
403 | ERROR | Invalid JSON or Invalid APIKey. |
500 | ERROR | Gateway or Server Error, please contact API team. |
Code | Type | Message |
---|---|---|
403 | ERROR | Invalid JSON or Invalid APIKey. |
500 | ERROR | Gateway or Server Error, please contact API team. |