From 1bdd2d507a4bf57958751f9f8018954750c45fc9 Mon Sep 17 00:00:00 2001
From: Jerry Padgett <sjpadgett@gmail.com>
Date: Mon, 15 Jul 2024 15:42:27 -0400
Subject: [PATCH] Add back RingCentral to our FaxSMS Module (#7542)

* Add back RingCentral to our FaxSMS Module
- need RC SDK

* Files clean up
Twillio fax is gone ans so is fax server and good riddence!
refactors
RC forms

* Modify FileUtils class adding methods for mime management.

* author

* finish file utility

* more RC integration
remove unneeded 2FA redirect file rcauth.

* add mass deletes to RC and etherFAX
working on UI list

* add email client
add email notification to cron script
cleanup

* update twilio version

* escaping and error alerts

* review fixws

* add user alert  to email setuop to use standard SMPT setup
---
 composer.json                                 |    3 +-
 composer.lock                                 |  857 +++++++++++-
 .../oe-module-faxsms/composer.json            |    3 -
 .../oe-module-faxsms/contact.php              |   26 +-
 .../library/rc_sms_notification.php           |  125 +-
 .../library/setup_services.php                |   89 +-
 .../oe-module-faxsms/messageUI.php            |  469 +++++--
 .../oe-module-faxsms/moduleConfig.php         |    1 -
 .../oe-module-faxsms/openemr.bootstrap.php    |   29 +-
 .../custom_modules/oe-module-faxsms/setup.php |   47 +
 .../oe-module-faxsms/setup_email.php          |  163 +++
 .../oe-module-faxsms/setup_rc.php             |  198 +++
 .../oe-module-faxsms/src/BootstrapService.php |    4 +-
 .../src/Controller/AppDispatch.php            |   99 +-
 .../src/Controller/EmailClient.php            |   55 +-
 .../src/Controller/EtherFaxActions.php        |  171 ++-
 .../src/Controller/RCFaxClient.php            | 1231 +++++++++++++++++
 .../src/Controller/TwilioSMSClient.php        |   19 +-
 .../oe-module-faxsms/version.php              |    2 +-
 src/Common/Utils/FileUtils.php                |  120 +-
 20 files changed, 3404 insertions(+), 307 deletions(-)
 create mode 100644 interface/modules/custom_modules/oe-module-faxsms/setup_email.php
 create mode 100644 interface/modules/custom_modules/oe-module-faxsms/setup_rc.php
 create mode 100644 interface/modules/custom_modules/oe-module-faxsms/src/Controller/RCFaxClient.php

diff --git a/composer.json b/composer.json
index b7bd46aecdd..fca84368cfc 100644
--- a/composer.json
+++ b/composer.json
@@ -77,7 +77,8 @@
         "vlucas/phpdotenv": "5.5.0",
         "waryway/php-traits-library": "1.0.4",
         "yubico/u2flib-server": "1.0.2",
-        "twilio/sdk": "7.12.0"
+        "twilio/sdk": "7.16.2",
+        "ringcentral/ringcentral-php": "3.0.1"
     },
     "config": {
         "platform": {
diff --git a/composer.lock b/composer.lock
index 4b703f0b3da..3ef863cc061 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "1f7af00752aeae4abc29c2437615cae9",
+    "content-hash": "0df2348dc099b9fefe4bb43ba76d9d32",
     "packages": [
         {
             "name": "academe/authorizenet-objects",
@@ -702,6 +702,53 @@
             },
             "time": "2023-02-07T12:51:48+00:00"
         },
+        {
+            "name": "evenement/evenement",
+            "version": "v3.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/igorw/evenement.git",
+                "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/igorw/evenement/zipball/0a16b0d71ab13284339abb99d9d2bd813640efbc",
+                "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9 || ^6"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Evenement\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Igor Wiedler",
+                    "email": "igor@wiedler.ch"
+                }
+            ],
+            "description": "Événement is a very simple event dispatching library for PHP",
+            "keywords": [
+                "event-dispatcher",
+                "event-emitter"
+            ],
+            "support": {
+                "issues": "https://github.com/igorw/evenement/issues",
+                "source": "https://github.com/igorw/evenement/tree/v3.0.2"
+            },
+            "time": "2023-08-08T05:53:35+00:00"
+        },
         {
             "name": "ezyang/htmlpurifier",
             "version": "v4.16.0",
@@ -7120,6 +7167,63 @@
             },
             "time": "2021-10-29T13:26:27+00:00"
         },
+        {
+            "name": "pubnub/pubnub",
+            "version": "6.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/pubnub/php.git",
+                "reference": "26de0a59dda29f1246378995f96c4f95f6d64980"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/pubnub/php/zipball/26de0a59dda29f1246378995f96c4f95f6d64980",
+                "reference": "26de0a59dda29f1246378995f96c4f95f6d64980",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.4|>=8.0",
+                "psr/log": "^1.1|^2.0|^3.0",
+                "rmccue/requests": "^2.0"
+            },
+            "require-dev": {
+                "monolog/monolog": "^2.9 || ^3.0",
+                "phpstan/phpstan": "^1.8",
+                "phpunit/phpunit": "^9.5",
+                "squizlabs/php_codesniffer": "^3.7"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "PubNub\\": "src/PubNub"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "proprietary"
+            ],
+            "authors": [
+                {
+                    "name": "PubNub",
+                    "email": "support@pubnub.com"
+                }
+            ],
+            "description": "This is the official PubNub PHP SDK repository.",
+            "homepage": "http://www.pubnub.com/",
+            "keywords": [
+                "ajax",
+                "api",
+                "push",
+                "real time",
+                "real-time",
+                "realtime"
+            ],
+            "support": {
+                "issues": "https://github.com/pubnub/php/issues",
+                "source": "https://github.com/pubnub/php/tree/v6.3.0"
+            },
+            "time": "2024-06-19T07:31:01+00:00"
+        },
         {
             "name": "ralouphie/getallheaders",
             "version": "3.0.3",
@@ -7345,6 +7449,738 @@
             ],
             "time": "2023-04-15T23:01:58+00:00"
         },
+        {
+            "name": "ratchet/pawl",
+            "version": "v0.4.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/ratchetphp/Pawl.git",
+                "reference": "af70198bab77a582b31169d3cc3982bed25c161f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/ratchetphp/Pawl/zipball/af70198bab77a582b31169d3cc3982bed25c161f",
+                "reference": "af70198bab77a582b31169d3cc3982bed25c161f",
+                "shasum": ""
+            },
+            "require": {
+                "evenement/evenement": "^3.0 || ^2.0",
+                "guzzlehttp/psr7": "^2.0 || ^1.7",
+                "php": ">=5.4",
+                "ratchet/rfc6455": "^0.3.1",
+                "react/socket": "^1.9"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8"
+            },
+            "suggest": {
+                "reactivex/rxphp": "~2.0"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "src/functions_include.php"
+                ],
+                "psr-4": {
+                    "Ratchet\\Client\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "Asynchronous WebSocket client",
+            "keywords": [
+                "Ratchet",
+                "async",
+                "client",
+                "websocket",
+                "websocket client"
+            ],
+            "support": {
+                "issues": "https://github.com/ratchetphp/Pawl/issues",
+                "source": "https://github.com/ratchetphp/Pawl/tree/v0.4.1"
+            },
+            "time": "2021-12-10T14:32:34+00:00"
+        },
+        {
+            "name": "ratchet/rfc6455",
+            "version": "v0.3.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/ratchetphp/RFC6455.git",
+                "reference": "7c964514e93456a52a99a20fcfa0de242a43ccdb"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/ratchetphp/RFC6455/zipball/7c964514e93456a52a99a20fcfa0de242a43ccdb",
+                "reference": "7c964514e93456a52a99a20fcfa0de242a43ccdb",
+                "shasum": ""
+            },
+            "require": {
+                "guzzlehttp/psr7": "^2 || ^1.7",
+                "php": ">=5.4.2"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^5.7",
+                "react/socket": "^1.3"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Ratchet\\RFC6455\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Chris Boden",
+                    "email": "cboden@gmail.com",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Matt Bonneau",
+                    "role": "Developer"
+                }
+            ],
+            "description": "RFC6455 WebSocket protocol handler",
+            "homepage": "http://socketo.me",
+            "keywords": [
+                "WebSockets",
+                "rfc6455",
+                "websocket"
+            ],
+            "support": {
+                "chat": "https://gitter.im/reactphp/reactphp",
+                "issues": "https://github.com/ratchetphp/RFC6455/issues",
+                "source": "https://github.com/ratchetphp/RFC6455/tree/v0.3.1"
+            },
+            "time": "2021-12-09T23:20:49+00:00"
+        },
+        {
+            "name": "react/cache",
+            "version": "v1.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/reactphp/cache.git",
+                "reference": "d47c472b64aa5608225f47965a484b75c7817d5b"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/reactphp/cache/zipball/d47c472b64aa5608225f47965a484b75c7817d5b",
+                "reference": "d47c472b64aa5608225f47965a484b75c7817d5b",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0",
+                "react/promise": "^3.0 || ^2.0 || ^1.1"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "React\\Cache\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Christian Lück",
+                    "email": "christian@clue.engineering",
+                    "homepage": "https://clue.engineering/"
+                },
+                {
+                    "name": "Cees-Jan Kiewiet",
+                    "email": "reactphp@ceesjankiewiet.nl",
+                    "homepage": "https://wyrihaximus.net/"
+                },
+                {
+                    "name": "Jan Sorgalla",
+                    "email": "jsorgalla@gmail.com",
+                    "homepage": "https://sorgalla.com/"
+                },
+                {
+                    "name": "Chris Boden",
+                    "email": "cboden@gmail.com",
+                    "homepage": "https://cboden.dev/"
+                }
+            ],
+            "description": "Async, Promise-based cache interface for ReactPHP",
+            "keywords": [
+                "cache",
+                "caching",
+                "promise",
+                "reactphp"
+            ],
+            "support": {
+                "issues": "https://github.com/reactphp/cache/issues",
+                "source": "https://github.com/reactphp/cache/tree/v1.2.0"
+            },
+            "funding": [
+                {
+                    "url": "https://opencollective.com/reactphp",
+                    "type": "open_collective"
+                }
+            ],
+            "time": "2022-11-30T15:59:55+00:00"
+        },
+        {
+            "name": "react/dns",
+            "version": "v1.13.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/reactphp/dns.git",
+                "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/reactphp/dns/zipball/eb8ae001b5a455665c89c1df97f6fb682f8fb0f5",
+                "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0",
+                "react/cache": "^1.0 || ^0.6 || ^0.5",
+                "react/event-loop": "^1.2",
+                "react/promise": "^3.2 || ^2.7 || ^1.2.1"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36",
+                "react/async": "^4.3 || ^3 || ^2",
+                "react/promise-timer": "^1.11"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "React\\Dns\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Christian Lück",
+                    "email": "christian@clue.engineering",
+                    "homepage": "https://clue.engineering/"
+                },
+                {
+                    "name": "Cees-Jan Kiewiet",
+                    "email": "reactphp@ceesjankiewiet.nl",
+                    "homepage": "https://wyrihaximus.net/"
+                },
+                {
+                    "name": "Jan Sorgalla",
+                    "email": "jsorgalla@gmail.com",
+                    "homepage": "https://sorgalla.com/"
+                },
+                {
+                    "name": "Chris Boden",
+                    "email": "cboden@gmail.com",
+                    "homepage": "https://cboden.dev/"
+                }
+            ],
+            "description": "Async DNS resolver for ReactPHP",
+            "keywords": [
+                "async",
+                "dns",
+                "dns-resolver",
+                "reactphp"
+            ],
+            "support": {
+                "issues": "https://github.com/reactphp/dns/issues",
+                "source": "https://github.com/reactphp/dns/tree/v1.13.0"
+            },
+            "funding": [
+                {
+                    "url": "https://opencollective.com/reactphp",
+                    "type": "open_collective"
+                }
+            ],
+            "time": "2024-06-13T14:18:03+00:00"
+        },
+        {
+            "name": "react/event-loop",
+            "version": "v1.5.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/reactphp/event-loop.git",
+                "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/reactphp/event-loop/zipball/bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354",
+                "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36"
+            },
+            "suggest": {
+                "ext-pcntl": "For signal handling support when using the StreamSelectLoop"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "React\\EventLoop\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Christian Lück",
+                    "email": "christian@clue.engineering",
+                    "homepage": "https://clue.engineering/"
+                },
+                {
+                    "name": "Cees-Jan Kiewiet",
+                    "email": "reactphp@ceesjankiewiet.nl",
+                    "homepage": "https://wyrihaximus.net/"
+                },
+                {
+                    "name": "Jan Sorgalla",
+                    "email": "jsorgalla@gmail.com",
+                    "homepage": "https://sorgalla.com/"
+                },
+                {
+                    "name": "Chris Boden",
+                    "email": "cboden@gmail.com",
+                    "homepage": "https://cboden.dev/"
+                }
+            ],
+            "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.",
+            "keywords": [
+                "asynchronous",
+                "event-loop"
+            ],
+            "support": {
+                "issues": "https://github.com/reactphp/event-loop/issues",
+                "source": "https://github.com/reactphp/event-loop/tree/v1.5.0"
+            },
+            "funding": [
+                {
+                    "url": "https://opencollective.com/reactphp",
+                    "type": "open_collective"
+                }
+            ],
+            "time": "2023-11-13T13:48:05+00:00"
+        },
+        {
+            "name": "react/promise",
+            "version": "v3.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/reactphp/promise.git",
+                "reference": "8a164643313c71354582dc850b42b33fa12a4b63"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/reactphp/promise/zipball/8a164643313c71354582dc850b42b33fa12a4b63",
+                "reference": "8a164643313c71354582dc850b42b33fa12a4b63",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1.0"
+            },
+            "require-dev": {
+                "phpstan/phpstan": "1.10.39 || 1.4.10",
+                "phpunit/phpunit": "^9.6 || ^7.5"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "src/functions_include.php"
+                ],
+                "psr-4": {
+                    "React\\Promise\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jan Sorgalla",
+                    "email": "jsorgalla@gmail.com",
+                    "homepage": "https://sorgalla.com/"
+                },
+                {
+                    "name": "Christian Lück",
+                    "email": "christian@clue.engineering",
+                    "homepage": "https://clue.engineering/"
+                },
+                {
+                    "name": "Cees-Jan Kiewiet",
+                    "email": "reactphp@ceesjankiewiet.nl",
+                    "homepage": "https://wyrihaximus.net/"
+                },
+                {
+                    "name": "Chris Boden",
+                    "email": "cboden@gmail.com",
+                    "homepage": "https://cboden.dev/"
+                }
+            ],
+            "description": "A lightweight implementation of CommonJS Promises/A for PHP",
+            "keywords": [
+                "promise",
+                "promises"
+            ],
+            "support": {
+                "issues": "https://github.com/reactphp/promise/issues",
+                "source": "https://github.com/reactphp/promise/tree/v3.2.0"
+            },
+            "funding": [
+                {
+                    "url": "https://opencollective.com/reactphp",
+                    "type": "open_collective"
+                }
+            ],
+            "time": "2024-05-24T10:39:05+00:00"
+        },
+        {
+            "name": "react/socket",
+            "version": "v1.15.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/reactphp/socket.git",
+                "reference": "216d3aec0b87f04a40ca04f481e6af01bdd1d038"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/reactphp/socket/zipball/216d3aec0b87f04a40ca04f481e6af01bdd1d038",
+                "reference": "216d3aec0b87f04a40ca04f481e6af01bdd1d038",
+                "shasum": ""
+            },
+            "require": {
+                "evenement/evenement": "^3.0 || ^2.0 || ^1.0",
+                "php": ">=5.3.0",
+                "react/dns": "^1.11",
+                "react/event-loop": "^1.2",
+                "react/promise": "^3 || ^2.6 || ^1.2.1",
+                "react/stream": "^1.2"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36",
+                "react/async": "^4 || ^3 || ^2",
+                "react/promise-stream": "^1.4",
+                "react/promise-timer": "^1.10"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "React\\Socket\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Christian Lück",
+                    "email": "christian@clue.engineering",
+                    "homepage": "https://clue.engineering/"
+                },
+                {
+                    "name": "Cees-Jan Kiewiet",
+                    "email": "reactphp@ceesjankiewiet.nl",
+                    "homepage": "https://wyrihaximus.net/"
+                },
+                {
+                    "name": "Jan Sorgalla",
+                    "email": "jsorgalla@gmail.com",
+                    "homepage": "https://sorgalla.com/"
+                },
+                {
+                    "name": "Chris Boden",
+                    "email": "cboden@gmail.com",
+                    "homepage": "https://cboden.dev/"
+                }
+            ],
+            "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP",
+            "keywords": [
+                "Connection",
+                "Socket",
+                "async",
+                "reactphp",
+                "stream"
+            ],
+            "support": {
+                "issues": "https://github.com/reactphp/socket/issues",
+                "source": "https://github.com/reactphp/socket/tree/v1.15.0"
+            },
+            "funding": [
+                {
+                    "url": "https://opencollective.com/reactphp",
+                    "type": "open_collective"
+                }
+            ],
+            "time": "2023-12-15T11:02:10+00:00"
+        },
+        {
+            "name": "react/stream",
+            "version": "v1.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/reactphp/stream.git",
+                "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/reactphp/stream/zipball/1e5b0acb8fe55143b5b426817155190eb6f5b18d",
+                "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d",
+                "shasum": ""
+            },
+            "require": {
+                "evenement/evenement": "^3.0 || ^2.0 || ^1.0",
+                "php": ">=5.3.8",
+                "react/event-loop": "^1.2"
+            },
+            "require-dev": {
+                "clue/stream-filter": "~1.2",
+                "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "React\\Stream\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Christian Lück",
+                    "email": "christian@clue.engineering",
+                    "homepage": "https://clue.engineering/"
+                },
+                {
+                    "name": "Cees-Jan Kiewiet",
+                    "email": "reactphp@ceesjankiewiet.nl",
+                    "homepage": "https://wyrihaximus.net/"
+                },
+                {
+                    "name": "Jan Sorgalla",
+                    "email": "jsorgalla@gmail.com",
+                    "homepage": "https://sorgalla.com/"
+                },
+                {
+                    "name": "Chris Boden",
+                    "email": "cboden@gmail.com",
+                    "homepage": "https://cboden.dev/"
+                }
+            ],
+            "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP",
+            "keywords": [
+                "event-driven",
+                "io",
+                "non-blocking",
+                "pipe",
+                "reactphp",
+                "readable",
+                "stream",
+                "writable"
+            ],
+            "support": {
+                "issues": "https://github.com/reactphp/stream/issues",
+                "source": "https://github.com/reactphp/stream/tree/v1.4.0"
+            },
+            "funding": [
+                {
+                    "url": "https://opencollective.com/reactphp",
+                    "type": "open_collective"
+                }
+            ],
+            "time": "2024-06-11T12:45:25+00:00"
+        },
+        {
+            "name": "ringcentral/ringcentral-php",
+            "version": "3.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/ringcentral/ringcentral-php.git",
+                "reference": "ebda301de452e4357eac982e092d2aec02153ad8"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/ringcentral/ringcentral-php/zipball/ebda301de452e4357eac982e092d2aec02153ad8",
+                "reference": "ebda301de452e4357eac982e092d2aec02153ad8",
+                "shasum": ""
+            },
+            "require": {
+                "ext-curl": "*",
+                "ext-json": "*",
+                "guzzlehttp/guzzle": "^6.3.3|^7.4.1",
+                "guzzlehttp/psr7": "^2.1.0",
+                "php": ">=7.2",
+                "pubnub/pubnub": "^4.7.0|^6.0",
+                "ratchet/pawl": "^0.4.1",
+                "symfony/event-dispatcher": "^2|^3|^4|^5|^6"
+            },
+            "require-dev": {
+                "macfja/phar-builder": "^0.2.8",
+                "php-coveralls/php-coveralls": "^2.1",
+                "phpunit/phpunit": "^7.5.12|^8.0|^9.0",
+                "react/async": "^3.0"
+            },
+            "suggest": {
+                "ext-openssl": "to decrypt PubNub messages"
+            },
+            "type": "library",
+            "extra": {
+                "phar-builder": {
+                    "compression": "gzip",
+                    "name": "ringcentral.phar",
+                    "output-dir": "./dist",
+                    "entry-point": "./vendor/autoload.php",
+                    "skip-shebang": true,
+                    "include": [],
+                    "excluded": [
+                        "vendor/bin"
+                    ],
+                    "include-dev": false
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "RingCentral\\SDK\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Kirill Konshin",
+                    "email": "kirill.konshin@ringcentral.com"
+                },
+                {
+                    "name": "Byrne Reese",
+                    "email": "byrne.reese@ringcentral.com"
+                }
+            ],
+            "description": "RingCentral Platform PHP SDK",
+            "homepage": "http://developers.ringcentral.com",
+            "keywords": [
+                "api",
+                "connect",
+                "php",
+                "platform",
+                "ringcentral",
+                "sdk"
+            ],
+            "support": {
+                "issues": "https://github.com/ringcentral/ringcentral-php/issues",
+                "source": "https://github.com/ringcentral/ringcentral-php/tree/3.0.1"
+            },
+            "time": "2024-03-12T02:58:29+00:00"
+        },
+        {
+            "name": "rmccue/requests",
+            "version": "v2.0.12",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/WordPress/Requests.git",
+                "reference": "fb67e3d392ff6b89a90e96f19745662f4ecd62b1"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/WordPress/Requests/zipball/fb67e3d392ff6b89a90e96f19745662f4ecd62b1",
+                "reference": "fb67e3d392ff6b89a90e96f19745662f4ecd62b1",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*",
+                "php": ">=5.6"
+            },
+            "require-dev": {
+                "dealerdirect/phpcodesniffer-composer-installer": "^0.7",
+                "php-parallel-lint/php-console-highlighter": "^0.5.0",
+                "php-parallel-lint/php-parallel-lint": "^1.3.1",
+                "phpcompatibility/php-compatibility": "^9.0",
+                "requests/test-server": "dev-main",
+                "roave/security-advisories": "dev-latest",
+                "squizlabs/php_codesniffer": "^3.6",
+                "wp-coding-standards/wpcs": "^2.0",
+                "yoast/phpunit-polyfills": "^1.0.0"
+            },
+            "suggest": {
+                "art4/requests-psr18-adapter": "For using Requests as a PSR-18 HTTP Client",
+                "ext-curl": "For improved performance",
+                "ext-openssl": "For secure transport support",
+                "ext-zlib": "For improved performance when decompressing encoded streams"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "library/Deprecated.php"
+                ],
+                "psr-4": {
+                    "WpOrg\\Requests\\": "src/"
+                },
+                "classmap": [
+                    "library/Requests.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "ISC"
+            ],
+            "authors": [
+                {
+                    "name": "Ryan McCue",
+                    "homepage": "https://rmccue.io/"
+                },
+                {
+                    "name": "Alain Schlesser",
+                    "homepage": "https://github.com/schlessera"
+                },
+                {
+                    "name": "Juliette Reinders Folmer",
+                    "homepage": "https://github.com/jrfnl"
+                },
+                {
+                    "name": "Contributors",
+                    "homepage": "https://github.com/WordPress/Requests/graphs/contributors"
+                }
+            ],
+            "description": "A HTTP library written in PHP, for human beings.",
+            "homepage": "https://requests.ryanmccue.info/",
+            "keywords": [
+                "curl",
+                "fsockopen",
+                "http",
+                "idna",
+                "ipv6",
+                "iri",
+                "sockets"
+            ],
+            "support": {
+                "docs": "https://requests.ryanmccue.info/",
+                "issues": "https://github.com/WordPress/Requests/issues",
+                "source": "https://github.com/WordPress/Requests"
+            },
+            "time": "2024-07-08T08:10:42+00:00"
+        },
         {
             "name": "robthree/twofactorauth",
             "version": "v2.0.0",
@@ -9811,16 +10647,16 @@
         },
         {
             "name": "twilio/sdk",
-            "version": "7.12.0",
+            "version": "7.16.2",
             "source": {
                 "type": "git",
-                "url": "git@github.com:twilio/twilio-php.git",
-                "reference": "f281d7b793f02377ab0f38e7166e16820acc3eae"
+                "url": "https://github.com/twilio/twilio-php.git",
+                "reference": "02ad214b0cc9fc513bd67df251d54aed8901284f"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/twilio/twilio-php/zipball/f281d7b793f02377ab0f38e7166e16820acc3eae",
-                "reference": "f281d7b793f02377ab0f38e7166e16820acc3eae",
+                "url": "https://api.github.com/repos/twilio/twilio-php/zipball/02ad214b0cc9fc513bd67df251d54aed8901284f",
+                "reference": "02ad214b0cc9fc513bd67df251d54aed8901284f",
                 "shasum": ""
             },
             "require": {
@@ -9856,7 +10692,11 @@
                 "sms",
                 "twilio"
             ],
-            "time": "2023-10-19T11:57:30+00:00"
+            "support": {
+                "issues": "https://github.com/twilio/twilio-php/issues",
+                "source": "https://github.com/twilio/twilio-php/tree/7.16.2"
+            },
+            "time": "2024-04-01T10:34:42+00:00"
         },
         {
             "name": "vlucas/phpdotenv",
@@ -12483,7 +13323,8 @@
     },
     "platform-dev": [],
     "platform-overrides": {
-        "php": "8.1"
+        "php": "8.1",
+        "ext-intl": "8.1"
     },
     "plugin-api-version": "2.6.0"
 }
diff --git a/interface/modules/custom_modules/oe-module-faxsms/composer.json b/interface/modules/custom_modules/oe-module-faxsms/composer.json
index 1cc9e2b08d5..1549c470ca3 100644
--- a/interface/modules/custom_modules/oe-module-faxsms/composer.json
+++ b/interface/modules/custom_modules/oe-module-faxsms/composer.json
@@ -18,8 +18,5 @@
         "psr-4": {"OpenEMR\\Modules\\FaxSMS\\": "src/"}
     },
     "require": {
-    },
-    "conflict": {
-        "openemr/openemr": "<7.0.1"
     }
 }
diff --git a/interface/modules/custom_modules/oe-module-faxsms/contact.php b/interface/modules/custom_modules/oe-module-faxsms/contact.php
index e6a82db6009..a14a89b3579 100644
--- a/interface/modules/custom_modules/oe-module-faxsms/contact.php
+++ b/interface/modules/custom_modules/oe-module-faxsms/contact.php
@@ -24,6 +24,9 @@
     die("<h3>" . xlt("Not Authorised!") . "</h3>");
 }
 $logged_in = $clientApp->authenticate();
+if ($logged_in != 1) {
+    die("<h3>" . text($logged_in) . "</h3>");
+}
 $isSMS = $clientApp->getRequest('isSMS', false);
 $isEmail = $clientApp->getRequest('isEmail', false);
 $isForward = $isFax = 0;
@@ -215,12 +218,16 @@ function (data) {
         const getContactBook = function (e, rtnpid) {
             e.preventDefault();
             let btnClose = <?php echo xlj("Cancel"); ?>;
-            dlgopen('', '', 'modal-lg', 500, '', '', {
+            dlgopen('', '', 'modal-lg', '', '', '', {
                 buttons: [
                     {text: btnClose, close: true, style: 'primary  btn-sm'}
                 ],
                 url: top.webroot_url + '/interface/usergroup/addrbook_list.php?popup=2&type=' + encodeURIComponent(<?php echo js_escape($serviceType); ?>),
-                dialogId: 'fax'
+                dialogId: 'fax',
+                resolvePromiseOn: 'close',
+                sizeHeight: 'full'
+            }).then(function (contact) {
+                top.restoreSession();
             });
         };
     </script>
@@ -248,20 +255,19 @@ function (data) {
             <div class="row">
                 <div class="col-md-12">
                     <div class="form-group forwardExclude smsExclude faxExclude">
-                        <label for="form_pid"><?php echo xlt('MRN') ?></label>
+                        <label for="form_pid"><?php echo xlt('MRN or PID') ?></label>
                         <input id="form_pid" type="text" name="form_pid" class="form-control"
-                            placeholder="<?php echo xla('If Applicable for charting.') ?>"
+                            title="<?php echo xla('If Applicable for charting.') ?>"
                             value="<?php echo attr($interface_pid ?? 0) ?>" />
                     </div>
                     <div class="form-group show-detail">
                         <label for="form_name"><?php echo xlt('Name') ?></label>
                         <input id="form_name" type="text" name="name" class="form-control"
-                            placeholder="<?php echo xla('Not Required') ?>"
+                            placeholder="<?php echo xla('First name. Optionally if included then sent.') ?>"
                             value="<?php echo attr($details['fname'] ?? '') ?>" />
-                        <div class="form-group show-detail smsExclude faxExclude">
-                            <!--<label for="form_lastname"><?php /*echo xlt('Lastname') */ ?></label>-->
+                        <div class="form-group mt-1 show-detail smsExclude faxExclude">
                             <input id="form_lastname" type="text" name="surname" class="form-control"
-                                placeholder="<?php echo xla('Not Required') ?>"
+                                placeholder="<?php echo xla('Last name. Optionally if included then sent.') ?>"
                                 value="<?php echo attr($details['lname'] ?? '') ?>" />
                         </div>
                         <div class="form-group faxExclude smsExclude show-detail">
@@ -274,7 +280,7 @@ function (data) {
                                     echo($isSMTP ? xla('Forward to email address if address is included.') : xla('Unavailable! Setup SMTP in Config Notifications.'));
                                 }
                                 ?>"
-                                title="<?php echo xla('Forward to an email address.') ?>" />
+                                title="<?php echo xla('Attach and send to an email Address.') ?>" />
                         </div>
                         <div class="form-group">
                             <label for="form_phone"><?php echo xlt('Recipient Phone') ?></label>
@@ -287,7 +293,7 @@ function (data) {
                         <div class="form-group">
                             <label for="form_message"><?php echo xlt('Message') ?></label>
                             <textarea id="form_message" name="comments" class="form-control" placeholder="
-                            <?php echo xla('Add a note to recipient.'); ?>" rows="6"><?php echo $default_message; ?></textarea>
+                            <?php echo "\n" . xla('Add a note to recipient or cover sheet. If enabled, double click for Text Templates.'); ?>" rows="6"><?php echo $default_message; ?></textarea>
                         </div>
                         <?php } ?>
                         <div>
diff --git a/interface/modules/custom_modules/oe-module-faxsms/library/rc_sms_notification.php b/interface/modules/custom_modules/oe-module-faxsms/library/rc_sms_notification.php
index 4f0658cd1ae..1e44eeedef3 100644
--- a/interface/modules/custom_modules/oe-module-faxsms/library/rc_sms_notification.php
+++ b/interface/modules/custom_modules/oe-module-faxsms/library/rc_sms_notification.php
@@ -23,12 +23,12 @@
 $_SERVER['SERVER_NAME'] = 'localhost';
 $backpic = "";
 $clientApp = null;
+$emailApp = null;
 // for cron
 $error = '';
 $runtime = [];
 
 // Check for other arguments and perform your script logic
-
 if (($argc ?? null) > 1) {
     foreach ($argv as $k => $v) {
         if ($k == 0) {
@@ -68,16 +68,29 @@
 $TYPE = '';
 if (!empty($runtime['type'])) {
     $TYPE = strtoupper($runtime['type']);
+} elseif ($_GET['type'] ?? '' === 'email') {
+    $TYPE = $runtime['type'] = "EMAIL";
 } else {
     $TYPE = $runtime['type'] = "SMS"; // default
 }
+
 $CRON_TIME = 150;
 // use service if needed
 if ($TYPE === "SMS") {
-    $_SESSION['authUser'] = $runtime['user'] ?? '';
+    $_SESSION['authUser'] = $runtime['user'] ?? $_SESSION['authUser'];
     $clientApp = AppDispatch::getApiService('sms');
     $cred = $clientApp->getCredentials();
-    if (!$clientApp->verifyAcl('admin', 'docs', $runtime['user'] ?? '')) {
+
+    if (!$clientApp->verifyAcl('Clinicians', 'docs', $runtime['user'] ?? '')) {
+        die("<h3>" . xlt("Not Authorised!") . "</h3>");
+    }
+}
+if ($TYPE === "EMAIL") {
+    $_SESSION['authUser'] = $runtime['user'] ?? $_SESSION['authUser'];
+    $emailApp = AppDispatch::getApiService('email');
+    $cred = $emailApp->getEmailSetup();
+
+    if (!$emailApp->verifyAcl('Clinicians', 'docs', $runtime['user'] ?? '')) {
         die("<h3>" . xlt("Not Authorised!") . "</h3>");
     }
 }
@@ -108,16 +121,17 @@
         <title><?php echo xlt("SMS Notification") ?></title>
     </head>
     <style>
-      html {
-        font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif;
-        font-size: 14px;
-      }
+        html {
+            font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif;
+            font-size: 14px;
+        }
     </style>
     <body>
         <body>
             <div>
                 <div>
-                    <p class="text-center"><h2><?php echo xlt("Working and may take a few minutes to finish.") ?></h2></p>
+                    <p class="text-center">
+                    <h2><?php echo xlt("Working and may take a few minutes to finish.") ?></h2></p>
                 </div>
                 <?php
                 if ($bTestRun) {
@@ -129,7 +143,7 @@
                 flush();
                 // for every event found
                 $plast = '';
-                echo "<h3>========================" . text($TYPE) . " | " . text(date("Y-m-d H:i:s")) . " =========================</h3>";
+                echo "<h3>======================== " . text($TYPE) . " | " . text(date("Y-m-d H:i:s")) . " =========================</h3>";
                 for ($p = 0; $p < count($db_patient); $p++) {
                     ob_flush();
                     flush();
@@ -156,37 +170,80 @@
                     if ($remain_hour >= -($CRON_TIME) && $remain_hour <= $CRON_TIME) {
                         //set message
                         $db_sms_msg['message'] = cron_SetMessage($prow, $db_sms_msg);
-                        $isValid = isValidPhone($prow['phone_cell']);
                         // send sms to patient - if not in test mode
-                        if ($bTestRun == 0 && $isValid) {
+                        if ($bTestRun == 0) {
                             cron_InsertNotificationLogEntry($TYPE, $prow, $db_sms_msg);
-                            $clientApp->sendSMS(
-                                $prow['phone_cell'],
-                                $db_sms_msg['email_subject'],
-                                $db_sms_msg['message'],
-                                $db_sms_msg['email_sender']
-                            );
-                        }
-                        if (!$isValid) {
-                            $strMsg .= "<strong style='color:red'>\n* " . xlt("INVALID Mobile Phone#") . text('$prow["phone_cell"]') . " " . xlt("SMS NOT SENT Patient") . ":</strong>" . text($prow['fname']) . " " . text($prow['lname']) . "</b>";
-                            $db_sms_msg['message'] = xlt("Error: INVALID Mobile Phone") . '# ' . text($prow['phone_cell']) . xlt("SMS NOT SENT For") . ": " . text($prow['fname']) . " " . text($prow['lname']);
-                            if ($bTestRun == 0) {
-                                cron_InsertNotificationLogEntry($TYPE, $prow, $db_sms_msg);
+                            if ($TYPE == 'SMS' && $clientApp != null) {
+                                $isValid = isValidPhone($prow['phone_cell']);
+                                if ($isValid) {
+                                    $error = $clientApp->sendSMS(
+                                        $prow['phone_cell'] ?? '',
+                                        $db_sms_msg['email_subject'] ?? '',
+                                        $db_sms_msg['message'] ?? '',
+                                        $db_sms_msg['email_sender'] ?? ''
+                                    );
+                                    if (stripos($error, 'error') !== false) {
+                                        $strMsg .= " | " . xlt("Error:") . "<strong> " . text($error) . "</strong>";
+                                        echo(nl2br($strMsg));
+                                        exit;
+                                    }
+                                }
+                                if (!$isValid) {
+                                    $strMsg .= "<strong style='color:red'>\n* " . xlt("INVALID Mobile Phone#") . text('$prow["phone_cell"]') . " " . xlt("SMS NOT SENT Patient") . ":</strong>" . text($prow['fname']) . " " . text($prow['lname']) . "</b>";
+                                    $db_sms_msg['message'] = xlt("Error: INVALID Mobile Phone") . '# ' . text($prow['phone_cell']) . xlt("SMS NOT SENT For") . ": " . text($prow['fname']) . " " . text($prow['lname']);
+                                    if ($bTestRun == 0) {
+                                        cron_InsertNotificationLogEntry($TYPE, $prow, $db_sms_msg);
+                                    }
+                                } else {
+                                    $strMsg .= " | " . xlt("SMS SENT SUCCESSFULLY TO") . "<strong> " . text($prow['phone_cell']) . "</strong>";
+                                    cron_UpdateEntry($TYPE, $prow['pid'], $prow['pc_eid'], $prow['pc_recurrtype']);
+                                }
+                                if ((int)$prow['pc_recurrtype'] > 0) {
+                                    $row = fetchRecurrences($prow['pid']);
+                                    $strMsg .= "\n<strong>" . xlt("A Recurring") . " " . text($row[0]['pc_catname']) . " " . xlt("Event Occuring") . " " . text($row[0]['pc_recurrspec']) . "</strong>";
+                                }
+                                $strMsg .= "\n" . text($db_sms_msg['message']) . "\n";
+                                echo(nl2br($strMsg));
+                            }
+                            if ($TYPE == 'EMAIL' && $emailApp != null) {
+                                $isValid = $emailApp->validEmail($prow['email']);
+                                if ($isValid) {
+                                    try {
+                                        $error = $emailApp->emailReminder(
+                                            $prow['email'] ?? '',
+                                            $db_sms_msg['message'],
+                                        );
+                                    } catch (\PHPMailer\PHPMailer\Exception $e) {
+                                        $error = 'Error' . ' ' . $e->getMessage();
+                                    }
+                                    if (stripos($error, 'error') !== false) {
+                                        $strMsg .= " | " . xlt("Error:") . "<strong> " . text($error) . "</strong>";
+                                        echo(nl2br($strMsg));
+                                        exit;
+                                    }
+                                }
+                                if (!$isValid) {
+                                    $strMsg .= "<strong style='color:red'>\n* " . xlt("INVALID Email") . text('$prow["email"]') . " " . xlt("EMAIL NOT SENT Patient") . ":</strong>" . text($prow['fname']) . " " . text($prow['lname']) . "</b>";
+                                    $db_sms_msg['message'] = xlt("Error: INVALID EMAIL") . '# ' . text($prow['email']) . xlt("EMAIL NOT SENT For") . ": " . text($prow['fname']) . " " . text($prow['lname']);
+                                    if ($bTestRun == 0) {
+                                        cron_InsertNotificationLogEntry($TYPE, $prow, $db_sms_msg);
+                                    }
+                                } else {
+                                    $strMsg .= " | " . xlt("EMAILED SUCCESSFULLY TO") . "<strong> " . text($prow['email']) . "</strong>";
+                                    cron_UpdateEntry($TYPE, $prow['pid'], $prow['pc_eid'], $prow['pc_recurrtype']);
+                                }
+                                if ((int)$prow['pc_recurrtype'] > 0) {
+                                    $row = fetchRecurrences($prow['pid']);
+                                    $strMsg .= "\n<strong>" . xlt("A Recurring") . " " . text($row[0]['pc_catname']) . " " . xlt("Event Occuring") . " " . text($row[0]['pc_recurrspec']) . "</strong>";
+                                }
+                                $strMsg .= "\n" . text($db_sms_msg['message']) . "\n";
+                                echo(nl2br($strMsg));
                             }
-                        } else {
-                            $strMsg .= " | " . xlt("SENT SUCCESSFULLY TO") . "<strong> " . text($prow['phone_cell']) . "</strong>";
-                            cron_UpdateEntry($TYPE, $prow['pid'], $prow['pc_eid'], $prow['pc_recurrtype']);
-                        }
-                        if ((int)$prow['pc_recurrtype'] > 0) {
-                            $row = fetchRecurrences($prow['pid']);
-                            $strMsg .= "\n<strong>" . xlt("A Recurring") . " " . text($row[0]['pc_catname']) . " " . xlt("Event Occuring") . " " . text($row[0]['pc_recurrspec']) . "</strong>";
                         }
-                        $strMsg .= "\n" . text($db_sms_msg['message']) . "\n";
-                        echo (nl2br($strMsg));
                     }
                 }
-
                 unset($clientApp);
+                unset($emailApp);
                 echo "<br /><h2>" . xlt("Done!") . "</h2>";
                 ?>
             </div>
@@ -343,7 +400,7 @@ function displayHelp(): void
 {
     //echo text($helpt);
     $help =
-    <<<HELP
+        <<<HELP
 
 Usage:   php rc_sms_notification.php [options]
 Example: php rc_sms_notification.php site=default user=admin type=sms testrun=1
diff --git a/interface/modules/custom_modules/oe-module-faxsms/library/setup_services.php b/interface/modules/custom_modules/oe-module-faxsms/library/setup_services.php
index 5de3658db7e..b5973852d75 100644
--- a/interface/modules/custom_modules/oe-module-faxsms/library/setup_services.php
+++ b/interface/modules/custom_modules/oe-module-faxsms/library/setup_services.php
@@ -32,18 +32,32 @@
 <!DOCTYPE HTML>
 <html lang="eng">
 <head>
-    <title>><?php echo xlt("Enable Vendors") ?></title>
+    <title><?php echo xlt("Enable Vendors") ?></title>
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <?php
     if (count($vendors ?? []) === 0) {
         $boot->createVendorGlobals();
         $vendors = $boot->getVendorGlobals();
     }
-
+    $isRCSMS = $vendors['oefax_enable_sms'] == 1 ? '1' : '0';
+    $isRCFax = $vendors['oefax_enable_fax'] == 1 ? '1' : '0';
+    $isEMAIL = $vendors['oe_enable_email'] == 4 ? '1' : '0';
+    $setupUrl = './../setup.php';
+    if ($isRCFax) {
+        $setupUrl = './../setup_rc.php';
+    }
     Header::setupHeader();
     ?>
     <script>
+        let ServiceFax = <?php echo js_escape($isRCFax) ?>;
+        let ServiceSMS = <?php echo js_escape($isRCSMS); ?>;
+        let ServiceEmail = <?php echo js_escape($isEMAIL); ?>;
+
         function toggleSetup(id, type = 'single') {
+            let url = './../setup.php';
+            if (ServiceFax === '1') {
+                url = './../setup_rc.php';
+            }
             let dialog = $("#dialog").is(':checked');
             if (!dialog || id === 'set-service') {
                 $(".frame").addClass("d-none");
@@ -51,13 +65,17 @@ function toggleSetup(id, type = 'single') {
                 return false;
             }
             if (id === 'set-fax') {
+                let url = './../setup.php';
+                if (ServiceSMS === '1') {
+                    url = './../setup_rc.php';
+                }
                 let title = 'Fax Module Credentials';
                 let params = {
                     buttons: [{text: 'Cancel', close: true, style: 'default btn-sm'}],
                     sizeHeight: 'full',
                     allowDrag: false,
                     type: 'iframe',
-                    url: './../setup.php?type=fax&module_config=-1'
+                    url: url + '?type=fax&module_config=-1'
                 }
                 return dlgopen('', '', 'modal-mlg', '', '', title, params);
             }
@@ -68,7 +86,18 @@ function toggleSetup(id, type = 'single') {
                     sizeHeight: 'full',
                     allowDrag: false,
                     type: 'iframe',
-                    url: './../setup.php?type=sms&module_config=-1'
+                    url: url + '?type=sms&module_config=-1'
+                }
+                return dlgopen('', '', 'modal-lg', '', '', title, params);
+            }
+            if (id === 'set-email') {
+                let title = 'Email Module Credentials';
+                let params = {
+                    buttons: [{text: 'Cancel', close: true, style: 'default btn-sm'}],
+                    sizeHeight: 'full',
+                    allowDrag: false,
+                    type: 'iframe',
+                    url: './../setup_email.php?type=email&module_config=-1'
                 }
                 return dlgopen('', '', 'modal-lg', '', '', title, params);
             }
@@ -85,13 +114,16 @@ function toggleSetup(id, type = 'single') {
     </script>
 </head>
 <body>
-    <div class="w-100">
+    <div class="w-100 container-xl">
         <div class="form-group m-2 p-2 bg-dark">
             <button class="btn btn-outline-light" onclick="toggleSetup('set-service')"><?php echo xlt("Enable Accounts"); ?><i class="fa fa-caret"></i></button>
             <?php if (!empty($vendors['oefax_enable_sms'])) { ?>
-            <button class="btn btn-outline-light" onclick="toggleSetup('set-sms')"><?php echo xlt("Setup SMS Account"); ?><span class="caret"></span></button>
-            <?php } if (!empty($vendors['oefax_enable_fax'])) { ?>
-            <button class="btn btn-outline-light" onclick="toggleSetup('set-fax')"><?php echo xlt("Setup Fax Account"); ?><span class="caret"></span></button>
+                <button class="btn btn-outline-light" onclick="toggleSetup('set-sms')"><?php echo xlt("Setup SMS Account"); ?><span class="caret"></span></button>
+            <?php }
+            if (!empty($vendors['oefax_enable_fax'])) { ?>
+                <button class="btn btn-outline-light" onclick="toggleSetup('set-fax')"><?php echo xlt("Setup Fax Account"); ?><span class="caret"></span></button>
+            <?php } if (!empty($vendors['oe_enable_email'])) { ?>
+                <button class="btn btn-outline-light" onclick="toggleSetup('set-email')"><?php echo xlt("Setup Email Account"); ?><span class="caret"></span></button>
             <?php } ?>
             <span class="checkbox text-light br-dark" title="Use Dialog or Panels">
                 <label for="dialog"><?php echo xlt("Render in dialog."); ?></label>
@@ -112,7 +144,7 @@ function toggleSetup(id, type = 'single') {
                         <div class="col-sm-6" title="Enable SMS Support. Remember to setup credentials.">
                             <select class="form-control persist" name="sms_vendor" id="sms_vendor">
                                 <option value="0" <?php echo $vendors['oefax_enable_sms'] == '0' ? 'selected' : ''; ?>><?php echo xlt("Disabled"); ?></option>
-                                <!-- Placeholder for RC or another service -->
+                                <option value="1" <?php echo $vendors['oefax_enable_sms'] == '1' ? 'selected' : ''; ?>><?php echo xlt("RingCentral SMS"); ?></option>
                                 <option value="2" <?php echo $vendors['oefax_enable_sms'] == '2' ? 'selected' : ''; ?>><?php echo xlt("Twilio SMS"); ?></option>
                             </select>
                         </div>
@@ -122,7 +154,7 @@ function toggleSetup(id, type = 'single') {
                         <div class="col-sm-6" title="Enable Fax Support. Remember to setup credentials.">
                             <select class="form-control persist" name="fax_vendor" id="fax_vendor">
                                 <option value="0" <?php echo $vendors['oefax_enable_fax'] == '0' ? 'selected' : ''; ?>><?php echo xlt("Disabled"); ?></option>
-                                <!-- Placeholder for RC or another service -->
+                                <option value="1" <?php echo $vendors['oefax_enable_fax'] == '1' ? 'selected' : ''; ?>><?php echo xlt("RingCentral Fax"); ?></option>
                                 <option value="3" <?php echo $vendors['oefax_enable_fax'] == '3' ? 'selected' : ''; ?>><?php echo xlt("etherFAX"); ?></option>
                             </select>
                         </div>
@@ -132,7 +164,7 @@ function toggleSetup(id, type = 'single') {
                         <div class="col-sm-6" title="Enable Email Client Support.">
                             <select class="form-control persist" name="email_vendor" id="email_vendor">
                                 <option value="0" <?php echo $vendors['oe_enable_email'] == '0' ? 'selected' : ''; ?>><?php echo xlt("Disabled"); ?></option>
-                                <option value="1" <?php echo $vendors['oe_enable_email'] == '1' ? 'selected' : ''; ?>><?php echo xlt("Enabled"); ?></option>
+                                <option value="4" <?php echo $vendors['oe_enable_email'] == '4' ? 'selected' : ''; ?>><?php echo xlt("Enabled"); ?></option>
                             </select>
                         </div>
                     </div>
@@ -156,15 +188,32 @@ function toggleSetup(id, type = 'single') {
         </div>
         <!-- iframes to hold setup account scripts. Dialogs replace these if requested in UI -->
         <?php if (!empty($vendors['oefax_enable_fax'])) { ?>
-        <div id="set-fax" class="frame d-none">
-            <h3 class="text-center"><?php echo xlt("Setup Fax Account"); ?></h3>
-            <iframe src="./../setup.php?type=fax&module_config=1&mode=flat" style="border:none;height:100vh;width:100%;"></iframe>
-        </div>
-        <?php } if (!empty($vendors['oefax_enable_sms'])) { ?>
-        <div id="set-sms" class="frame d-none">
-            <h3 class="text-center"><?php echo xlt("Setup SMS Account"); ?></h3>
-            <iframe src="./../setup.php?type=sms&module_config=1&mode=flat" style="border:none;height:100vh;width:100%;"></iframe>
-        </div>
+            <div id="set-fax" class="frame d-none">
+                <h3 class="text-center"><?php echo xlt("Setup Fax Account"); ?></h3>
+                <iframe src="<?php
+                $setupUrl = './../setup.php';
+                if ($isRCFax) {
+                    $setupUrl = './../setup_rc.php';
+                }
+                echo attr($setupUrl . '?type=fax&module_config=1&mode=flat'); ?>" style="border:none;height:100vh;width:100%;"></iframe>
+            </div>
+        <?php }
+        if (!empty($vendors['oefax_enable_sms'])) { ?>
+            <div id="set-sms" class="frame d-none">
+                <h3 class="text-center"><?php echo xlt("Setup SMS Account"); ?></h3>
+                <iframe src="<?php
+                $setupUrl = './../setup.php';
+                if ($isRCSMS) {
+                    $setupUrl = './../setup_rc.php';
+                }
+                echo attr($setupUrl . '?type=sms&module_config=1&mode=flat'); ?>" style="border:none;height:100vh;width:100%;"></iframe>
+            </div>
+        <?php } ?>
+        <?php if (!empty($vendors['oe_enable_email'])) { ?>
+            <div id="set-email" class="frame d-none">
+                <h3 class="text-center"><?php echo xlt("Setup Email Account"); ?></h3>
+                <iframe src="<?php echo attr('./../setup_email.php?type=email&module_config=1&mode=flat'); ?>" style="border:none;height:100vh;width:100%;"></iframe>
+            </div>
         <?php } ?>
     </div>
 </body>
diff --git a/interface/modules/custom_modules/oe-module-faxsms/messageUI.php b/interface/modules/custom_modules/oe-module-faxsms/messageUI.php
index c26ea773b27..ba0fd5b6c9c 100644
--- a/interface/modules/custom_modules/oe-module-faxsms/messageUI.php
+++ b/interface/modules/custom_modules/oe-module-faxsms/messageUI.php
@@ -19,7 +19,8 @@
 $serviceType = $_REQUEST['type'] ?? '';
 $clientApp = AppDispatch::getApiService($serviceType);
 $service = $clientApp::getServiceType();
-$title = $service == "2" ? xlt('Twilio SMS') : '';
+$title = $service == "1" ? xlt('RingCentral') : '';
+$title = $service == "2" ? xlt('Twilio SMS') : $title;
 $title = $service == "3" ? xlt('etherFAX') : $title;
 $tabTitle = $serviceType == "sms" ? xlt('SMS') : xlt('FAX');
 ?>
@@ -38,6 +39,7 @@
         ";let currentService=" . js_escape($service) . ";let serviceType=" . js_escape($serviceType) . "</script>";
     ?>
     <script type="text/javascript" src="<?php echo $GLOBALS['assets_static_relative']; ?>/dropzone/dist/dropzone.js"></script>
+
     <script>
         $(function () {
             $('.datepicker').datetimepicker({
@@ -53,13 +55,22 @@
             $("#todate").val(new Date().toJSON().slice(0, 10));
 
             $(".other").hide();
-            if (currentService == '2') {
+            if (currentService == '1' && serviceType == 'fax') {
+                $(".rc-hide").hide();
+                $(".rc-fax-hide").hide();
+            } else if (currentService == '1' && serviceType == 'sms') {
+                $(".rc-hide").hide();
+            } else if (currentService == '2') {
                 $(".etherfax").hide();
             } else if (currentService == '3') {
                 $(".twilio").hide();
                 $(".etherfax-hide").hide();
                 $(".etherfax").show();
             }
+            if (serviceType == 'sms') {
+                $(".sms-hide").hide();
+            }
+
             retrieveMsgs();
             $('#received').tab('show');
         });
@@ -73,7 +84,11 @@
             dlgopen(url, '', 'modal-sm', 700, '', title, { // dialog restores session
                 buttons: [
                     {text: btnClose, close: true, style: 'secondary btn-sm'}
-                ]
+                ],
+                resolvePromiseOn: 'close',
+                sizeHeight: 'full'
+            }).then(function (contact) {
+                top.restoreSession();
             });
         };
 
@@ -111,12 +126,16 @@
                 console.log('Session restore failed!');
             }
             e.preventDefault();
+            let url = top.webroot_url + '/interface/modules/custom_modules/oe-module-faxsms/setup.php';
+            if (currentService === '1') {
+                url = top.webroot_url + '/interface/modules/custom_modules/oe-module-faxsms/setup_rc.php';
+            }
             let msg = <?php echo xlj('Credentials and Notifications') ?>;
             dlgopen('', 'setup', 'modal-md', 700, '', msg, {
                 buttons: [
                     {text: 'Cancel', close: true, style: 'secondary  btn-sm'}
                 ],
-                url: "./setup.php?type=" + encodeURIComponent(serviceType)
+                url: url + "?type=" + encodeURIComponent(serviceType)
             });
         };
 
@@ -168,7 +187,7 @@ function showPrint(base64, _contentType = 'image/tiff') {
         }
 
         // Function to get or dispose of document.
-        async function getDocument(e, docuri, docid, downFlag, deleteFlag = '') {
+        async function getDocument(e, docuri, docid, downFlag, deleteFlag = '', massDelete = false) {
             try {
                 top.restoreSession();
             } catch (error) {
@@ -189,7 +208,7 @@ function showPrint(base64, _contentType = 'image/tiff') {
                     return false;
                 }
             }
-            if (deleteFlag == 'true') {
+            if (deleteFlag == 'true' && !massDelete) {
                 let yn = confirm(
                     xl("Are you sure you want to continue with delete?")
                 );
@@ -216,7 +235,15 @@ function showPrint(base64, _contentType = 'image/tiff') {
                 } catch {
                     data = json;
                 }
+                if (data.error) {
+                    alertMsg(data.error);
+                    return false;
+                }
+
                 if (deleteFlag == 'true') {
+                    if (massDelete) {
+                        return false;
+                    }
                     setTimeout(retrieveMsgs, 1000);
                     return false;
                 }
@@ -238,17 +265,14 @@ function showPrint(base64, _contentType = 'image/tiff') {
                             file_path: data.path,
                             content: base64
                         })
-                    })
-                    .then(response => response.json())
-                    .then(result => {
+                    }).then(response => response.json()).then(result => {
                         // Download the file. result.url is temp file path of tiff to pdf by image conversion in JS or imagick.
                         if (result.success) {
                             location.href = "disposeDocument?type=fax&action=download&file_path=" + encodeURIComponent(result.url);
                         } else {
                             console.error('Failed to prepare the file for download:', jsText(result.message));
                         }
-                    })
-                    .catch(error => {
+                    }).catch(error => {
                         console.error('Error:', error);
                     });
 
@@ -284,7 +308,6 @@ function showPrint(base64, _contentType = 'image/tiff') {
                         resolve(imageData);
                     }));
                 }
-
                 return Promise.all(promises);
             } catch (error) {
                 console.error('Failed to convert TIFF to images:', error);
@@ -329,7 +352,7 @@ function showDocument(_base64, _contentType = 'image/tiff') {
                     view[i] = binary.charCodeAt(i);
                 }
 
-                const blob = new Blob([view], { type: _contentType });
+                const blob = new Blob([view], {type: _contentType});
                 const dataUrl = URL.createObjectURL(blob);
                 displayInNewWindow(dataUrl);
             } catch (e) {
@@ -365,7 +388,13 @@ function retrieveMsgs(e = '', req = '') {
                 e.stopPropagation();
             }
 
-            const actionUrl = (serviceType === 'fax') ? 'fax/getPending?type=fax' : 'fetchSMSList?type=sms';
+            let actionUrl = (serviceType === 'fax') ? 'getPending?type=fax' : '';
+            if (serviceType === 'sms' && currentService == '1') { //RC
+                actionUrl = 'getPending?type=sms';
+            } else if (serviceType === 'sms') { //Twilio
+                actionUrl = 'fetchSMSList?type=sms';
+            }
+
             const datefrom = $('#fromdate').val();
             const dateto = $('#todate').val();
 
@@ -393,7 +422,6 @@ function retrieveMsgs(e = '', req = '') {
                     alertMsg(data.error);
                     return false;
                 }
-
                 // Populate our cards
                 rcvDetailsBody.append(data[0]);
                 sentDetailsBody.append(data[1]);
@@ -533,7 +561,6 @@ function createPatient(e, faxId, recordId, data) {
             );
             return false;
         }
-
         // drop bucket
         const queueMsg = '' + <?php echo xlj('Fax Queue. Drop files or Click here for Fax Contact form.') ?>;
         Dropzone.autoDiscover = false;
@@ -576,6 +603,32 @@ function createPatient(e, faxId, recordId, data) {
                 }
             });
         });
+
+        document.addEventListener('DOMContentLoaded', function () {
+            const deleteSelectedFaxesButton = document.querySelectorAll('.delete-selected-items');
+            deleteSelectedFaxesButton.forEach(button => {
+                button.addEventListener('click', function () {
+                    const selectedFaxes = document.querySelectorAll('.delete-fax-checkbox:checked');
+                    const faxIds = Array.from(selectedFaxes).map(checkbox => checkbox.value);
+
+                    if (faxIds.length === 0) {
+                        alert('No faxes selected for deletion.');
+                        return;
+                    }
+
+                    if (!confirm(`Are you sure you want to delete ${faxIds.length} faxes?`)) {
+                        return;
+                    }
+
+                    faxIds.forEach(id => {
+                        getDocument('', null, id, 'false', 'true', true);
+                    });
+
+                    setTimeout(retrieveMsgs, 1000);
+                    return false;
+                });
+            });
+        });
     </script>
 </head>
 <body class="body_top">
@@ -626,35 +679,134 @@ function createPatient(e, faxId, recordId, data) {
                     <!-- Nav tabs -->
                     <ul id="tab-menu" class="nav nav-pills" role="tablist">
                         <li class="nav-item" role="tab">
-                            <a class="nav-link active" href="#received" aria-controls="received" role="tab" data-toggle="tab"><?php echo xlt("Received") ?><span class="fa fa-redo ml-1" onclick="retrieveMsgs('', this)"
-                                    title="<?php echo xla('Click to refresh using current date range. Refreshing just this tab.') ?>"></span></a>
+                            <a class="nav-link active" href="#received" aria-controls="received" role="tab" data-toggle="tab"><?php echo xlt("Received") ?>
+                                <span class="fa fa-redo ml-1" onclick="retrieveMsgs('', this)"
+                                    title="<?php echo xla('Click to refresh using current date range. Refreshing just this tab.') ?>">
+                                </span>
+                            </a>
                         </li>
-                        <li class="nav-item etherfax-hide" role="tab"><a class="nav-link" href="#sent" aria-controls="sent" role="tab" data-toggle="tab"><?php echo xlt("Sent") ?></a></li>
-                        <li class="nav-item etherfax-hide" role="tab"><a class="nav-link" href="#messages" aria-controls="messages" role="tab" data-toggle="tab"><?php echo xlt("SMS Log") ?></a></li>
+                        <li class="nav-item" role="tab"><a class="nav-link" href="#sent" aria-controls="sent" role="tab" data-toggle="tab"><?php echo xlt("Sent") ?></a></li>
+                        <li class="nav-item rc-fax-hide etherfax-hide" role="tab"><a class="nav-link" href="#messages" aria-controls="messages" role="tab" data-toggle="tab"><?php echo xlt("SMS Log") ?></a></li>
                         <li class="nav-item" role="tab"><a class="nav-link" href="#logs" aria-controls="logs" role="tab" data-toggle="tab"><?php echo xlt("Call Log") ?></a></li>
-                        <li class="nav-item etherfax-hide" role="tab">
+                        <li class="nav-item etherfax-hide rc-fax-hide" role="tab">
                             <a class="nav-link" href="#alertlogs" aria-controls="alertlogs" role="tab" data-toggle="tab"><?php echo xlt("Reminder Notifications Log") ?><span class="fa fa-redo ml-1" onclick="getNotificationLog(event, this)"
                                     title="<?php echo xla('Click to refresh using current date range. Refreshing just this tab.') ?>"></span></a>
                         </li>
-                        <li class="nav-item etherfax" role="tab"><a class="nav-link" href="#upLoad" aria-controls="logs" role="tab" data-toggle="tab"><?php echo xlt("Fax Drop Box") ?></a></li>
+                        <li class="nav-item sms-hide etherfax" role="tab"><a class="nav-link" href="#upLoad" aria-controls="logs" role="tab" data-toggle="tab"><?php echo xlt("Fax Drop Box") ?></a></li>
                     </ul>
                     <!-- Tab panes -->
-                    <div class="tab-content">
-                        <div role="tabpanel" class="container-fluid tab-pane fade" id="received">
-                            <?php if ($service == '3') { ?>
+                    <?php if ($service != '1') { ?>
+                        <div class="tab-content">
+                            <div role="tabpanel" class="container-fluid tab-pane fade" id="received">
+                                <?php if ($service == '3') { ?>
+                                    <div class="table-responsive">
+                                        <table class="table table-sm" id="rcvdetails">
+                                            <thead>
+                                            <tr>
+                                                <th><?php echo xlt("Time") ?></th>
+                                                <th><?php echo xlt("Caller #") ?></th>
+                                                <th><?php echo xlt("Caller Id") ?></th>
+                                                <th><?php echo xlt("To") ?></th>
+                                                <th><?php echo xlt("Pages") ?></th>
+                                                <th><?php echo xlt("Length") ?></th>
+                                                <th><?php echo xlt("Status") ?></th>
+                                                <th><a role='button' href='javascript:void(0)' class='btn btn-link fa fa-eye' onclick="toggleDetail('collapse')"></a><?php echo xlt("Extracted") ?></th>
+                                                <th><?php echo xlt("MRN Match") ?></th>
+                                                <th><?php echo xlt("Actions") ?></th>
+                                                <th><i role="button" id="delete-selected-received" title="<?php echo xla("Delete selected fax documents") ?>" class="delete-selected-items text-danger fa fa-trash"></i></th>
+                                            </tr>
+                                            </thead>
+                                            <tbody>
+                                            <tr>
+                                                <td><?php echo xlt("No Items Try Refresh") ?></td>
+                                            </tr>
+                                            </tbody>
+                                        </table>
+                                    </div>
+                                <?php } else { // all others ?>
+                                    <div class="table-responsive">
+                                        <table class="table table-sm" id="rcvdetails">
+                                            <thead>
+                                            <tr>
+                                                <th><?php echo xlt("Time") ?></th>
+                                                <th><?php echo xlt("Type") ?></th>
+                                                <th class=""><?php echo xlt("Message") ?></th>
+                                                <th><?php echo xlt("From") ?></th>
+                                                <th><?php echo xlt("To") ?></th>
+                                                <th><?php echo xlt("Result") ?></th>
+                                                <th><?php echo xlt("Reply") ?></th>
+                                            </tr>
+                                            </thead>
+                                            <tbody>
+                                            <tr>
+                                                <td><?php echo xlt("No Items Try Refresh") ?></td>
+                                            </tr>
+                                            </tbody>
+                                        </table>
+                                    </div>
+                                <?php } ?>
+                            </div>
+                            <div role="tabpanel" class="container-fluid tab-pane fade" id="sent">
+                                <?php if ($service == '3') { ?>
+                                    <div class="table-responsive">
+                                        <table class="table table-sm" id="sent-details">
+                                            <thead>
+                                            <tr>
+                                                <th><?php echo xlt("Time") ?></th>
+                                                <th><?php echo xlt("Caller #") ?></th>
+                                                <th><?php echo xlt("Caller Id") ?></th>
+                                                <th><?php echo xlt("To") ?></th>
+                                                <th><?php echo xlt("Pages") ?></th>
+                                                <th><?php echo xlt("Length") ?></th>
+                                                <th><?php echo xlt("Status") ?></th>
+                                                <th><?php echo xlt("Actions") ?></th>
+                                                <th><i role="button" id="delete-selected-sent" title="<?php echo xlt("Delete selected fax documents") ?>" class="delete-selected-items text-danger fa fa-trash"></i></th>
+                                            </tr>
+                                            </thead>
+                                            <tbody>
+                                            <tr>
+                                                <td><?php echo xlt("No Items Try Refresh") ?></td>
+                                            </tr>
+                                            </tbody>
+                                        </table>
+                                    </div>
+                                <?php } else { ?>
+                                    <div class="table-responsive">
+                                        <table class="table table-sm table-striped" id="sent-details">
+                                            <thead>
+                                            <tr>
+                                                <th><?php echo xlt("Start Time") ?></th>
+                                                <th class="twilio"><?php echo xlt("Price") ?></th>
+                                                <th class="etherfax"><?php echo xlt("Type") ?></th>
+                                                <th><?php echo xlt("Message") ?></th>
+                                                <th><?php echo xlt("From") ?></th>
+                                                <th><?php echo xlt("To") ?></th>
+                                                <th><?php echo xlt("Result") ?></th>
+                                                <th class="etherfax"><?php echo xlt("Download") ?></th>
+                                                <th class="twilio"><?php echo xlt("Reply") ?></th>
+                                            </tr>
+                                            </thead>
+                                            <tbody>
+                                            <tr>
+                                                <td><?php echo xlt("No Items Try Refresh") ?></td>
+                                            </tr>
+                                            </tbody>
+                                        </table>
+                                    </div>
+                                <?php } ?>
+                            </div>
+                            <div role="tabpanel" class="container-fluid tab-pane fade" id="messages">
                                 <div class="table-responsive">
-                                    <table class="table table-sm" id="rcvdetails">
+                                    <table class="table table-sm table-striped" id="msgdetails">
                                         <thead>
                                         <tr>
-                                            <th><?php echo xlt("Time") ?></th>
-                                            <th><?php echo xlt("Caller #") ?></th>
-                                            <th><?php echo xlt("Caller Id") ?></th>
+                                            <th><?php echo xlt("Date") ?></th>
+                                            <th><?php echo xlt("Type") ?></th>
+                                            <th><?php echo xlt("From") ?></th>
                                             <th><?php echo xlt("To") ?></th>
-                                            <th><?php echo xlt("Pages") ?></th>
-                                            <th><?php echo xlt("Length") ?></th>
-                                            <th><a role='button' href='javascript:void(0)' class='btn btn-link fa fa-eye' onclick="toggleDetail('collapse')"></a><?php echo xlt("Extracted") ?></th>
-                                            <th><?php echo xlt("MRN Match") ?></th>
-                                            <th><?php echo xlt("Actions") ?></th>
+                                            <th><?php echo xlt("Result") ?></th>
+                                            <th class="other"><?php echo xlt("Download") ?></th>
+                                            <th><?php echo xlt("View") ?></th>
                                         </tr>
                                         </thead>
                                         <tbody>
@@ -664,18 +816,19 @@ function createPatient(e, faxId, recordId, data) {
                                         </tbody>
                                     </table>
                                 </div>
-                            <?php } else { ?>
+                            </div>
+                            <div role="tabpanel" class="container-fluid tab-pane fade" id="logs">
                                 <div class="table-responsive">
-                                    <table class="table table-sm" id="rcvdetails">
+                                    <table class="table table-sm table-striped" id="logdetails">
                                         <thead>
                                         <tr>
-                                            <th><?php echo xlt("Time") ?></th>
+                                            <th><?php echo xlt("Date") ?></th>
                                             <th><?php echo xlt("Type") ?></th>
-                                            <th class=""><?php echo xlt("Message") ?></th>
                                             <th><?php echo xlt("From") ?></th>
                                             <th><?php echo xlt("To") ?></th>
+                                            <th><?php echo xlt("Action") ?></th>
                                             <th><?php echo xlt("Result") ?></th>
-                                            <th><?php echo xlt("Reply") ?></th>
+                                            <th><?php echo xlt("Id") ?></th>
                                         </tr>
                                         </thead>
                                         <tbody>
@@ -685,106 +838,162 @@ function createPatient(e, faxId, recordId, data) {
                                         </tbody>
                                     </table>
                                 </div>
-                            <?php } ?>
-                        </div>
-                        <div role="tabpanel" class="container-fluid tab-pane fade" id="sent">
-                            <div class="table-responsive">
-                                <table class="table table-sm table-striped" id="sent-details">
-                                    <thead>
-                                    <tr>
-                                        <th><?php echo xlt("Start Time") ?></th>
-                                        <th class="twilio"><?php echo xlt("Price") ?></th>
-                                        <th class="etherfax"><?php echo xlt("Type") ?></th>
-                                        <th><?php echo xlt("Message") ?></th>
-                                        <th><?php echo xlt("From") ?></th>
-                                        <th><?php echo xlt("To") ?></th>
-                                        <th><?php echo xlt("Result") ?></th>
-                                        <th class="etherfax"><?php echo xlt("Download") ?></th>
-                                        <th class="twilio"><?php echo xlt("Reply") ?></th>
-                                    </tr>
-                                    </thead>
-                                    <tbody>
-                                    <tr>
-                                        <td><?php echo xlt("No Items Try Refresh") ?></td>
-                                    </tr>
-                                    </tbody>
-                                </table>
                             </div>
-                        </div>
-                        <div role="tabpanel" class="container-fluid tab-pane fade" id="messages">
-                            <div class="table-responsive">
-                                <table class="table table-sm table-striped" id="msgdetails">
-                                    <thead>
-                                    <tr>
-                                        <th><?php echo xlt("Date") ?></th>
-                                        <th><?php echo xlt("Type") ?></th>
-                                        <th><?php echo xlt("From") ?></th>
-                                        <th><?php echo xlt("To") ?></th>
-                                        <th><?php echo xlt("Result") ?></th>
-                                        <th class="other"><?php echo xlt("Download") ?></th>
-                                        <th><?php echo xlt("View") ?></th>
-                                    </tr>
-                                    </thead>
-                                    <tbody>
-                                    <tr>
-                                        <td><?php echo xlt("No Items Try Refresh") ?></td>
-                                    </tr>
-                                    </tbody>
-                                </table>
+                            <div role="tabpanel" class="container-fluid tab-pane fade etherfax-hide" id="alertlogs">
+                                <div class="table-responsive">
+                                    <table class="table table-sm table-striped" id="alertdetails">
+                                        <thead>
+                                        <tr>
+                                            <th><?php echo xlt("Id") ?></th>
+                                            <th><?php echo xlt("Date Sent") ?></th>
+                                            <th><?php echo xlt("Appt Date Time") ?></th>
+                                            <th><?php echo xlt("Patient") ?></th>
+                                            <th><?php echo xlt("Message") ?></th>
+                                        </tr>
+                                        </thead>
+                                        <tbody>
+                                        <tr>
+                                            <td><?php echo xlt("No Items") ?></td>
+                                        </tr>
+                                        </tbody>
+                                    </table>
+                                </div>
                             </div>
-                        </div>
-                        <div role="tabpanel" class="container-fluid tab-pane fade" id="logs">
-                            <div class="table-responsive">
-                                <table class="table table-sm table-striped" id="logdetails">
-                                    <thead>
-                                    <tr>
-                                        <th><?php echo xlt("Date") ?></th>
-                                        <th><?php echo xlt("Type") ?></th>
-                                        <th><?php echo xlt("From") ?></th>
-                                        <th><?php echo xlt("To") ?></th>
-                                        <th><?php echo xlt("Action") ?></th>
-                                        <th><?php echo xlt("Result") ?></th>
-                                        <th><?php echo xlt("Id") ?></th>
-                                    </tr>
-                                    </thead>
-                                    <tbody>
-                                    <tr>
-                                        <td><?php echo xlt("No Items Try Refresh") ?></td>
-                                    </tr>
-                                    </tbody>
-                                </table>
+                            <div role="tabpanel" class="container-fluid tab-pane fade" id="upLoad">
+                                <div class="panel container-fluid">
+                                    <div id="fax-queue-container">
+                                        <div id="fax-queue">
+                                            <form id="faxQueue" method="post" enctype="multipart/form-data" class="dropzone"></form>
+                                        </div>
+                                    </div>
+                                </div>
                             </div>
                         </div>
-                        <div role="tabpanel" class="container-fluid tab-pane fade" id="alertlogs">
-                            <div class="table-responsive">
-                                <table class="table table-sm table-striped" id="alertdetails">
-                                    <thead>
-                                    <tr>
-                                        <th><?php echo xlt("Id") ?></th>
-                                        <th><?php echo xlt("Date Sent") ?></th>
-                                        <th><?php echo xlt("Appt Date Time") ?></th>
-                                        <th><?php echo xlt("Patient") ?></th>
-                                        <th><?php echo xlt("Message") ?></th>
-                                    </tr>
-                                    </thead>
-                                    <tbody>
-                                    <tr>
-                                        <td><?php echo xlt("No Items") ?></td>
-                                    </tr>
-                                    </tbody>
-                                </table>
+                    <?php }
+                    if ($service == '1') { ?>
+                        <div class="tab-content">
+                            <div role="tabpanel" class="container-fluid tab-pane fade" id="received">
+                                <div class="table-responsive">
+                                    <table class="table table-sm table-striped" id="rcvdetails">
+                                        <thead>
+                                        <tr>
+                                            <th><?php echo xlt("Start Time") ?></th>
+                                            <th><?php echo xlt("End Time") ?></th>
+                                            <th><?php echo xlt("Pages") ?></th>
+                                            <th><?php echo xlt("From") ?></th>
+                                            <th><?php echo xlt("To") ?></th>
+                                            <th><?php echo xlt("Status") ?></th>
+                                            <th><?php echo xlt("Actions") ?></th>
+                                            <th><i role="button" id="delete-selected-received" title="<?php echo xla("Delete selected fax documents") ?>" class="delete-selected-items text-danger fa fa-trash"></i></th>
+                                        </tr>
+                                        </thead>
+                                        <tbody>
+                                        <tr>
+                                            <td><?php echo xlt("No Items Try Refresh") ?></td>
+                                        </tr>
+                                        </tbody>
+                                    </table>
+                                </div>
                             </div>
-                        </div>
-                        <div role="tabpanel" class="container-fluid tab-pane fade" id="upLoad">
-                            <div class="panel container-fluid">
-                                <div id="fax-queue-container">
-                                    <div id="fax-queue">
-                                        <form id="faxQueue" method="post" enctype="multipart/form-data" class="dropzone"></form>
+                            <div role="tabpanel" class="container-fluid tab-pane fade" id="sent">
+                                <div class="table-responsive">
+                                    <table class="table table-sm table-striped" id="sent-details">
+                                        <thead>
+                                        <tr>
+                                            <th><?php echo xlt("Start Time") ?></th>
+                                            <th><?php echo xlt("End Time") ?></th>
+                                            <th><?php echo xlt("Pages") ?></th>
+                                            <th><?php echo xlt("From") ?></th>
+                                            <th><?php echo xlt("To") ?></th>
+                                            <th><?php echo xlt("Status") ?></th>
+                                            <th><?php echo xlt("Actions") ?></th>
+                                            <th><i role="button" id="delete-selected-received" title="<?php echo xla("Delete selected fax documents") ?>" class="delete-selected-items text-danger fa fa-trash"></i></th>
+                                        </tr>
+                                        </thead>
+                                        <tbody>
+                                        <tr>
+                                            <td><?php echo xlt("No Items Try Refresh") ?></td>
+                                        </tr>
+                                        </tbody>
+                                    </table>
+                                </div>
+                            </div>
+                            <div role="tabpanel" class="container-fluid tab-pane fade d-none" id="messages">
+                                <div class="table-responsive">
+                                    <table class="table table-sm table-striped" id="msgdetails">
+                                        <thead>
+                                        <tr>
+                                            <th><?php echo xlt("Date") ?></th>
+                                            <th><?php echo xlt("Type") ?></th>
+                                            <th><?php echo xlt("From") ?></th>
+                                            <th><?php echo xlt("To") ?></th>
+                                            <th><?php echo xlt("Result") ?></th>
+                                            <th class="twilio"><?php echo xlt("Download") ?></th>
+                                            <th class="ringcentral"><?php echo xlt("Message") ?></th>
+                                            <th><?php echo xlt("View") ?></th>
+                                        </tr>
+                                        </thead>
+                                        <tbody>
+                                        <tr>
+                                            <td><?php echo xlt("No Items Try Refresh") ?></td>
+                                        </tr>
+                                        </tbody>
+                                    </table>
+                                </div>
+                            </div>
+                            <div role="tabpanel" class="container-fluid tab-pane fade" id="logs">
+                                <div class="table-responsive">
+                                    <table class="table table-sm table-striped" id="logdetails">
+                                        <thead>
+                                        <tr>
+                                            <th><?php echo xlt("Date") ?></th>
+                                            <th><?php echo xlt("Type") ?></th>
+                                            <th><?php echo xlt("From") ?></th>
+                                            <th><?php echo xlt("To") ?></th>
+                                            <th><?php echo xlt("Action") ?></th>
+                                            <th><?php echo xlt("Result") ?></th>
+                                            <th><?php echo xlt("Id") ?></th>
+                                        </tr>
+                                        </thead>
+                                        <tbody>
+                                        <tr>
+                                            <td><?php echo xlt("No Items Try Refresh") ?></td>
+                                        </tr>
+                                        </tbody>
+                                    </table>
+                                </div>
+                            </div>
+                            <div role="tabpanel" class="container-fluid tab-pane fade rc-fax-hide" id="alertlogs">
+                                <div class="table-responsive">
+                                    <table class="table table-sm table-striped" id="alertdetails">
+                                        <thead>
+                                        <tr>
+                                            <th><?php echo xlt("Id") ?></th>
+                                            <th><?php echo xlt("Date Sent") ?></th>
+                                            <th><?php echo xlt("Appt Date Time") ?></th>
+                                            <th><?php echo xlt("Patient") ?></th>
+                                            <th><?php echo xlt("Message") ?></th>
+                                        </tr>
+                                        </thead>
+                                        <tbody>
+                                        <tr>
+                                            <td><?php echo xlt("No Items") ?></td>
+                                        </tr>
+                                        </tbody>
+                                    </table>
+                                </div>
+                            </div>
+                            <div role="tabpanel" class="container-fluid tab-pane sms-hide fade in active" id="upLoad">
+                                <div class="panel container-fluid">
+                                    <div id="fax-queue-container">
+                                        <div id="fax-queue">
+                                            <form id="faxQueue" method="post" enctype="multipart/form-data" class="dropzone"></form>
+                                        </div>
                                     </div>
                                 </div>
                             </div>
                         </div>
-                    </div>
+                    <?php } ?>
                 </div>
             </div>
         </div>
diff --git a/interface/modules/custom_modules/oe-module-faxsms/moduleConfig.php b/interface/modules/custom_modules/oe-module-faxsms/moduleConfig.php
index d802842eb3a..4bfb0eed2eb 100644
--- a/interface/modules/custom_modules/oe-module-faxsms/moduleConfig.php
+++ b/interface/modules/custom_modules/oe-module-faxsms/moduleConfig.php
@@ -16,6 +16,5 @@
 ?>
 
 <div id="set-services">
-    <h3 class="text-center"><?php echo xlt("Select Services"); ?></h3>
     <iframe src="library/setup_services.php?module_config=1" style="border:none;height:100vh;width:100%;"></iframe>
 </div>
diff --git a/interface/modules/custom_modules/oe-module-faxsms/openemr.bootstrap.php b/interface/modules/custom_modules/oe-module-faxsms/openemr.bootstrap.php
index bb0b2632ace..3be44f6c164 100644
--- a/interface/modules/custom_modules/oe-module-faxsms/openemr.bootstrap.php
+++ b/interface/modules/custom_modules/oe-module-faxsms/openemr.bootstrap.php
@@ -65,7 +65,7 @@ function oe_module_faxsms_add_menu_item(MenuEvent $event): MenuEvent
     $menuItem->requirement = 0;
     $menuItem->target = 'sms';
     $menuItem->menu_id = 'mod0';
-    $menuItem->label = $allowSMS == '2' ? xlt("Manage Twilio Messaging") : xlt("Manage Messaging");
+    $menuItem->label = $allowSMS == '2' ? xlt("Manage Twilio Messaging") : xlt("RingCentral SMS");
     $menuItem->url = "/interface/modules/custom_modules/oe-module-faxsms/messageUI.php?type=sms";
     $menuItem->children = [];
     $menuItem->acl_req = ["patients", "docs"];
@@ -75,15 +75,29 @@ function oe_module_faxsms_add_menu_item(MenuEvent $event): MenuEvent
     $menuItem2->requirement = 0;
     $menuItem2->target = 'fax';
     $menuItem2->menu_id = 'mod1';
-    $menuItem2->label = $allowFax == '3' ? xlt("Manage etherFAX") : xlt("Manage FAX");
+    $menuItem2->label = $allowFax == '3' ? xlt("Manage etherFAX") : xlt("RingCentral FAX");
     $menuItem2->url = "/interface/modules/custom_modules/oe-module-faxsms/messageUI.php?type=fax";
     $menuItem2->children = [];
     $menuItem2->acl_req = ["patients", "docs"];
     $menuItem2->global_req = ["oefax_enable_fax"];
+
+    $menuItemSetup = new stdClass();
+    $menuItemSetup->requirement = 0;
+    $menuItemSetup->target = 'setup';
+    $menuItemSetup->menu_id = 'mod2';
+    $menuItemSetup->label = xlt("Setup Module Services");
+    $menuItemSetup->url = "/interface/modules/custom_modules/oe-module-faxsms/library/setup_services.php?module_config=1";
+    $menuItemSetup->children = [];
+    $menuItemSetup->acl_req = ["patients", "docs"];
+    $menuItemSetup->global_req = ["oefax_enable_fax"];
+
     // Child of Manage Modules top menu.
     foreach ($menu as $item) {
         if ($item->menu_id == 'modimg') {
             // ensure service is on in globals.
+            if (!empty($allowSMS) || !empty($allowFax)) {
+                $item->children[] = $menuItemSetup;
+            }
             if (!empty($allowFax)) {
                 $item->children[] = $menuItem2;
             }
@@ -155,10 +169,15 @@ function doFax(e, filePath, mime='') {
     e.preventDefault();
     let btnClose = <?php echo xlj("Cancel"); ?>;
     let title = <?php echo xlj("Send To Contact"); ?>;
-    let url = top.webroot_url +
-    '/interface/modules/custom_modules/oe-module-faxsms/contact.php?isDocuments=1&type=fax&file=' +
+    let url = top.webroot_url + '/interface/modules/custom_modules/oe-module-faxsms/contact.php?isDocuments=1&type=fax&file=' +
     encodeURIComponent(filePath) + '&mime=' + encodeURIComponent(mime) + '&docid=' + encodeURIComponent(docid);
-    dlgopen(url, 'faxto', 'modal-md', 700, '', title, {buttons: [{text: btnClose, close: true, style: 'primary'}]});
+    dlgopen(url, 'faxto', 'modal-md', 'full', '', title, {
+    buttons: [],
+    sizeHeight: 'full',
+    resolvePromiseOn: 'close'
+    }).then(function () {
+        top.restoreSession();
+    });
     return false;
 }
 <?php }
diff --git a/interface/modules/custom_modules/oe-module-faxsms/setup.php b/interface/modules/custom_modules/oe-module-faxsms/setup.php
index 316b397c81f..6dd9d86bef9 100644
--- a/interface/modules/custom_modules/oe-module-faxsms/setup.php
+++ b/interface/modules/custom_modules/oe-module-faxsms/setup.php
@@ -172,6 +172,53 @@
                             <textarea id="form_message" type="text" rows="3" name="smsmessage" class="form-control"
                                 value='<?php echo attr($c['smsMessage']) ?>'><?php echo text($c['smsMessage']) ?></textarea>
                         </div>
+                    <?php } elseif ($service == '1') {
+                        ?> <!-- RC -->
+                        <div class="checkbox">
+                            <label>
+                                <input id="form_production" type="checkbox" name="production" <?php echo attr($c['production']) ? ' checked' : '' ?>>
+                                <?php echo xlt("Production Check") ?>
+                            </label>
+                        </div>
+                        <div class="form-group">
+                            <label for="form_username"><?php echo xlt("Twilio Account Sid") ?> *</label>
+                            <input id="form_username" type="text" name="username" class="form-control"
+                                required="required" value='<?php echo attr($c['username']) ?>' />
+                        </div>
+                        <div class="form-group">
+                            <label for="form_password"><?php echo xlt("Twilio Auth Token") ?> *</label>
+                            <input id="form_password" type="password" name="password" class="form-control"
+                                required="required" value='<?php echo attr($c['password']) ?>' />
+                        </div>
+                        <div class="form-group">
+                            <label for="form_smsnumber"><?php echo xlt("SMS Number") ?></label>
+                            <input id="form_smsnumber" type="text" name="smsnumber" class="form-control"
+                                value='<?php echo attr($c['smsNumber']) ?>' required />
+                        </div>
+                        <div class="form-group">
+                            <label for="form_key"><?php echo xlt("Twilio Api Sid") ?> *</label>
+                            <input id="form_key" type="text" name="key" class="form-control"
+                                required="required" value='<?php echo attr($c['appKey']) ?>' />
+                        </div>
+                        <div class="form-group">
+                            <label for="form_secret"><?php echo xlt("Twilio Api Secret") ?> *</label>
+                            <input id="form_secret" type="password" name="secret" class="form-control"
+                                required="required" value='<?php echo attr($c['appSecret']) ?>' />
+                        </div>
+                        <div class=" form-group">
+                            <label for="form_nhours"><?php echo xlt("Appointments Advance Notify (Hours)") ?> *</label>
+                            <input id="form_nhours" type="text" name="smshours" class="form-control"
+                                placeholder="<?php echo xla('Please enter number of hours before appointment') ?> *"
+                                required="required" value='<?php echo attr($c['smsHours']) ?>' />
+                        </div>
+                        <div class="form-group">
+                            <label for="form_message"><?php echo xlt("Message Template") ?> *</label>
+                            <span style="font-size:12px;font-style: italic">&nbsp;
+                                <?php echo xlt("Replace Tags") ?>: <?php echo text("***NAME***, ***PROVIDER***, ***DATE***, ***STARTTIME***, ***ENDTIME***, ***ORG***"); ?>
+                            </span>
+                            <textarea id="form_message" type="text" rows="3" name="smsmessage" class="form-control"
+                                value='<?php echo attr($c['smsMessage']) ?>'><?php echo text($c['smsMessage']) ?></textarea>
+                        </div>
                     <?php } ?>
                     <div>
                         <span class="text-muted"><strong>*</strong> <?php echo xlt("These fields are required.") ?> </span>
diff --git a/interface/modules/custom_modules/oe-module-faxsms/setup_email.php b/interface/modules/custom_modules/oe-module-faxsms/setup_email.php
new file mode 100644
index 00000000000..bf509df3983
--- /dev/null
+++ b/interface/modules/custom_modules/oe-module-faxsms/setup_email.php
@@ -0,0 +1,163 @@
+<?php
+
+/**
+ * Patient Reminder Setup
+ *
+ * @package   OpenEMR
+ * @link      http://www.open-emr.org
+ * @author    Jerry Padgett <sjpadgett@gmail.com>
+ * @copyright Copyright (c) 2024 Jerry Padgett <sjpadgett@gmail.com>
+ * @license   https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
+ */
+
+require_once(__DIR__ . "/../../../globals.php");
+
+use OpenEMR\Core\Header;
+use OpenEMR\Modules\FaxSMS\Controller\EmailClient;
+
+$emailSetup = new EmailClient();
+$credentials = $emailSetup->getEmailSetup();
+
+if ($_SERVER['REQUEST_METHOD'] === 'POST') {
+    $credentials = array(
+        'sender_name' => $_POST['sender_name'],
+        'sender_email' => $_POST['sender_email'],
+        'notification_email' => $_POST['notification_email'],
+        'email_transport' => $_POST['email_transport'],
+        'smtp_host' => $_POST['smtp_host'],
+        'smtp_port' => $_POST['smtp_port'],
+        'smtp_user' => $_POST['smtp_user'],
+        'smtp_password' => $_POST['smtp_password'],
+        'smtp_security' => $_POST['smtp_security'],
+        'notification_hours' => $_POST['notification_hours'],
+        'smsMessage' => $_POST['smsmessage']
+    );
+    $emailSetup->saveEmailSetup($credentials);
+}
+
+?>
+<!DOCTYPE html>
+<html>
+<head>
+    <title><?php echo xlt("Patient Reminder Setup") ?></title>
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <?php Header::setupHeader(); ?>
+    <script>
+        const popNotify = function (e, ppath) {
+            try {
+                top.restoreSession();
+            } catch (error) {
+                console.log('Session restore failed!');
+            }
+            let msg = <?php echo xlj('Are you sure you wish to send all scheduled reminders now?') ?>;
+            if (e === 'live') {
+                let yn = confirm(msg);
+                if (!yn) {
+                    return false;
+                }
+            }
+            let msg1 = <?php echo xlj('Appointment Reminder Alerts!') ?>;
+            dlgopen(ppath, '_blank', 1240, 900, true, msg1);
+        };
+    </script>
+</head>
+<body>
+    <div class="container">
+        <div class="alert alert-info">
+            <?php echo xlt("Use Config Notifications to setup SMTP. Setup here is work in progress for a secondary client. Sending Email reminders is still available from Reminder Actions dropdown.") ?>
+        </div>
+        <div class="d-flex justify-content-between align-items-center">
+            <h4><?php echo xlt("Setup Patient Reminder Credentials") ?></h4>
+            <div class="dropdown mr-1">
+                <button class="btn btn-outline-primary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                    <?php echo xlt("Reminder Actions") ?>
+                </button>
+                <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
+                    <a class="dropdown-item" href="#" onclick="popNotify('', './library/rc_sms_notification.php?dryrun=1&type=email&site=<?php echo attr_url($_SESSION['site_id']) ?>')"><?php echo xlt('Test Email Reminders'); ?></a>
+                    <a class="dropdown-item" href="#" onclick="popNotify('live', './library/rc_sms_notification.php?type=email&site=<?php echo attr_url($_SESSION['site_id']) ?>')"><?php echo xlt('Send Email Reminders'); ?></a>
+                </div>
+            </div>
+        </div>
+        <form class="form" id="setup-form" method="POST" role="form">
+            <div class="messages"></div>
+            <div class="row">
+                <div class="col-md-6">
+                    <div class="form-group">
+                        <label for="sender_name"><?php echo xlt("Patient Reminder Sender Name") ?></label>
+                        <input id="sender_name" type="text" name="sender_name" class="form-control" value='<?php echo attr($credentials['sender_name']) ?>' required>
+                    </div>
+                </div>
+                <div class="col-md-6">
+                    <div class="form-group">
+                        <label for="sender_email"><?php echo xlt("Patient Reminder Sender Email") ?></label>
+                        <input id="sender_email" type="email" name="sender_email" class="form-control" value='<?php echo attr($credentials['sender_email']) ?>' required>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="col-md-6">
+                    <div class="form-group">
+                        <label for="notification_email"><?php echo xlt("Notification Email Address") ?></label>
+                        <input id="notification_email" type="email" name="notification_email" class="form-control" value='<?php echo attr($credentials['notification_email']) ?>' required>
+                    </div>
+                </div>
+                <div class="col-md-6">
+                    <div class="form-group">
+                        <label for="email_transport"><?php echo xlt("Email Transport Method") ?></label>
+                        <input id="email_transport" type="text" name="email_transport" class="form-control" value='<?php echo attr($credentials['email_transport']) ?>' required>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="col-md-6">
+                    <div class="form-group">
+                        <label for="smtp_host"><?php echo xlt("SMTP Server Hostname") ?></label>
+                        <input id="smtp_host" type="text" name="smtp_host" class="form-control" value='<?php echo attr($credentials['smtp_host']) ?>' required>
+                    </div>
+                </div>
+                <div class="col-md-6">
+                    <div class="form-group">
+                        <label for="smtp_port"><?php echo xlt("SMTP Server Port Number") ?></label>
+                        <input id="smtp_port" type="number" name="smtp_port" class="form-control" value='<?php echo attr($credentials['smtp_port']) ?>' required>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="col-md-6">
+                    <div class="form-group">
+                        <label for="smtp_user"><?php echo xlt("SMTP User for Authentication") ?></label>
+                        <input id="smtp_user" type="text" name="smtp_user" class="form-control" value='<?php echo attr($credentials['smtp_user']) ?>' required>
+                    </div>
+                </div>
+                <div class="col-md-6">
+                    <div class="form-group">
+                        <label for="smtp_password"><?php echo xlt("SMTP Password for Authentication") ?></label>
+                        <input id="smtp_password" type="password" name="smtp_password" class="form-control" value='<?php echo attr($credentials['smtp_password']) ?>' required>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="col-md-6">
+                    <div class="form-group">
+                        <label for="smtp_security"><?php echo xlt("SMTP Security Protocol") ?></label>
+                        <input id="smtp_security" type="text" name="smtp_security" class="form-control" value='<?php echo attr($credentials['smtp_security']) ?>' required>
+                    </div>
+                </div>
+                <div class="col-md-6">
+                    <div class="form-group">
+                        <label for="notification_hours"><?php echo xlt("Email Notification Hours") ?></label>
+                        <input id="notification_hours" type="number" name="notification_hours" class="form-control" value='<?php echo attr($credentials['notification_hours']) ?>' required>
+                    </div>
+                </div>
+            </div>
+            <div class="form-group">
+                <label for="form_message"><?php echo xlt("Message Template") ?></label>
+                <span style="font-size:12px;font-style: italic;">&nbsp;
+                    <?php echo xlt("Tags") ?>: ***NAME***, ***PROVIDER***, ***DATE***, ***STARTTIME***, ***ENDTIME***, ***ORG***</span>
+                <textarea id="form_message" rows="3" name="smsmessage" class="form-control" required><?php echo text($credentials['smsMessage']) ?></textarea>
+            </div>
+            <button type="submit" class="btn btn-success float-right m-2"><?php echo xlt("Save Settings") ?></button>
+        </form>
+    </div>
+</body>
+</html>
diff --git a/interface/modules/custom_modules/oe-module-faxsms/setup_rc.php b/interface/modules/custom_modules/oe-module-faxsms/setup_rc.php
new file mode 100644
index 00000000000..dddf4991110
--- /dev/null
+++ b/interface/modules/custom_modules/oe-module-faxsms/setup_rc.php
@@ -0,0 +1,198 @@
+<?php
+
+/**
+ * Fax SMS Module Member
+ *
+ * @package   OpenEMR
+ * @link      http://www.open-emr.org
+ * @author    Jerry Padgett <sjpadgett@gmail.com>
+ * @copyright Copyright (c) 2018-2024 Jerry Padgett <sjpadgett@gmail.com>
+ * @license   https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
+ */
+
+namespace OpenEMR\Modules\FaxSMS\Controllers;
+
+require_once(__DIR__ . "/../../../globals.php");
+
+use OpenEMR\Core\Header;
+use OpenEMR\Modules\FaxSMS\Controller\AppDispatch;
+
+// kick off app endpoints controller
+$serviceType = $_REQUEST['type'] ?? $_SESSION["oefax_current_module_type"] ?? '';
+// kick off app endpoints controller
+$clientApp = AppDispatch::getApiService($serviceType);
+$service = $clientApp::getServiceType();
+if (!$clientApp->verifyAcl()) {
+    die("<h3>" . xlt("Not Authorised!") . "</h3>");
+}
+$c = $clientApp->getCredentials();
+echo "<script>var pid=" . js_escape($pid) . "</script>";
+?>
+<!DOCTYPE html>
+<html>
+<head>
+    <title>><?php echo xlt("Setup") ?></title>
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <?php Header::setupHeader();
+    echo "<script>let Service=" . js_escape($service) . "</script>";
+    ?>
+    <script>
+        $(function () {
+            $('#setup-form').on('submit', function (e) {
+                if (!e.isDefaultPrevented()) {
+                    $(window).scrollTop(0);
+                    let wait = '<i class="fa fa-cog fa-spin fa-4x"></i>';
+                    let url = 'saveSetup?type=' + encodeURIComponent(<?php echo js_escape($serviceType) ?>);
+                    $.ajax({
+                        type: "POST",
+                        url: url,
+                        data: $(this).serialize(),
+                        success: function (data) {
+                            var err = (data.search(/Exception/) !== -1 ? 1 : 0);
+                            if (!err) {
+                                err = (data.search(/Error:/) !== -1 ? 1 : 0);
+                            }
+                            var messageAlert = 'alert-' + (err !== 0 ? 'danger' : 'success');
+                            var messageText = data;
+                            var alertBox = '<div class="alert ' + messageAlert + ' alert-dismissable"><button type="button" ' +
+                                'class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + messageText + '</div>';
+                            if (messageAlert && messageText) {
+                                // inject the alert to .messages div in our form
+                                $('#setup-form').find('.messages').html(alertBox);
+                                if (!err) {
+                                    // empty the form
+                                    $('#setup-form')[0].reset();
+                                    setTimeout(function () {
+                                        $('#setup-form').find('.messages').remove();
+                                        <?php if (!$module_config) { ?>
+                                        dlgclose();
+                                        <?php } else { ?>
+                                        location.reload();
+                                        <?php } ?>
+                                    }, 2000);
+                                }
+                            }
+                        }
+                    });
+                    return false;
+                }
+            });
+            if (Service === '2') {
+                $(".ringcentral").hide();
+            } else {
+                $(".twilio").hide();
+            }
+        });
+
+        function openJwtWindow() {
+            const clientId = document.getElementById('form_key').value;
+            if (clientId) {
+                const url = 'https://developers.ringcentral.com/console/my-credentials/create?client_id=' + encodeURIComponent(clientId);
+                window.open(url, '_blank', 'width=1200,height=800');
+            } else {
+                let message = xl("Please enter Client ID first.");
+                (async (message, time) => {
+                    await asyncAlertMsg(message, time, 'warning', 'lg');
+                })(message,1500).then(res => {
+                    console.log(res);
+                });
+            }
+        }
+
+        function togglePasswordVisibility(id) {
+            var input = document.getElementById(id);
+            var icon = document.querySelector(`#${id} + .toggle-password i`);
+            if (input.type === "password") {
+                input.type = "text";
+                icon.classList.remove("fa-eye");
+                icon.classList.add("fa-eye-slash");
+            } else {
+                input.type = "password";
+                icon.classList.remove("fa-eye-slash");
+                icon.classList.add("fa-eye");
+            }
+        }
+    </script>
+</head>
+<body>
+    <div class="container-fluid">
+        <?php if ($module_config) { ?>
+            <h4><?php echo xlt("Setup Credentials") ?></h4>
+        <?php } ?>
+        <form class="form" id="setup-form" role="form">
+            <div class="messages"></div>
+            <div class="row">
+                <div class="col-md-12">
+                    <div class="checkbox">
+                        <button type="submit" class="btn btn-success float-right m-2" value=""><?php echo xlt("Save Settings") ?></button>
+                        <label>
+                            <input id="form_production" type="checkbox" name="production" <?php echo attr($c['production']) ? ' checked' : '' ?>>
+                            <?php echo xlt("Production Check") ?>
+                        </label>
+                    </div>
+                        <div class="form-group">
+                            <label for="form_extension"><?php echo xlt("Extension") ?></label>
+                            <input id="form_extension" type="text" name="extension" class="form-control" required="required" value='<?php echo attr($c['extension']) ?>'>
+                        </div>
+                        <div class="form-group">
+                            <label for="form_phone"><?php echo xlt("FAX Phone Number") ?></label>
+                            <input type="tel" class="form-control" id="form_phone" name="phone" value='<?php echo attr($c['phone']) ?>'>
+                        </div>
+                        <div class="form-group">
+                            <label for="form_smsnumber"><?php echo xlt("SMS Phone Number") ?></label>
+                            <input id="form_smsnumber" type="tel" name="smsnumber" class="form-control"
+                                value='<?php echo attr($c['smsNumber']) ?>'>
+                        </div>
+                    </div>
+                    <div class="col-md">
+                        <div class="form-group">
+                            <label for="form_key"><?php echo xlt("Client ID") ?> *</label>
+                            <div class="input-group">
+                                <input id="form_key" type="password" name="key" class="form-control"
+                                    required="required" value='<?php echo attr($c['appKey']) ?>'>
+                                <div class="input-group-append toggle-password" onclick="togglePasswordVisibility('form_key')">
+                                    <span class="input-group-text"><i class="fa fa-eye"></i></span>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="form-group">
+                            <label for="form_secret"><?php echo xlt("Client Secret") ?> *</label>
+                            <div class="input-group">
+                                <input id="form_secret" type="password" name="secret" class="form-control"
+                                    required="required" value='<?php echo attr($c['appSecret']) ?>'>
+                                <div class="input-group-append toggle-password" onclick="togglePasswordVisibility('form_secret')">
+                                    <span class="input-group-text"><i class="fa fa-eye"></i></span>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="form-group">
+                            <label for="form_jwt"><?php echo xlt("Copy and Paste JWT") ?> *</label>
+                            <span class="form-group">
+                                <button type="button" class="btn btn-primary btn-download btn-sm mb-1 p-0 px-1 float-right" onclick="openJwtWindow()"><?php echo xlt("Create JWT") ?></button>
+                            </span>
+                            <textarea id="form_jwt" type="text" rows="3" name="jwt" class="form-control small" required="required"><?php echo attr($c['jwt']) ?></textarea>
+                        </div>
+                        <div class=" form-group">
+                            <label for="form_nhours"><?php echo xlt("Appointment Advance Notification (Hours)") ?> *</label>
+                            <input id="form_nhours" type="text" name="smshours" class="form-control"
+                                placeholder="<?php echo xlt('Please enter number of hours before appointment') ?> *"
+                                required="required" value='<?php echo attr($c['smsHours']) ?>'>
+                        </div>
+                    </div>
+                    <div class="col-md-12">
+                        <div class="form-group">
+                            <label for="form_message"><?php echo xlt("Message Template") ?> *</label>
+                            <span style="font-size:12px;font-style: italic;">&nbsp;
+<?php echo xlt("Tags") ?>: ***NAME***, ***PROVIDER***, ***DATE***, ***STARTTIME***, ***ENDTIME***, ***ORG***</span>
+                            <textarea id="form_message" type="text" rows="3" name="smsmessage" class="form-control small"
+                                required="required" value='<?php echo attr($c['smsMessage']) ?>'><?php echo attr($c['smsMessage']) ?></textarea>
+                        </div>
+                    </div>
+                    <p class="text-muted"><strong>*</strong> <?php echo xlt("These fields are required.") ?> </p>
+                </div>
+            </div>
+            <button type="submit" class="btn btn-success float-right m-2" value=""><?php echo xlt("Save Settings") ?></button>
+        </form>
+    </div>
+</body>
+</html>
diff --git a/interface/modules/custom_modules/oe-module-faxsms/src/BootstrapService.php b/interface/modules/custom_modules/oe-module-faxsms/src/BootstrapService.php
index 0321a208e0d..43498f6b27f 100644
--- a/interface/modules/custom_modules/oe-module-faxsms/src/BootstrapService.php
+++ b/interface/modules/custom_modules/oe-module-faxsms/src/BootstrapService.php
@@ -114,9 +114,9 @@ public function saveModuleListenerGlobals($items): void
 
     /**
      * @param $settings
-     * @return mixed
+     * @return array|false|null
      */
-    public function persistSetupSettings($settings): mixed
+    public function persistSetupSettings($settings): array|null|false
     {
         // vendor for backup of setup globals.
         $vendor = '_persisted';
diff --git a/interface/modules/custom_modules/oe-module-faxsms/src/Controller/AppDispatch.php b/interface/modules/custom_modules/oe-module-faxsms/src/Controller/AppDispatch.php
index bb4568bdc17..f7963a8507a 100644
--- a/interface/modules/custom_modules/oe-module-faxsms/src/Controller/AppDispatch.php
+++ b/interface/modules/custom_modules/oe-module-faxsms/src/Controller/AppDispatch.php
@@ -6,7 +6,7 @@
  * @package   OpenEMR
  * @link      http://www.open-emr.org
  * @author    Jerry Padgett <sjpadgett@gmail.com>
- * @copyright Copyright (c) 2018-2023 Jerry Padgett <sjpadgett@gmail.com>
+ * @copyright Copyright (c) 2018-2024 Jerry Padgett <sjpadgett@gmail.com>
  * @license   https://github.com/openemr/openemr/blob/master/LICENSE GNU General public License 3
  */
 
@@ -15,7 +15,6 @@
 use MyMailer;
 use OpenEMR\Common\Acl\AclMain;
 use OpenEMR\Common\Session\SessionUtil;
-use OpenEMR\Events\Messaging\SendNotificationEvent;
 
 /**
  * Class AppDispatch
@@ -26,7 +25,7 @@ abstract class AppDispatch
 {
     const ACTION_DEFAULT = 'index';
     static $_apiService;
-    static $_apiModule;
+    static mixed $_apiModule;
     public string $authErrorDefault;
     public static $timeZone;
     protected $crypto;
@@ -99,7 +98,7 @@ private function dispatchActions(): void
     public function getSession($param = null, $default = null): mixed
     {
         if ($param) {
-            return $_SESSION[$param] ?? $default;
+            return $_SESSION[$param] ?: $default;
         }
 
         return $this->_session;
@@ -113,7 +112,7 @@ public function getSession($param = null, $default = null): mixed
     public function getQuery($param = null, $default = null): mixed
     {
         if ($param) {
-            return $this->_query[$param] ?? $default;
+            return $this->_query[$param] ?: $default;
         }
 
         return $this->_query;
@@ -223,7 +222,7 @@ static function getServiceInstance($type)
                 case 0:
                     break;
                 case 1:
-                    // for new service in future
+                    return new RCFaxClient();
                     break;
                 case 2:
                     return new TwilioSMSClient();
@@ -233,7 +232,7 @@ static function getServiceInstance($type)
                 case 0:
                     break;
                 case 1:
-                    // for new service in future
+                    return new RCFaxClient();
                     break;
                 case 3:
                     return new EtherFaxActions();
@@ -242,7 +241,7 @@ static function getServiceInstance($type)
             switch ($s) {
                 case 0:
                     break;
-                case 1:
+                case 4:
                     return new EmailClient();
             }
         }
@@ -284,6 +283,8 @@ static function getModuleType(): mixed
         return self::$_apiModule;
     }
 
+    //abstract function faxProcessUploads();
+
     /**
      * @return string|bool
      */
@@ -312,7 +313,7 @@ abstract function fetchReminderCount(): string|bool;
     public function getPost($param = null, $default = null): mixed
     {
         if ($param) {
-            return $this->_post[$param] ?? $default;
+            return $this->_post[$param] ?: $default;
         }
 
         return $this->_post;
@@ -362,6 +363,7 @@ protected function saveSetup(array $setup = []): string
             $smsNumber = $this->getRequest('smsnumber');
             $smsMessage = $this->getRequest('smsmessage');
             $smsHours = $this->getRequest('smshours');
+            $jwt = $this->getRequest('jwt');
             $setup = array(
                 'username' => "$username",
                 'extension' => "$ext",
@@ -370,13 +372,14 @@ protected function saveSetup(array $setup = []): string
                 'password' => "$password",
                 'appKey' => "$appkey",
                 'appSecret' => "$appsecret",
-                'server' => "",
-                'portal' => "",
+                'server' => !$production ? 'https://platform.devtest.ringcentral.com' : "https://platform.ringcentral.com",
+                'portal' => !$production ? "https://service.devtest.ringcentral.com/" : "https://service.ringcentral.com/",
                 'smsNumber' => "$smsNumber",
                 'production' => $production,
-                'redirect_url' => "",
+                'redirect_url' => $this->getRequest('redirect_url'),
                 'smsHours' => $smsHours,
-                'smsMessage' => $smsMessage
+                'smsMessage' => $smsMessage,
+                'jwt' => $jwt ?? '',
             );
         }
 
@@ -405,7 +408,7 @@ protected function saveSetup(array $setup = []): string
     public function getRequest($param = null, $default = null): mixed
     {
         if ($param) {
-            return $this->_request[$param] ?? $default;
+            return $this->_request[$param] ?: $default;
         }
 
         return $this->_request;
@@ -418,7 +421,7 @@ static function getModuleVendor(): mixed
     {
         switch ((string)self::getServiceType()) {
             case '1':
-                return '_default_email';
+                return '_ringcentral';
             case '2':
                 return '_twilio';
             case '3':
@@ -427,6 +430,56 @@ static function getModuleVendor(): mixed
         return null;
     }
 
+    public function getEmailSetup(): mixed
+    {
+        $vendor = '_email';
+        $this->authUser = (int)$this->getSession('authUserID');
+        if (!($GLOBALS['oerestrict_users'] ?? null)) {
+            $this->authUser = 0;
+        }
+        $credentials = sqlQuery("SELECT * FROM `module_faxsms_credentials` WHERE `auth_user` = ? AND `vendor` = ?", array($this->authUser, $vendor));
+
+        if (empty($credentials['smtp_user']) || empty($credentials['smtp_host']) || empty($credentials['smtp_password'])) {
+            $credentials = array(
+                'sender_name' => $GLOBALS['patient_reminder_sender_name'],
+                'sender_email' => $GLOBALS['patient_reminder_sender_email'],
+                'notification_email' => $GLOBALS['practice_return_email_path'],
+                'email_transport' => $GLOBALS['EMAIL_METHOD'],
+                'smtp_host' => $GLOBALS['SMTP_HOST'],
+                'smtp_port' => $GLOBALS['SMTP_PORT'],
+                'smtp_user' => $GLOBALS['SMTP_USER'],
+                'smtp_password' => $GLOBALS['SMTP_PASS'],
+                'smtp_security' => $GLOBALS['SMTP_SECURE'],
+                'notification_hours' => $GLOBALS['EMAIL_NOTIFICATION_HOUR']
+            );
+            if (empty($credentials['smsMessage'] ?? '')) {
+                $credentials['smsMessage'] = "A courtesy reminder for ***NAME*** \r\nFor the appointment scheduled on: ***DATE*** At: ***STARTTIME*** Until: ***ENDTIME*** \r\nWith: ***PROVIDER*** Of: ***ORG***\r\nPlease call if unable to attend.";
+            }
+            return $credentials;
+        } else {
+            $credentials = $credentials['credentials'];
+            if (empty($credentials['smsMessage'] ?? '')) {
+                $credentials['smsMessage'] = "A courtesy reminder for ***NAME*** \r\nFor the appointment scheduled on: ***DATE*** At: ***STARTTIME*** Until: ***ENDTIME*** \r\nWith: ***PROVIDER*** Of: ***ORG***\r\nPlease call if unable to attend.";
+            }
+        }
+
+        $decrypt = $this->crypto->decryptStandard($credentials);
+        $decode = json_decode($decrypt, true);
+        return $decode;
+    }
+
+    public function saveEmailSetup($credentials): void
+    {
+        $vendor = '_email';
+        $this->authUser = (int)$this->getSession('authUserID');
+        if (!($GLOBALS['oerestrict_users'] ?? null)) {
+            $this->authUser = 0;
+        }
+        $encoded = json_encode($credentials);
+        $encrypted = $this->crypto->encryptStandard($encoded);
+        sqlStatement("INSERT INTO `module_faxsms_credentials` (auth_user, vendor, credentials, updated) VALUES (?, ?, ?, NOW()) ON DUPLICATE KEY UPDATE credentials = VALUES(credentials), updated = VALUES(updated)", array($this->authUser, $vendor, $encrypted));
+    }
+
     /**
      * Common credentials storage between services
      * the service class will set specific credential.
@@ -458,13 +511,15 @@ protected function getSetup(): mixed
                 'redirect_url' => '',
                 'smsHours' => "50",
                 'smsMessage' => "A courtesy reminder for ***NAME*** \r\nFor the appointment scheduled on: ***DATE*** At: ***STARTTIME*** Until: ***ENDTIME*** \r\nWith: ***PROVIDER*** Of: ***ORG***\r\nPlease call if unable to attend.",
+                'jwt' => '',
             );
             return $credentials;
         } else {
             $credentials = $credentials['credentials'];
         }
 
-        $decode = json_decode($this->crypto->decryptStandard($credentials), true);
+        $decrypt = $this->crypto->decryptStandard($credentials);
+        $decode = json_decode($decrypt, true);
         if (empty($decode['smsMessage'])) {
             $decode['smsMessage'] = "A courtesy reminder for ***NAME*** \r\nFor the appointment scheduled on: ***DATE*** At: ***STARTTIME*** Until: ***ENDTIME*** \r\nWith: ***PROVIDER*** Of: ***ORG***\r\nPlease call if unable to attend.";
         }
@@ -500,7 +555,7 @@ public function getPatientDetails(): bool|string
      * @param $email
      * @return bool
      */
-    protected function validEmail($email): bool
+    public function validEmail($email): bool
     {
         if (preg_match("/^[_a-z0-9-]+(\.[_a-z0-9-\+]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$/i", $email)) {
             return true;
@@ -559,16 +614,8 @@ public function mailEmail($email, $from_name, $body, string $subject = '', strin
      * @param $u
      * @return bool
      */
-    public function verifyAcl($sect = 'admin', $v = 'docs', $u = ''): bool
+    public function verifyAcl($sect = 'Clinicians', $v = 'docs', $u = ''): bool
     {
         return AclMain::aclCheckCore($sect, $v, $u);
     }
-
-    /**
-     * @return null
-     */
-    private function indexAction()
-    {
-        return null;
-    }
 }
diff --git a/interface/modules/custom_modules/oe-module-faxsms/src/Controller/EmailClient.php b/interface/modules/custom_modules/oe-module-faxsms/src/Controller/EmailClient.php
index 085ed9139d9..b7433031dcf 100644
--- a/interface/modules/custom_modules/oe-module-faxsms/src/Controller/EmailClient.php
+++ b/interface/modules/custom_modules/oe-module-faxsms/src/Controller/EmailClient.php
@@ -14,6 +14,7 @@
 
 use MyMailer;
 use OpenEMR\Common\Crypto\CryptoGen;
+use PHPMailer\PHPMailer\Exception;
 use Symfony\Component\HttpClient\HttpClient;
 
 class EmailClient extends AppDispatch
@@ -22,10 +23,11 @@ class EmailClient extends AppDispatch
     public $baseDir;
     public $uriDir;
     public $serverUrl;
-    public $credentials;
+    public mixed $credentials;
     public string $portalUrl;
     protected $crypto;
     private EmailClient $client;
+    private bool $smtpEnabled;
 
     public function __construct()
     {
@@ -35,6 +37,7 @@ public function __construct()
         $this->crypto = new CryptoGen();
         $this->baseDir = $GLOBALS['temporary_files_dir'];
         $this->uriDir = $GLOBALS['OE_SITE_WEBROOT'];
+        $this->smtpEnabled = !empty($GLOBALS['SMTP_PASS'] ?? null) && !empty($GLOBALS["SMTP_USER"] ?? null);
         parent::__construct();
     }
 
@@ -55,10 +58,6 @@ public function getCredentials(): mixed
         return $credentials;
     }
 
-    /**
-     * @return bool|string
-     */
-
     /**
      * @return string
      */
@@ -81,7 +80,7 @@ public function sendFax(): string|bool
      * @param $acl
      * @return int
      */
-    public function authenticate($acl = ['admin', 'doc']): int
+    public function authenticate($acl = ['patient', 'doc']): int
     {
         list($s, $v) = $acl;
         return $this->verifyAcl($s, $v);
@@ -107,6 +106,50 @@ public function sendEmail(): string
         return js_escape($statusMsg);
     }
 
+    /**
+     * @throws Exception
+     */
+    public function emailDocument($email, $body, $file, array $user = []): string
+    {
+        $from_name = ($user['fname'] ?? '') . ' ' . ($user['lname'] ?? '');
+        $desc = xlt("Comment") . ":\n" . text($body) . "\n" . xlt("This email has an attached fax document.");
+        $mail = new MyMailer();
+        $from_name = text($from_name);
+        $from = $GLOBALS["practice_return_email_path"];
+        $mail->AddReplyTo($from, $from_name);
+        $mail->SetFrom($from, $from);
+        $mail->AddAddress($email, $email);
+        $mail->Subject = xlt("Forwarded Fax Document");
+        $mail->Body = $desc;
+        $mail->AddAttachment($file);
+
+        return $mail->Send() ? xlt("Email successfully sent.") : xlt("Error: Email failed") . text($mail->ErrorInfo);
+    }
+
+    /**
+     * @throws Exception
+     */
+    public function emailReminder($email, $body): false|string
+    {
+        $hasEmail = $this->validEmail($email);
+        if (!$hasEmail) {
+            return js_escape(xlt("Error: Missing valid email address. Try again."));
+        }
+        if (!$this->smtpEnabled) {
+            return text(js_escape('SMTP not setup.'));
+        }
+        $from_name = text($GLOBALS["Patient Reminder Sender Name"] ?? 'UNK');
+        $desc = text($body);
+        $mail = new MyMailer();
+        $from = text($GLOBALS["practice_return_email_path"]);
+        $mail->AddReplyTo($from, $from_name);
+        $mail->SetFrom($from, $from);
+        $mail->AddAddress($email, $email);
+        $mail->Subject = xlt("A Reminder for You");
+        $mail->Body = $desc;
+
+        return $mail->Send();
+    }
     /**
      * @return false|string
      */
diff --git a/interface/modules/custom_modules/oe-module-faxsms/src/Controller/EtherFaxActions.php b/interface/modules/custom_modules/oe-module-faxsms/src/Controller/EtherFaxActions.php
index 29b73491a93..f015d6a7208 100644
--- a/interface/modules/custom_modules/oe-module-faxsms/src/Controller/EtherFaxActions.php
+++ b/interface/modules/custom_modules/oe-module-faxsms/src/Controller/EtherFaxActions.php
@@ -169,6 +169,8 @@ public function sendSMS(): string
     }
 
     /**
+     * TODO: decrypt document
+     *
      * @return string
      * @throws \PHPMailer\PHPMailer\Exception
      */
@@ -345,6 +347,8 @@ public function forwardFax(): string
     }
 
     /**
+     * Rest Endpoint
+     *
      * @return string|void
      */
     public function getPending()
@@ -352,65 +356,140 @@ public function getPending()
         if (!$this->authenticate()) {
             return $this->authErrorDefault;
         }
-        // Let us fetch any new pending faxes to queue.
-        $newFaxCount = $this->pollAndInsertAllPendingFax();
-        $pull = $this->fetchReminderCount();
-        $dateFrom = date("Y-m-d H:i:s", strtotime(($this->getRequest('datefrom') . 'T00:00:01')));
-        $dateTo = date("Y-m-d H:i:s", strtotime(($this->getRequest('dateto') . 'T23:59:59')));
+
+        $this->pollAndInsertAllPendingFax();
+        $dateFrom = date("Y-m-d H:i:s", strtotime($this->getRequest('datefrom') . 'T00:00:01'));
+        $dateTo = date("Y-m-d H:i:s", strtotime($this->getRequest('dateto') . 'T23:59:59'));
         $faxStore = $this->fetchFaxQueue($dateFrom, $dateTo, false);
-        $responseMsgs = [0 => '', 2 => xlt('Not Implemented')];
+
+        $responseMsg = [0 => '', 2 => xlt('Not Implemented')];
 
         foreach ($faxStore as $faxDetails) {
             $id = $faxDetails->JobId;
             $record_id = $faxDetails->RecordId;
             $faxDate = strtotime($faxDetails->ReceivedOn . ' UTC');
-            $to = $faxDetails->CalledNumber;
-            $from = $faxDetails->CallingNumber;
-            $params = $faxDetails->DocumentParams;
-            $showFlag = 0;
-            $recogized = $faxDetails->AnalyzeFormResult->AnalyzeResult->DocumentResults ?? [];
-
-            $form = '';
-            foreach ($recogized as $r) {
-                $details = null;
-                $form = "<tr id='" . text($id) . "' class='d-none collapse-all'><td colspan='12'><div class='container table-responsive'><table class='table table-sm table-bordered table-dark'>";
-                $form .= "<thead><tr><th>" . xlt("Parameter") . "</th><th>" . xlt("Value") . "</th><th>" . xlt("Confidence") . " : " . text($r->DocTypeConfidence * 100) . "</th></tr></thead><tbody>";
-                $parse = $this->parseValidators($r->Fields) ?? [];
-                $pid_assumed = sqlQuery(
-                    "Select pid From patient_data Where fname = ? And lname = ? And DOB = ?",
-                    [$parse['fname'] ?? '', $parse['lname'] ?? '', date("Y-m-d", strtotime(($parse['DOB'] ?? '')))]
-                )['pid'] ?? 'No';
-
-                foreach ($r->Fields as $field) {
-                    if ($field->Text == 'unselected' || empty($field->Text)) {
-                        continue;
-                    }
-                    $showFlag++;
-                    $form .= "<tr><td>" . text(str_replace(" - ", "-", $field->Name)) . "</td><td>" . text($field->Text) . "</td><td>" . text($field->Confidence * 100) . "</td></tr>";
-                }
-                $form .= "</tbody></table></div></td></tr>";
+            $formattedDate = date('M j, Y g:i:sa T', $faxDate);
+            $docLen = round($faxDetails->DocumentParams->Length / 1000, 2) . "KB";
+            $transactionType = $this->getTransactionTypeWord($faxDetails->TransactionType);
+
+            $recognizeResult = $faxDetails->AnalyzeFormResult->AnalyzeResult->DocumentResults ?? [];
+            $form = $this->generateFaxForm($id, $recognizeResult);
+
+            $pid_assumed = $this->getAssumedPatientId($recognizeResult);
+
+            $actionLinks = $this->generateActionLinks($id, $record_id, $pid_assumed);
+            $detailLink = $this->generateDetailLink($id, $recognizeResult);
+
+            if ($faxDetails->TransactionType == '0') {
+                $faxRow = "<tr>
+                <td>" . text($formattedDate) . "</td>
+                <td>" . text($faxDetails->CallingNumber) . "</td>
+                <td>" . text($faxDetails->RemoteId) . "</td>
+                <td>" . text($faxDetails->CalledNumber) . "</td>
+                <td>" . text($faxDetails->PagesReceived) . "</td>
+                <td>" . text($docLen) . "</td>
+                <td>" . text($transactionType) . "</td>
+                <td class='text-left'>" . $detailLink . "</td>
+                <td class='text-center'>" . text($pid_assumed) . "</td>
+                <td class='text-left'>" . $actionLinks . "</td>
+                <td class='text-center'><input type='checkbox' class='delete-fax-checkbox' value='" . attr($id) . "'></td></tr>";
+            } else {
+                $faxRow = "<tr>
+                <td>" . text($formattedDate) . "</td>
+                <td>" . text($faxDetails->CallingNumber) . "</td>
+                <td>" . text($faxDetails->RemoteId) . "</td>
+                <td>" . text($faxDetails->CalledNumber) . "</td>
+                <td>" . text($faxDetails->PagesReceived) . "</td>
+                <td>" . text($docLen) . "</td>
+                <td>" . text($transactionType) . "</td>
+                <td class='text-left'>" . $actionLinks . "</td>
+                <td class='text-center'><input type='checkbox' class='delete-fax-checkbox' value='" . attr($id) . "'></td></tr>";
             }
+            $responseMsg[$faxDetails->TransactionType == '0' ? 0 : 1] .= $faxRow . $form;
+        }
+
+        if (empty($responseMsg[0])) {
+            $responseMsg[0] = xlt("Currently inbox is empty.");
+        }
 
-            $patientLink = "<a role='button' href='javascript:void(0)' onclick=\"createPatient(event, " . attr_js($id) . ", " . attr_js($record_id) . ", " . attr_js(json_encode($parse ?? [])) . ")\"> <i class='fa fa-chart-simple mr-2' title='" . xla("Chart fax or Create patient and chart fax to documents.") . "'></i></a>";
-            $messageLink = "<a role='button' href='javascript:void(0)' onclick=\"notifyUser(event, " . attr_js($id) . ", " . attr_js($record_id) . ", " . attr_js(($pid_assumed ?? 0)) . ")\"> <i class='fa fa-paper-plane mr-2' title='" . xla("Notify a user and attach this fax to message.") . "'></i></a>";
-            $downloadLink = "<a role='button' href='javascript:void(0)' onclick=\"getDocument(event, null, " . attr_js($id) . ", 'true')\"> <i class='fa fa-file-download mr-2' title='" . xla("Download and delete fax") . "'></i></a>";
-            $viewLink = "<a role='button' href='javascript:void(0)' onclick=\"getDocument(event, null, " . attr_js($id) . ", 'false')\"> <i class='fa fa-file-pdf mr-2' title='" . xla("View fax document") . "'></i></a>";
-            $deleteLink = "<a role='button' href='javascript:void(0)' onclick=\"getDocument(event, null, " . attr_js($id) . ", 'false', 'true')\"> <i class='text-danger fa fa-trash mr-2' title='" . xla("Delete this fax document") . "'></i></a>";
-            $forwardLink = "<a role='button' href='javascript:void(0)' onclick=\"forwardFax(event, " . attr_js($id) . ")\"> <i class='fa fa-forward mr-2' title='" . xla("Forward fax to new fax recipient or email attachment.") . "'></i></a>";
-            $detailLink = $showFlag ? "<a role='button' href='javascript:void(0)' class='btn btn-link fa fa-eye' onclick='toggleDetail(\"#" . text($id) . "\")'></a>" . text($showFlag) . ' ' . xlt("Items") : '';
+        echo json_encode($responseMsg);
+        exit();
+    }
+
+    private function getTransactionTypeWord($transactionType)
+    {
+        $transactionTypes = [
+            '0' => xlt('Received'),
+            '1' => xlt('Sent'),
+            '2' => xlt('Forwarded'),
+            '3' => xlt('Failed')
+            // Add more as needed
+        ];
+
+        return $transactionTypes[$transactionType] ?? xlt('Unknown');
+    }
 
-            $faxFormattedDate = date('M j, Y g:i:sa T', $faxDate);
-            $docLen = text(round($params->Length / 1000, 2)) . "KB";
-            $responseMsgs[0] .= "<tr><td>" . text($faxFormattedDate) . "</td><td>" . text($from) . "</td><td>" . text($faxDetails->RemoteId) . "</td><td>" . text($to) . "</td><td>" . text($faxDetails->PagesReceived) . "</td><td>" . text($docLen) . "</td><td class='text-left'>" . $detailLink . "</td><td class='text-center'>" . text($pid_assumed ?? '') . "</td><td class='text-left'>" . $patientLink . $messageLink . $forwardLink . $viewLink . $downloadLink . $deleteLink . "</td></tr>";
-            $responseMsgs[0] .= $form;
+    private function getAssumedPatientId($recognized)
+    {
+        foreach ($recognized as $r) {
+            $parse = $this->parseValidators($r->Fields) ?? [];
+            return sqlQuery(
+                "SELECT pid FROM patient_data WHERE fname = ? AND lname = ? AND DOB = ?",
+                [$parse['fname'] ?? '', $parse['lname'] ?? '', date("Y-m-d", strtotime($parse['DOB'] ?? ''))]
+            )['pid'] ?? 'No';
+        }
+        return 'No';
+    }
+
+    private function generateFaxForm($id, $recognized)
+    {
+        if (empty($recognized)) {
+            return '';
         }
 
-        if (empty($responseMsgs[0])) {
-            $responseMsgs[0] = xlt("Currently inbox is empty.");
+        $form = "<tr id='" . attr($id) . "' class='d-none collapse-all'><td colspan='12'><div class='container table-responsive'><table class='table table-sm table-bordered table-dark'>";
+        $form .= "<thead><tr><th>" . xlt("Parameter") . "</th><th>" . xlt("Value") . "</th><th>" . xlt("Confidence") . "</th></tr></thead><tbody>";
+
+        foreach ($recognized as $r) {
+            foreach ($r->Fields as $field) {
+                if ($field->Text == 'unselected' || empty($field->Text)) {
+                    continue;
+                }
+
+                $form .= "<tr><td>" . text(str_replace(" - ", "-", $field->Name)) . "</td><td>" . text($field->Text) . "</td><td>" . text($field->Confidence * 100) . "</td></tr>";
+            }
         }
 
-        echo json_encode($responseMsgs);
-        exit();
+        $form .= "</tbody></table></div></td></tr>";
+        return $form;
+    }
+
+    private function generateActionLinks($id, $record_id, $pid_assumed)
+    {
+        return "<a role='button' href='javascript:void(0)' onclick=\"createPatient(event, " . attr_js($id) . ", " . attr_js($record_id) . ", " . attr_js(json_encode([])) . ")\">
+                <i class='fa fa-chart-simple mr-2' title='" . xla("Chart fax or Create patient and chart fax to documents.") . "'></i>
+            </a>
+            <a role='button' href='javascript:void(0)' onclick=\"notifyUser(event, " . attr_js($id) . ", " . attr_js($record_id) . ", " . attr_js($pid_assumed) . ")\">
+                <i class='fa fa-paper-plane mr-2' title='" . xla("Notify a user and attach this fax to message.") . "'></i>
+            </a>
+            <a role='button' href='javascript:void(0)' onclick=\"getDocument(event, null, " . attr_js($id) . ", 'true')\">
+                <i class='fa fa-file-download mr-2' title='" . xla("Download and delete fax") . "'></i>
+            </a>
+            <a role='button' href='javascript:void(0)' onclick=\"getDocument(event, null, " . attr_js($id) . ", 'false')\">
+                <i class='fa fa-file-pdf mr-2' title='" . xla("View fax document") . "'></i>
+            </a>
+            <a role='button' href='javascript:void(0)' onclick=\"getDocument(event, null, " . attr_js($id) . ", 'false', 'true')\">
+                <i class='text-danger fa fa-trash mr-2' title='" . xla("Delete this fax document") . "'></i>
+            </a>
+            <a role='button' href='javascript:void(0)' onclick=\"forwardFax(event, " . attr_js($id) . ")\">
+                <i class='fa fa-forward mr-2' title='" . xla("Forward fax to new fax recipient or email attachment.") . "'></i>
+            </a>";
+    }
+
+    private function generateDetailLink($id, $recognized)
+    {
+        $showFlag = count($recognized);
+        return $showFlag ? "<a role='button' href='javascript:void(0)' class='btn btn-link fa fa-eye' onclick='toggleDetail(\"#" . text($id) . "\")'></a>" . text($showFlag) . ' ' . xlt("Items") : '';
     }
 
     /**
diff --git a/interface/modules/custom_modules/oe-module-faxsms/src/Controller/RCFaxClient.php b/interface/modules/custom_modules/oe-module-faxsms/src/Controller/RCFaxClient.php
new file mode 100644
index 00000000000..d0498de301b
--- /dev/null
+++ b/interface/modules/custom_modules/oe-module-faxsms/src/Controller/RCFaxClient.php
@@ -0,0 +1,1231 @@
+<?php
+
+/**
+ * Fax SMS Module Member
+ *
+ * @package   OpenEMR
+ * @link      http://www.open-emr.org
+ * @license   https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
+ */
+
+namespace OpenEMR\Modules\FaxSMS\Controller;
+
+use Document;
+use Exception;
+use MyMailer;
+use OpenEMR\Common\Crypto\CryptoGen;
+use OpenEMR\Common\Utils\FileUtils;
+use OpenEMR\Services\ImageUtilities\HandleImageService;
+use RingCentral\SDK\Http\ApiException;
+use RingCentral\SDK\SDK;
+
+class RCFaxClient extends AppDispatch
+{
+    public $baseDir;
+    public $uriDir;
+    public $serverUrl;
+    public $redirectUrl;
+    public $portalUrl;
+    public $credentials;
+    public $cacheDir;
+    public $apiBase;
+    public $apiService;
+    protected $platform;
+    protected $rcsdk;
+    protected $crypto;
+
+    public function __construct()
+    {
+        $this->crypto = new CryptoGen();
+        $this->baseDir = $GLOBALS['temporary_files_dir'];
+        $this->uriDir = $GLOBALS['OE_SITE_WEBROOT'];
+        $this->cacheDir = $GLOBALS['OE_SITE_DIR'] . '/documents/logs_and_misc/_cache';
+        $this->credentials = $this->getCredentials();
+        $this->portalUrl = $this->credentials['production'] ? "https://service.ringcentral.com/" : "https://service.devtest.ringcentral.com/";
+        $this->serverUrl = $this->credentials['production'] ? "https://platform.ringcentral.com" : "https://platform.devtest.ringcentral.com";
+        $this->redirectUrl = $this->credentials['redirect_url'];
+        $this->initializeSDK();
+        parent::__construct();
+    }
+
+    /**
+     * @return int|string
+     */
+    public function authenticateRingCentral(): int|string
+    {
+        try {
+            $authback = $this->cacheDir . DIRECTORY_SEPARATOR . 'platform.json';
+            $cachedAuth = $this->getCachedAuth($authback);
+            if (!empty($cachedAuth['refresh_token'])) {
+                $this->platform->auth()->setData($cachedAuth);
+            }
+
+            if ($this->platform->loggedIn()) {
+                return $this->refreshToken();
+            } else {
+                return $this->loginWithJWT();
+            }
+        } catch (Exception $e) {
+            return text($e->getMessage());
+        }
+    }
+
+    /**
+     * @param string $authback
+     * @return array
+     */
+    private function getCachedAuth(string $authback): array
+    {
+        if (file_exists($authback)) {
+            $cachedAuth = file_get_contents($authback);
+            $cachedAuth = json_decode($this->crypto->decryptStandard($cachedAuth), true);
+            unlink($authback);
+// Remove cached file after reading
+            return $cachedAuth;
+        }
+        return [];
+    }
+
+    /**
+     * @return int|string
+     */
+    private function refreshToken(): int|string
+    {
+        try {
+            $this->platform->refresh();
+        } catch (Exception $e) {
+            return $this->loginWithJWT();
+        }
+        $this->setSession('sessionAccessToken', $this->platform->auth()->data());
+        $this->cacheAuthData($this->platform);
+        return 1;
+    }
+
+    /**
+     * @return int|string
+     */
+    private function loginWithJWT(): int|string
+    {
+        $jwt = trim($this->credentials['jwt'] ?? '');
+        try {
+            $this->platform->login(['jwt' => $jwt]);
+            if ($this->platform->loggedIn()) {
+                $this->setSession('sessionAccessToken', $this->platform->auth()->data());
+                $this->cacheAuthData($this->platform);
+                return 1;
+            }
+        } catch (ApiException $e) {
+            return "API Error: " . text($e->getMessage()) . " - " . text($e->getCode());
+        } catch (Exception $e) {
+            return "Error: " . text($e->getMessage());
+        }
+        return "Login with JWT failed.";
+    }
+
+    /**
+     * @param $platform
+     * @return void
+     */
+    private function cacheAuthData($platform): void
+    {
+        $data = $platform->auth()->data();
+        $encryptedData = $this->crypto->encryptStandard(json_encode($data));
+        file_put_contents($this->cacheDir . DIRECTORY_SEPARATOR . 'platform.json', $encryptedData);
+    }
+
+    /**
+     * @return void
+     * @throws Exception
+     */
+    private function initializeSDK(): void
+    {
+        if (isset($this->credentials['appKey'], $this->credentials['appSecret'])) {
+            $this->rcsdk = new SDK($this->credentials['appKey'], $this->credentials['appSecret'], $this->serverUrl, 'OpenEMR', '1.0.0');
+            $this->platform = $this->rcsdk->platform();
+        } else {
+            throw new Exception("App Key and App Secret are required to initialize SDK.");
+        }
+    }
+
+    /**
+     * @return int|string
+     */
+    public function authenticate(): int|string
+    {
+        if (empty($this->credentials['appKey'])) {
+            $this->credentials = $this->getCredentials();
+            if (empty($this->credentials['appKey'])) {
+                return 'Missing or Invalid RingCentral Credentials. Please contact your administrator.';
+                // No credentials set
+            }
+        }
+        $error = $this->authenticateRingCentral();
+        if (is_numeric($error)) {
+            return $error;
+        }
+        return $error;
+    }
+
+    /**
+     * Used by fax file drag and drop
+     *
+     * @return string
+     */
+    public function faxProcessUploads(): string
+    {
+        if (empty($_FILES['fax']) || $_FILES['fax']['error'] !== UPLOAD_ERR_OK) {
+            error_log('Error: No file uploaded or upload error.');
+            return '';
+        }
+
+        $name = basename($_FILES['fax']['name']);
+        $tmpName = $_FILES['fax']['tmp_name'];
+        $targetDir = $this->baseDir . '/send';
+        if (!file_exists($targetDir) && !mkdir($targetDir, 0777, true)) {
+            error_log('Error: Failed to create directory.');
+            return '';
+        }
+
+        $filepath = $targetDir . "/" . $name;
+        if (!move_uploaded_file($tmpName, $filepath)) {
+            error_log('Error: Failed to move uploaded file.');
+            return '';
+        }
+
+        return $filepath;
+    }
+
+    /**
+     * @param string $toPhone
+     * @param string $subject
+     * @param string $message
+     * @param string $from
+     * @return string|bool
+     */
+    public function sendSMS(string $toPhone = '', string $subject = '', string $message = '', string $from = ''): string|bool
+    {
+        $authErrorMsg = $this->authenticate();
+        if ($authErrorMsg !== 1) {
+            return text(js_escape($authErrorMsg));
+            // goes to alert
+        }
+
+        $smsNumber = $this->credentials['smsNumber'];
+        if ($smsNumber) {
+            try {
+                $this->platform->post('/account/~/extension/~/sms', [
+                    'from' => ['phoneNumber' => $smsNumber],
+                    'to' => [['phoneNumber' => $toPhone]],
+                    'text' => $message,
+                ]);
+                sleep(1);
+                // RC may only allow 1/second.
+                return true;
+            } catch (ApiException $e) {
+                return text("API Error: " . $e->getMessage() . " - " . $e->getCode());
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * @return array
+     */
+    public function getCredentials(): array
+    {
+        if (!file_exists($this->cacheDir)) {
+            mkdir($this->cacheDir, 0777, true);
+        }
+        return AppDispatch::getSetup();
+    }
+
+    /**
+     * API Endpoint for sending
+     * @return string
+     */
+    public function forwardFax(): string
+    {
+        $authErrorMsg = $this->authenticate();
+        if ($authErrorMsg !== 1) {
+            return text(js_escape($authErrorMsg)); // goes to alert
+        }
+
+        $jobId = $this->getRequest('docid');
+        $email = $this->getRequest('email');
+        $faxNumber = $this->formatPhone($this->getRequest('phone'));
+        $hasEmail = $this->validEmail($email);
+        $smtpEnabled = !empty($GLOBALS['SMTP_PASS'] ?? null) && !empty($GLOBALS["SMTP_USER"] ?? null);
+        $user = $this::getLoggedInUser();
+        $facility = substr($user['facility'], 0, 20);
+        $csid = $this->formatPhone($this->credentials['phone']);
+        $tag = xlt("Forwarded");
+        $statusMsg = xlt("Forwarding Requests") . "<br />";
+
+        if (!$hasEmail && empty($faxNumber)) {
+            return js_escape(xlt("Error: Nothing to forward. Try again."));
+        }
+
+        try {
+            // Fetch the fax message details
+            $messageDetailsResponse = $this->platform->get("/account/~/extension/~/message-store/{$jobId}");
+            $messageDetails = $messageDetailsResponse->json();
+
+            // Fetch the fax content
+            $contentUri = $messageDetails->attachments[0]->uri;
+            $apiResponse = $this->platform->get($contentUri);
+            $contentType = $apiResponse->response()->getHeader('Content-Type')[0];
+            $rawData = (string)$apiResponse->raw();
+
+            $ext = $this->getExtensionFromContentType($contentType);
+            $type = $this->getTypeFromContentType($contentType);
+            $filePath = $this->baseDir . "/send/" . ($jobId . $ext);
+
+            if (!file_exists($this->baseDir . '/send')) {
+                mkdir($this->baseDir . '/send', 0777, true);
+            }
+            file_put_contents($filePath, $rawData);
+
+            if ($hasEmail && $smtpEnabled) {
+                $statusMsg .= self::emailDocument($email, $this->getRequest('comments'), $filePath, $user) . "<br />";
+            }
+            if ($faxNumber) {
+                try {
+                    $this->sendFax(
+                        $faxNumber,
+                        $filePath,
+                        $user['username'],
+                        $jobId,
+                        $contentType
+                    );
+                    $statusMsg .= xlt("Successfully forwarded fax to") . ' ' . text($faxNumber) . "<br />";
+                } catch (Exception $e) {
+                    return js_escape('Error: ' . text($e->getMessage()));
+                }
+            }
+            unlink($filePath);
+            return js_escape($statusMsg);
+        } catch (ApiException | Exception $e) {
+            return js_escape('Error: ' . text($e->getMessage()));
+        }
+    }
+
+    /**
+     * @param $phone
+     * @param $file
+     * @param $name
+     * @param $comments
+     * @param $fileName
+     * @return bool|string
+     */
+    public function sendFax($phone = '', $file = '', $name = '', $comments = '', $fileName = null): bool|string
+    {
+        // Authenticate and refresh token if needed
+        $authErrorMsg = $this->authenticate();
+        if ($authErrorMsg !== 1) {
+            return text(js_escape($authErrorMsg)); // goes to alert
+        }
+
+        // Ensure some needed args if not past in or from API abstracted endpoint sendFax().
+        $isContent = $this->getRequest('isContent'); // remember this flag is set in patient report and not just it has content.
+        $file = $this->getRequest('file', $file); // could be content or file path.
+        $isFilePath = is_file($file);
+        $isDocuments = (int)$this->getRequest('isDocuments', 0); //from patient documents
+        $docId = $this->getRequest('docid');
+        $phone = $this->formatPhone($this->getRequest('phone', $phone));
+        $comments = trim($this->getRequest('comments', $comments));
+        $email = $this->getRequest('email');
+        $hasEmail = $this->validEmail($email);
+        $smtpEnabled = !empty($GLOBALS['SMTP_PASS'] ?? null) && !empty($GLOBALS["SMTP_USER"] ?? null);
+        $user = $this::getLoggedInUser();
+        $name = $this->getRequest('name', $name) . ' ' . $this->getRequest('surname', '');
+        $fileName = $fileName ?? pathinfo($file, PATHINFO_BASENAME);
+        // validate/format file path
+        if (is_file($file)) {
+            if (str_starts_with($file, 'file://')) {
+                $file = substr($file, 7);
+            }
+            $realPath = realpath($file);
+            if ($realPath !== false) {
+                $file = str_replace("\\", "/", $realPath);
+            } else {
+                return xlt('Error: No content');
+            }
+        }
+        // Check if the content is from patient report
+        if ($isContent) {
+            $content = $file;
+            $file = 'report-' . attr($GLOBALS['pid']) . '.pdf';
+        } else {
+            // Is it from patient documents
+            if ($isDocuments) {
+                $content = (new Document($docId))->get_data();
+            } else {
+                // Get the content of the file or the file path
+                $content = (is_file($file) && empty($content)) ? file_get_contents($file) : $file;
+            }
+            if (empty($content)) {
+                return xlt('Error: No content to send.');
+            }
+        }
+
+        // Decrypt content if needed
+        if ($this->crypto->cryptCheckStandard($content)) {
+            $content = $this->crypto->decryptStandard($content, null, 'database');
+        }
+
+        // Email the document if email is provided and SMTP is enabled.
+        // TODO: need check to ensure not from forward fax
+        $error = false;
+        if ($hasEmail && $smtpEnabled) {
+            try {
+                self::emailDocument($email, $comments, $file, $user);
+                $error = false;
+            } catch (\PHPMailer\PHPMailer\Exception $e) {
+                $error = true;
+            }
+        }
+        // Request to send the fax
+        try {
+            $this->sendFaxRequest($phone, $content, $fileName, $comments, $name);
+            // debug error log
+            error_log($phone . ' ' . $fileName . ' ' . $comments . ' ' . $name);
+            return xlt('Fax Successfully Sent') . ($error === true ? ("<br />" . xlt("Email Failed")) : '');
+        } catch (Exception $e) {
+            return 'Error: ' . text(js_escape($e->getMessage()));
+        }
+    }
+
+    /**
+     * @param $phone
+     * @param $content
+     * @param $fileName
+     * @param $comments
+     * @param $name
+     * @return void
+     * @throws Exception
+     */
+    private function sendFaxRequest($phone, $content, $fileName = '', $comments = 'No Comment', $name = ''): void
+    {
+        // Almost always $content is file content but lets check in case it is a file path
+        if (is_file($content)) {
+            $content = file_get_contents($content);
+        }
+        try {
+            $mime = FileUtils::fileGetMimeType($fileName, $content);
+            $type = $mime['type'];
+            $fileName = $mime['filePath'];
+            if (empty($type)) {
+                $type = mime_content_type($content);
+            }
+            //error_log($phone . ' ' . $fileName . ' ' . $type . ' ' . $name);
+            $request = $this->rcsdk->createMultipartBuilder()
+                ->setBody([
+                    'to' => [['phoneNumber' => $phone, 'name' => $name]],
+                    'faxResolution' => 'High',
+                    'coverPageText' => text($comments)
+                ])
+                ->add($content, $fileName, ['Content-Type' => (string)$type])
+                ->request('/account/~/extension/~/fax');
+            $this->platform->sendRequest($request);
+        } catch (ApiException $e) {
+            throw new Exception($this->handleApiException($e));
+        }
+    }
+
+    /**
+     * @param ApiException $e
+     * @return string
+     */
+    private function handleApiException(ApiException $e): string
+    {
+        $error = $e->apiResponse ? $e->apiResponse->text() : $e->getMessage();
+
+        if (stripos($error, 'invalid_grant') !== false) {
+            try {
+                $this->platform->login(['jwt' => $this->credentials['jwt']]);
+                if ($this->platform->loggedIn()) {
+                    $this->cacheAuthData($this->platform);
+                    return 'Fax Successfully Sent';
+                }
+            } catch (Exception $ex) {
+                return "Re-authentication Error: " . text($ex->getMessage());
+            }
+        }
+        return "API Error: " . text($e->getMessage()) . " - " . text($e->getCode()) . "\n" . text(json_encode($e->apiResponse ? $e->apiResponse->json() : [], JSON_PRETTY_PRINT));
+    }
+
+    /**
+     * @return string
+     */
+    public function getStoredDoc(): string
+    {
+        $docuri = $this->getRequest('docuri');
+        $authErrorMsg = $this->authenticate();
+        if ($authErrorMsg !== 1) {
+            return text(js_escape($authErrorMsg));
+            // goes to alert
+        }
+
+        try {
+            $apiResponse = $this->platform->get($docuri);
+        } catch (ApiException $e) {
+            return "Error: Retrieving Fax: " . text($e->getMessage() . $e->apiResponse()->request()->getUri()->__toString());
+        }
+
+        $contentType = $apiResponse->response()->getHeader('Content-Type')[0];
+        $rawData = (string)$apiResponse->raw();
+        if ($contentType == 'application/pdf') {
+            return 'data:application/pdf;base64,' . rawurlencode(base64_encode($rawData));
+        } elseif ($contentType == 'image/tiff') {
+            return 'data:image/tiff;base64,' . rawurlencode(base64_encode($rawData));
+        } else {
+            return $rawData;
+        }
+    }
+
+    /**
+     * @param string $contentType
+     * @return string
+     */
+    public function getExtensionFromContentType(string $contentType): string
+    {
+        switch ($contentType) {
+            case 'application/pdf':
+                return 'pdf';
+            case 'text/plain':
+                return 'txt';
+            case 'image/tiff':
+                return 'tiff';
+            case 'image/jpeg':
+                return 'jpeg';
+            case 'image/jpg':
+                return 'jpg';
+            case 'image/gif':
+                return 'gif';
+            case 'image/png':
+                return 'png';
+            case 'application/xml':
+                return 'xml';
+            case 'audio/wav':
+            case 'audio/x-wav':
+                return 'wav';
+            default:
+                return 'application/pdf';
+        }
+    }
+
+    /**
+     * @param string $contentType
+     * @return string
+     */
+    private function getTypeFromContentType(string $contentType): string
+    {
+        switch ($contentType) {
+            case 'application/pdf':
+            case 'image/tiff':
+                return 'Fax';
+            case 'audio/wav':
+            case 'audio/x-wav':
+                return 'Audio';
+            default:
+                return 'Text';
+        }
+    }
+
+    /**
+     * @param string $content
+     * @return void
+     */
+    public function disposeDoc($content = ''): void
+    {
+        $where = $this->getSession('where');
+        if (file_exists($where)) {
+            ob_clean();
+            header("Cache-Control: public");
+            header("Content-Description: File Transfer");
+            header("Content-Disposition: attachment; filename=" . basename($where));
+            header("Content-Type: application/download");
+            header("Content-Transfer-Encoding: binary");
+            header('Content-Length: ' . filesize($where));
+            readfile($where);
+            unlink($where);
+            exit;
+        }
+        die(xlt('Problem with download. Use browser back button'));
+    }
+
+    /**
+     * @return string
+     */
+    public function viewFax(): string
+    {
+        $authErrorMsg = $this->authenticate();
+        if ($authErrorMsg !== 1) {
+            return text(js_escape($authErrorMsg)); // goes to alert
+        }
+
+        $jobId = $this->getRequest('docid');
+        $isDownload = $this->getRequest('download') == 'true';
+        $isDelete = $this->getRequest('delete') == 'true';
+
+        $messageStoreDir = $this->baseDir;
+        if (!file_exists($messageStoreDir)) {
+            mkdir($messageStoreDir, 0777, true);
+        }
+
+        try {
+            // Fetch the message details
+            $messageDetailsResponse = $this->platform->get("/account/~/extension/~/message-store/{$jobId}");
+            if ($messageDetailsResponse->response()->getStatusCode() !== 200) {
+                return json_encode(['error' => "Error: Retrieving Fax: " . $messageDetailsResponse->response()->getReasonPhrase()]);
+            }
+            $messageDetails = $messageDetailsResponse->json();
+
+            if ($isDelete) {
+                // Delete the message
+                $this->platform->delete("/account/~/extension/~/message-store/{$jobId}");
+                return json_encode('success');
+            }
+
+            $contentUri = $messageDetails->attachments[0]->uri;
+            $apiResponse = $this->platform->get($contentUri);
+            $contentType = $apiResponse->response()->getHeader('Content-Type')[0];
+            $rawData = (string)$apiResponse->raw();
+
+            if ($isDownload) {
+                $filePath = $this->saveFaxToFile($rawData, $jobId, $contentType);
+                $this->setSession('where', $filePath);
+                return text(json_encode(['base64' => base64_encode($rawData), 'mime' => $contentType, 'path' => $filePath]));
+            }
+            return text(json_encode(['base64' => base64_encode($rawData), 'mime' => $contentType]));
+        } catch (ApiException $e) {
+            return text(json_encode(['error' => "Error: Retrieving Fax: " . $e->getMessage()]));
+        }
+    }
+
+    /**
+     * @param string $jobId
+     * @return mixed
+     */
+    public function fetchFaxFromQueue(string $jobId): mixed
+    {
+        $authErrorMsg = $this->authenticate();
+        if ($authErrorMsg !== 1) {
+            return text(js_escape($authErrorMsg)); // goes to alert
+        }
+
+        try {
+            $apiResponse = $this->platform->get("/account/~/extension/~/message-store/{$jobId}/content");
+            $contentType = $apiResponse->response()->getHeader('Content-Type')[0];
+            $rawData = (string)$apiResponse->raw();
+
+            return [
+                'contentType' => $contentType,
+                'data' => base64_encode($rawData)
+            ];
+        } catch (ApiException $e) {
+            return text(json_encode(['error' => "API Error: " . $e->getMessage()]));
+        } catch (Exception $e) {
+            return text(json_encode(['error' => "Error: " . $e->getMessage()]));
+        }
+    }
+
+    /**
+     * @param string $data
+     * @param string $contentType
+     * @return string
+     */
+    private function formatFaxDataUrl(string $data, string $contentType): string
+    {
+        switch ($contentType) {
+            case 'application/pdf':
+                return 'data:application/pdf;base64,' . base64_encode($data);
+            case 'image/tiff':
+            case 'image/tif':
+                return 'data:image/tiff;base64,' . base64_encode($data);
+            default:
+                return 'data:text/plain;base64,' . base64_encode($data);
+        }
+    }
+
+    /**
+     * @param string $data
+     * @param string $jobId
+     * @param string $contentType
+     * @return string
+     */
+    private function saveFaxToFile(string $data, string $jobId, string $contentType): string
+    {
+        $fileExtension = $this->getFileExtension($contentType);
+        $fileName = "Fax_{$jobId}." . $fileExtension;
+        $filePath = $this->baseDir . DIRECTORY_SEPARATOR . $fileName;
+
+        file_put_contents($filePath, $data);
+
+        return $filePath;
+    }
+
+    /**
+     * @param string $contentType
+     * @return string
+     */
+    private function getFileExtension(string $contentType): string
+    {
+        switch ($contentType) {
+            case 'application/pdf':
+                return 'pdf';
+            case 'image/tiff':
+            case 'image/tif':
+                return 'tiff';
+            default:
+                return 'txt';
+        }
+    }
+
+    /**
+     * @param $encodedFax
+     * @return string
+     * @throws Exception
+     */
+    public function formatFax($encodedFax): string
+    {
+        $control = new HandleImageService();
+        $formatted_document = $control->convertImageToPdf($encodedFax, '');
+
+        return $formatted_document ? base64_encode($formatted_document) : false;
+    }
+
+    /**
+     * @return string
+     */
+    public function disposeDocument(): string
+    {
+        $response = ['success' => false, 'message' => '', 'url' => ''];
+        $where = $this->getRequest('file_path') ?? $this->getSession('where');
+
+        if (empty($where)) {
+            die(xlt('Problem with download. Use browser back button'));
+        }
+
+        $content = $this->getRequest('content', '');
+        $action = $this->getRequest('action');
+
+        if ($action == 'download') {
+            $this->sendFile($where);
+            sleep(2);
+            unlink($where);
+            exit;
+        }
+
+        if (!empty($content) && $action == 'setup') {
+            $decodedContent = base64_decode($content);
+            if (file_put_contents($where, $decodedContent) !== false) {
+                $response['success'] = true;
+                $response['url'] = $where;
+            } else {
+                $response['message'] = 'Failed to write file';
+            }
+        } elseif ($action == 'setup') {
+            $response['success'] = true;
+            $response['url'] = $where;
+        }
+
+        return json_encode($response);
+    }
+
+    /**
+     * @param string $filePath
+     * @return void
+     */
+    private function sendFile(string $filePath): void
+    {
+        ob_end_clean();
+        header("Cache-Control: public");
+        header("Content-Description: File Transfer");
+        header("Content-Disposition: attachment; filename=" . basename($filePath));
+        header("Content-Type: application/pdf");
+        header("Content-Transfer-Encoding: binary");
+        header('Content-Length: ' . filesize($filePath));
+
+        readfile($filePath);
+        exit;
+    }
+
+    /**
+     * @param string $messageId
+     * @return string
+     */
+    public function downloadFax(string $messageId): string
+    {
+        $authErrorMsg = $this->authenticate();
+        if ($authErrorMsg !== 1) {
+            return text(js_escape($authErrorMsg)); // goes to alert
+        }
+
+        try {
+            $response = $this->platform->get("/account/~/extension/~/message-store/{$messageId}/content");
+            $contentType = $response->response()->getHeader('Content-Type')[0];
+            $fileExtension = $this->getFileExtension($contentType);
+            $fileName = "fax_{$messageId}." . $fileExtension;
+
+            // Save the file locally
+            $filePath = $this->cacheDir . DIRECTORY_SEPARATOR . $fileName;
+            file_put_contents($filePath, $response->raw());
+
+            // Prepare the file for download
+            header('Content-Description: File Transfer');
+            header('Content-Type: ' . $contentType);
+            header('Content-Disposition: attachment; filename="' . basename($filePath) . '"');
+            header('Content-Transfer-Encoding: binary');
+            header('Expires: 0');
+            header('Cache-Control: must-revalidate');
+            header('Pragma: public');
+            header('Content-Length: ' . filesize($filePath));
+            readfile($filePath);
+
+            // Optionally, you can delete the file after download
+            unlink($filePath);
+
+            exit; // Stop further script execution
+        } catch (ApiException $e) {
+            return text(json_encode(['error' => "API Error: " . $e->getMessage()]));
+        } catch (Exception $e) {
+            return text(json_encode(['error' => "Error: " . $e->getMessage()]));
+        }
+    }
+
+    /**
+     * @return string
+     */
+    public function getUser(): string
+    {
+        $id = $this->getRequest('uid');
+        $query = "SELECT * FROM users WHERE id = ?";
+        $result = sqlStatement($query, [$id]);
+        $u = sqlFetchArray($result);
+        return json_encode([$u['fname'], $u['lname'], $u['fax']]);
+    }
+
+    /**
+     * @return string
+     */
+    public function getNotificationLog(): string
+    {
+        $type = $this->getRequest('type');
+        $fromDate = $this->getRequest('datefrom');
+        $toDate = $this->getRequest('dateto');
+        try {
+            $query = "SELECT notification_log.* FROM notification_log WHERE notification_log.dSentDateTime > ? AND notification_log.dSentDateTime < ?";
+            $res = sqlStatement($query, [$fromDate, $toDate]);
+            $responseMsg = '';
+            while ($nrow = sqlFetchArray($res)) {
+                $adate = ($nrow['pc_eventDate'] . '::' . $nrow['pc_startTime']);
+                $pinfo = str_replace("|||", " ", $nrow['patient_info']);
+                $msg = text($nrow["message"]);
+                $responseMsg .= "<tr><td>" . text($nrow["pc_eid"]) . "</td><td>" . text($nrow["dSentDateTime"]) . "</td><td>" . text($adate) . "</td><td>" . text($pinfo) . "</td><td>" . text($msg) . "</td></tr>";
+            }
+        } catch (\Exception $e) {
+            return 'Error: ' . text($e->getMessage()) . PHP_EOL;
+        }
+
+        return $responseMsg;
+    }
+
+    /**
+     * @return string
+     */
+    public function getCallLogs(): string
+    {
+        $fromDate = $this->getRequest('datefrom');
+        $toDate = $this->getRequest('dateto');
+        $authErrorMsg = $this->authenticate();
+        if ($authErrorMsg !== 1) {
+            return text(js_escape($authErrorMsg));
+            // goes to alert
+        }
+
+        try {
+            $pageCount = 1;
+            $recordCountPerPage = 100;
+            $timePerCallLogRequest = 10;
+            $flag = true;
+            $timeFrom = '00:00:00.000Z';
+            $timeTo = '23:59:59.000Z';
+            $responseMsg = "";
+            while ($flag) {
+                $start = microtime(true);
+                $dateFrom = $fromDate . 'T' . $timeFrom;
+                $dateTo = $toDate . 'T' . $timeTo;
+                $apiResponse = $this->platform->get('/account/~/extension/~/call-log', [
+                    'dateFrom' => $dateFrom,
+                    'dateTo' => $dateTo,
+                    'perPage' => 500,
+                    'page' => $pageCount
+                ]);
+                foreach ($apiResponse->json()->records as $value) {
+                    $responseMsg .= "<tr><td>" . text(str_replace(["T", "Z"], " ", $value->startTime)) . "</td><td>" . text($value->type) . "</td><td>" . text($value->from->name) . "</td><td>" . text($value->to->name) . "</td><td>" . text($value->action) . "</td><td>" . text($value->result) . "</td><td>" . text($value->message->id) . "</td></tr>";
+                }
+
+                $end = microtime(true);
+                $time = ($end - $start);
+                if (isset($apiResponse->json()->navigation->nextPage)) {
+                    if ($time < $timePerCallLogRequest) {
+                        sleep($timePerCallLogRequest - $time);
+                        sleep(5);
+                        $pageCount++;
+                    }
+                } else {
+                    $flag = false;
+                }
+            }
+        } catch (ApiException $e) {
+            return xlt('HTTP Error') . ': ' . text($e->getMessage()) . PHP_EOL;
+        }
+
+        return $responseMsg;
+    }
+
+    /**
+     * @return false|string
+     */
+    public function getPending(): false|string
+    {
+        $authErrorMsg = $this->authenticate();
+        if ($authErrorMsg !== 1) {
+            return json_encode(['error' => js_escape($authErrorMsg)]);
+        }
+
+        $dateFrom = $this->getRequest('datefrom');
+        $dateTo = $this->getRequest('dateto');
+        $serviceType = $this->getRequest('type', '');
+
+        try {
+            $messageStoreDir = $this->baseDir;
+
+            if (!file_exists($messageStoreDir) && !mkdir($messageStoreDir, 0777, true) && !is_dir($messageStoreDir)) {
+                throw new \RuntimeException(sprintf('Directory "%s" was not created', $messageStoreDir));
+            }
+
+            $dateFrom .= 'T00:00:01.000Z';
+            $dateTo .= 'T23:59:59.000Z';
+
+            $messageStoreList = $this->platform->get('/account/~/extension/~/message-store', [
+                'dateFrom' => $dateFrom,
+                'dateTo' => $dateTo,
+            ])->json()->records;
+
+            $responseMsg = $this->processMessageStoreList($messageStoreList, $serviceType);
+        } catch (ApiException $e) {
+            $responseMsg = "<tr><td>" . text($e->getMessage()) . " : " . xlt('Ensure account credentials are correct.') . "</td></tr>";
+            return json_encode(['error' => $responseMsg]);
+        }
+
+        return json_encode($responseMsg ?: [xlt("Nothing to report"), xlt("Nothing to report"), xlt("Nothing to report")]);
+    }
+
+    private function processMessageStoreList($messageStoreList, $serviceType): array
+    {
+        $responseMsg = [];
+        foreach ($messageStoreList as $messageStore) {
+            if (property_exists($messageStore, 'attachments')) {
+                foreach ($messageStore->attachments as $attachment) {
+                    $id = attr($attachment->id);
+                    $uri = $attachment->uri;
+                    $to = $messageStore->to[0]->name . " " . $messageStore->to[0]->phoneNumber;
+                    $from = $messageStore->from->name . " " . $messageStore->from->phoneNumber;
+                    $status = $messageStore->messageStatus . $messageStore->from->faxErrorCode;
+                    $faxFormattedDate = date('M j, Y g:i:sa T', strtotime($messageStore->creationTime));
+                    $updateDate = date('M j Y g:i:sa T', strtotime($messageStore->lastModifiedTime));
+
+                    $links = $this->generateActionLinks($id, $uri);
+                    $checkbox = "<input type='checkbox' class='delete-fax-checkbox' value='" . attr($id) . "'>";
+                    $type = strtolower($messageStore->type);
+                    $direction = strtolower($messageStore->direction);
+                    $readStatus = $messageStore->readStatus;
+                    if ($type === "sms") {
+                        $messageText = $this->getMessageContent($uri);
+                        $responseMsg[2] .= "<tr><td>" . text($faxFormattedDate) . "</td><td>" . text($messageStore->type) . "</td><td>" . text($from) . "</td><td>" . text($to) . "</td><td>" . text($status) . "</td><td><span class='$id'>" . text(substr($messageText, 0, 30)) . "</span><div class='d-none $id'>" . text($messageText) . "</div></td><td class='btn-group'>" . attr($links['sms']) . "</td><td class='text-center'>" . $checkbox . "</td></tr>";
+                    } elseif ($direction === "inbound" && $type === $serviceType) {
+                        $status = $messageStore->to[0]->faxErrorCode ?: $messageStore->messageStatus;
+                        $responseMsg[0] .= "<tr><td>" . text($faxFormattedDate) . "</td><td>" . text($updateDate) . "</td><td>" . text($messageStore->faxPageCount) . "</td><td>" . text($from) . "</td><td>" . text($messageStore->subject) . "</td><td>" . text($status) . "</td><td class='text-left'>" . $links['inbound'] . "</td><td class='text-center'>" . $checkbox . "</td></tr>";
+                    } elseif ($direction === "outbound" && $type === $serviceType) {
+                        $status = $messageStore->to[0]->faxErrorCode ?: $messageStore->messageStatus;
+                        $responseMsg[1] .= "<tr><td>" . text($faxFormattedDate) . "</td><td>" . text($updateDate) . "</td><td>" . text($messageStore->faxPageCount) .
+                            "</td><td>" . text($from) . "</td><td>" . text($to) . "</td><td>" . text($status) . "</td><td>" . $links['outbound'] . "</td><td class='text-center'>" . $checkbox . "</td></tr>";
+                    }
+                }
+            }
+        }
+
+        return $responseMsg;
+    }
+
+    private function generateActionLinks($id, $uri): array
+    {
+        $patientLink = "<a role='button' href='javascript:void(0)' onclick=\"createPatient(event, " . attr_js($id) . ", " . attr_js($id) . ", " . attr_js(json_encode([])) . ")\"> <i class='fa fa-chart-simple mr-2' title='" . xla("Chart fax or Create patient and chart fax to documents.") . "'></i></a>";
+        $messageLink = "<a role='button' href='javascript:void(0)' onclick=\"notifyUser(event, " . attr_js($id) . ", " . attr_js($id) . ", " . attr_js(0) . ")\"> <i class='fa fa-paper-plane mr-2' title='" . xla("Notify a user and attach this fax to message.") . "'></i></a>";
+        $downloadLink = "<a role='button' href='javascript:void(0)' onclick=\"getDocument(event, null, " . attr_js($id) . ", 'true')\"> <i class='fa fa-file-download mr-2' title='" . xla("Download and delete fax") . "'></i></a>";
+        $viewLink = "<a role='button' href='javascript:void(0)' onclick=\"getDocument(event, null, " . attr_js($id) . ", 'false')\"> <i class='fa fa-file-pdf mr-2' title='" . xla("View fax document") . "'></i></a>";
+        $deleteLink = "<a role='button' href='javascript:void(0)' onclick=\"getDocument(event, null, " . attr_js($id) . ", 'false', 'true')\"> <i class='text-danger fa fa-trash mr-2' title='" . xla("Delete this fax document") . "'></i></a>";
+        $forwardLink = "<a role='button' href='javascript:void(0)' onclick=\"forwardFax(event, " . attr_js($id) . ")\"> <i class='fa fa-forward mr-2' title='" . xla("Forward fax to new fax recipient or email attachment.") . "'></i></a>";
+
+        $vtoggle = "<a href='javascript:' onclick=messageShow(" . attr_js($id) . ")><span class='mx-1 fa fa-eye-slash fa-1x'></span></a>";
+        $vreply = "<a href='javascript:' onclick=messageReply(" . attr_js($id) . ")><span class='mx-1 fa fa-reply'></span></a>";
+
+        return [
+            'sms' => $vtoggle . $vreply,
+            'inbound' => $patientLink . $messageLink . $forwardLink . $viewLink . $downloadLink . $deleteLink,
+            'outbound' => $viewLink . $downloadLink . $deleteLink
+        ];
+    }
+
+    /**
+     * @param array $responseMsg
+     * @param       $messageStore
+     * @param       $attachment
+     * @return void
+     */
+    private function formatMessageStore(array &$responseMsg, $messageStore, $attachment): void
+    {
+        $id = $attachment->id;
+        $uri = $attachment->uri;
+        $to = $messageStore->to[0]->name . " " . $messageStore->to[0]->phoneNumber;
+        $from = $messageStore->from->name . " " . $messageStore->from->phoneNumber;
+        $errors = $messageStore->to[0]->faxErrorCode ? "why: " . $messageStore->to[0]->faxErrorCode : $messageStore->from->faxErrorCode;
+        $status = $messageStore->messageStatus . " " . $errors;
+        $patientLink = "<a role='button' href='javascript:void(0)' onclick=\"createPatient(event, " . attr_js($id) . ", " . attr_js($id) . ", " . attr_js(json_encode($parse ?? [])) . ")\"> <i class='fa fa-chart-simple mr-2' title='" . xla("Chart fax or Create patient and chart fax to documents.") . "'></i></a>";
+        $messageLink = "<a role='button' href='javascript:void(0)' onclick=\"notifyUser(event, " . attr_js($id) . ", " . attr_js($id) . ", " . attr_js(($pid_assumed ?? 0)) . ")\"> <i class='fa fa-paper-plane mr-2' title='" . xla("Notify a user and attach this fax to message.") . "'></i></a>";
+        $downloadLink = "<a role='button' href='javascript:void(0)' onclick=\"getDocument(event, null, " . attr_js($id) . ", 'true')\"> <i class='fa fa-file-download mr-2' title='" . xla("Download and delete fax") . "'></i></a>";
+        $viewLink = "<a role='button' href='javascript:void(0)' onclick=\"getDocument(event, null, " . attr_js($id) . ", 'false')\"> <i class='fa fa-file-pdf mr-2' title='" . xla("View fax document") . "'></i></a>";
+        $deleteLink = "<a role='button' href='javascript:void(0)' onclick=\"getDocument(event, null, " . attr_js($id) . ", 'false', 'true')\"> <i class='text-danger fa fa-trash mr-2' title='" . xla("Delete this fax document") . "'></i></a>";
+        $forwardLink = "<a role='button' href='javascript:void(0)' onclick=\"forwardFax(event, " . attr_js($id) . ")\"> <i class='fa fa-forward mr-2' title='" . xla("Forward fax to new fax recipient or email attachment.") . "'></i></a>";
+
+        $faxFormattedDate = date('M j, Y g:i:sa T', strtotime($messageStore->lastModifiedTime));
+        $docLen = text(round(1024 / 1024, 2)) . "KB"; // todo add length
+        $responseMsg[0] .= "<tr><td>" . text($faxFormattedDate) . "</td><td>" . text($from) . "</td><td>" . text('todo: add caller id') . "</td><td>" . text($to) . "</td><td>" . text($messageStore->PagesReceived) . "</td><td>" . text($docLen) . "</td><td class='text-left'>" . /*$detailLink .*/
+            "</td><td class='text-center'>" . text($pid_assumed ?? '') . "</td><td class='text-left'>" . $patientLink . $messageLink . $forwardLink . $viewLink . $downloadLink . $deleteLink . "</td></tr>";
+        //$responseMsg[0] .= $form;
+        $aUrl = "<a href='#' onclick=getDocument(event," . attr_js($uri) . "," . attr_js($id) . ",'true')>" . text($id) . " <span class='fa fa-download'></span></a></br>";
+        $vUrl = "<a href='#' onclick=getDocument(event," . attr_js($uri) . "," . attr_js($id) . ",'false')> <span class='fa fa-file-pdf-o'></span></a></br>";
+        if ($status != 'failed' && $this->formatPhone($this->credentials['smsNumber']) != $messageStore->from) {
+            $vreply = "<a href='javaScript:' onclick=messageReply(" . attr_js($messageStore->from) . ")><span class='mx-1 fa fa-reply'></span></a>";
+        } else {
+            $vreply = "<a href='#' title='SMS failure'> <span class='fa fa-file-pdf text-danger'></span></a></br>";
+        }
+        $row = "<tr>
+                <td>" . text(str_replace(["T", "Z"], " ", $messageStore->lastModifiedTime)) . "</td>
+                <td>" . text($messageStore->type) . "</td>
+                <td>" . text($from) . "</td>
+                <td>" . text($to) . "</td>
+                <td>" . text($status) . "</td>
+                <td>" . ($aUrl) . "</td>
+                <td>" . ($vUrl) . "</td>";
+        if (strtolower($messageStore->type) === "sms") {
+            $row .= "<td>" . ($vreply) . "</td>";
+        }
+
+        $row .= "</tr>";
+        if (strtolower($messageStore->type) === "sms") {
+            $responseMsg[2] .= $row;
+            // sms
+        } elseif (strtolower($messageStore->direction) === "inbound") {
+            $responseMsg[0] .= $row;
+// in fax
+        } else {
+            $responseMsg[1] .= $row;
+// out fax
+        }
+    }
+
+    /**
+     * @param $number
+     * @return string
+     */
+    public function formatPhone($number): string
+    {
+        // this is u.s only. need E-164
+        $n = preg_replace('/[^0-9]/', '', $number);
+        if (stripos($n, '1') === 0) {
+            $n = '+' . $n;
+        } else {
+            $n = '+1' . $n;
+        }
+        return $n;
+    }
+
+    /**
+     * @return string|void
+     */
+    public function getMessage()
+    {
+        $authErrorMsg = $this->authenticate();
+        if ($authErrorMsg !== 1) {
+            return text(js_escape($authErrorMsg));
+            // goes to alert
+        }
+
+        try {
+            $messageStoreDir = $this->baseDir;
+            if (!file_exists($messageStoreDir)) {
+                mkdir($messageStoreDir, 0777, true);
+            }
+
+            $messageStoreList = $this->platform->get('/account/~/extension/~/message-store', [
+                'messageType' => "",
+                'dateFrom' => '2018-05-01'
+            ])->json()->records;
+            $timePerMessageStore = 6;
+            $responseMsgs = "";
+            foreach ($messageStoreList as $messageStore) {
+                if (property_exists($messageStore, 'attachments')) {
+                    foreach ($messageStore->attachments as $attachment) {
+                        $id = $attachment->id;
+                        $uri = $attachment->uri;
+                        try {
+                            $apiResponse = $this->platform->get($uri);
+                        } catch (ApiException $e) {
+                            $responseMsgs .= "<tr><td>Errors: " . text($e->getMessage()) . $e->apiResponse()->request()->getUri()->__toString() . "</td></tr>";
+                            continue;
+                        }
+
+                        $ext = $this->getExtensionFromContentType($apiResponse->response()->getHeader('Content-Type')[0]);
+                        $type = $this->getTypeFromContentType($apiResponse->response()->getHeader('Content-Type')[0]);
+                        $start = microtime(true);
+                        file_put_contents("{$messageStoreDir}/{$type}_{$id}.{$ext}", $apiResponse->raw());
+                        $responseMsgs .= "<tr><td>" . $messageStore->creationTime . "</td><td>" . $messageStore->type . "</td><td>" . $messageStore->from->name . "</td><td>" . $messageStore->to->name . "</td><td>" . $messageStore->availability . "</td><td>" . $messageStore->messageStatus . "</td><td>" . $messageStore->message->id . "</td></tr>";
+                        $end = microtime(true);
+                        $time = ($end - $start);
+                        if ($time < $timePerMessageStore) {
+                            sleep($timePerMessageStore - $time);
+                        }
+                    }
+                } else {
+                    echo xlt("Does not have messages") . PHP_EOL;
+                }
+            }
+        } catch (ApiException $e) {
+            echo "<tr><td>Error: " . text($e->getMessage() . $e->apiResponse()->request()->getUri()->__toString()) . "</td></tr>";
+        }
+
+        exit;
+    }
+
+    /**
+     * @return string|null
+     */
+    protected function index(): ?string
+    {
+        if (!$this->getSession('pid', '')) {
+            $pid = $this->getRequest('patient_id');
+            $this->setSession('pid', $pid);
+        }
+
+        return null;
+    }
+
+    /**
+     * @return mixed
+     */
+    public function sendEmail(): mixed
+    {
+        return null;
+    }
+
+    /**
+     * @return string|bool
+     */
+    public function fetchReminderCount(): string|bool
+    {
+        $authErrorMsg = $this->authenticate();
+        if ($authErrorMsg !== 1) {
+            return text(js_escape($authErrorMsg)); // goes to alert
+        }
+
+        if (self::$_apiModule == 'sms') {
+            return '0';
+        }
+        try {
+            $platform = $this->rcsdk->platform();
+            $response = $platform->get('/restapi/v1.0/account/~/extension/~/message-store', [
+                'messageType' => 'Fax',
+                'direction' => 'Inbound',
+                'availability' => 'Alive'
+            ]);
+            $json = $response->json();
+            return (string) text(count($json->records));
+        } catch (Exception $e) {
+            error_log('Error fetching incoming faxes: ' . text($e->getMessage()));
+            return false;
+        }
+    }
+
+    /**
+     * @param $pid
+     * @param $jobId
+     * @param $fileName
+     * @return string
+     */
+    public function chartFaxDocument($pid, $jobId, $fileName = null): string
+    {
+        $authErrorMsg = $this->authenticate();
+        if ($authErrorMsg !== 1) {
+            return text(js_escape($authErrorMsg)); // goes to alert
+        }
+
+        // Determine the category ID
+        $catid = sqlQuery("SELECT id FROM `categories` WHERE `name` = 'FAX'")['id'] ?? sqlQuery("SELECT id FROM `categories` WHERE `name` = 'Medical Record'")['id'];
+
+        try {
+            // Fetch the fax message details
+            $messageDetailsResponse = $this->platform->get("/account/~/extension/~/message-store/{$jobId}");
+            $messageDetails = $messageDetailsResponse->json();
+
+            // Fetch the fax content
+            $contentUri = $messageDetails->attachments[0]->uri;
+            $apiResponse = $this->platform->get($contentUri);
+            $contentType = $apiResponse->response()->getHeader('Content-Type')[0];
+            $rawData = (string)$apiResponse->raw();
+
+            // Determine file extension and file name
+            $ext = $this->getExtensionFromContentType($contentType);
+            $fileName = $fileName ?? xlt("fax") . '_' . text($jobId) . $ext;
+            $content = $rawData;
+
+            // Create a new document and save it
+            $document = new Document();
+            $result = $document->createDocument($pid, $catid, $fileName, $contentType, $content);
+
+            return $result ? xlt("Error: Failed to save document. Category Fax") : xlt("Chart Success");
+        } catch (ApiException $e) {
+            return json_encode(['error' => "Error: Retrieving Fax: " . text($e->getMessage())]);
+        } catch (Exception $e) {
+            return json_encode(['error' => "Error: " . text($e->getMessage())]);
+        }
+    }
+
+    /**
+     * @param       $email
+     * @param       $body
+     * @param       $file
+     * @param array $user
+     * @return string
+     * @throws \PHPMailer\PHPMailer\Exception
+     */
+    public static function emailDocument($email, $body, $file, array $user = []): string
+    {
+        $from_name = ($user['fname'] ?? '') . ' ' . ($user['lname'] ?? '');
+        $desc = xlt("Comment") . ":\n" . text($body) . "\n" . xlt("This email has an attached fax document.");
+        $mail = new MyMailer();
+        $from_name = text($from_name);
+        $from = $GLOBALS["practice_return_email_path"];
+        $mail->AddReplyTo($from, $from_name);
+        $mail->SetFrom($from, $from);
+        $mail->AddAddress($email, $email);
+        $mail->Subject = xlt("Forwarded Fax Document");
+        $mail->Body = $desc;
+        $mail->AddAttachment($file);
+
+        return $mail->Send() ? xlt("Email successfully sent.") : xlt("Error: Email failed") . text($mail->ErrorInfo);
+    }
+}
diff --git a/interface/modules/custom_modules/oe-module-faxsms/src/Controller/TwilioSMSClient.php b/interface/modules/custom_modules/oe-module-faxsms/src/Controller/TwilioSMSClient.php
index 276b9fdce9a..a8b6db5b0db 100644
--- a/interface/modules/custom_modules/oe-module-faxsms/src/Controller/TwilioSMSClient.php
+++ b/interface/modules/custom_modules/oe-module-faxsms/src/Controller/TwilioSMSClient.php
@@ -54,7 +54,7 @@ public function fetchSMSFilteredList($dateFrom, $dateTo)
      * @param $uiDateRangeFlag
      * @return false|string|null
      */
-    public function fetchSMSList($uiDateRangeFlag = true)
+    public function fetchSMSList($uiDateRangeFlag = true): false|string|null
     {
         return $this->_getPending($uiDateRangeFlag);
     }
@@ -62,7 +62,7 @@ public function fetchSMSList($uiDateRangeFlag = true)
     /**
      * @return array|mixed
      */
-    public function getCredentials()
+    public function getCredentials(): mixed
     {
         $credentials = appDispatch::getSetup();
         $this->accountSID = $credentials['username'] ?? '';
@@ -77,16 +77,9 @@ public function getCredentials()
         return $credentials;
     }
 
-    /**
-     * @param $tophone
-     * @param $subject
-     * @param $message
-     * @param $from
-     * @return mixed
-     */
-    public function sendSMS($tophone = '', $subject = '', $message = '', $from = ''): mixed
+    public function sendSMS($toPhone = '', $subject = '', $message = '', $from = ''): mixed
     {
-        $tophone = $tophone ?: $this->getRequest('phone');
+        $toPhone = $toPhone ?: $this->getRequest('phone');
         $from = $from ?: $this->getRequest('from');
         $message = $message ?: $this->getRequest('comments');
 
@@ -95,11 +88,11 @@ public function sendSMS($tophone = '', $subject = '', $message = '', $from = '')
         } else {
             $from = $this->formatPhone($from);
         }
-        $tophone = $this->formatPhone($tophone);
+        $toPhone = $this->formatPhone($toPhone);
         try {
             $twilio = new Client($this->appKey, $this->appSecret, $this->sid);
             $message = $twilio->messages->create(
-                $tophone,
+                $toPhone,
                 array(
                     "body" => text($message),
                     "from" => attr($from)
diff --git a/interface/modules/custom_modules/oe-module-faxsms/version.php b/interface/modules/custom_modules/oe-module-faxsms/version.php
index 949c3471ca3..b6b0cc44cfb 100644
--- a/interface/modules/custom_modules/oe-module-faxsms/version.php
+++ b/interface/modules/custom_modules/oe-module-faxsms/version.php
@@ -7,5 +7,5 @@
  * */
 
 $v_major = '4';
-$v_minor = '0';
+$v_minor = '1';
 $v_patch = '0';
diff --git a/src/Common/Utils/FileUtils.php b/src/Common/Utils/FileUtils.php
index 87a172c15ef..2dd9041eebc 100644
--- a/src/Common/Utils/FileUtils.php
+++ b/src/Common/Utils/FileUtils.php
@@ -7,6 +7,7 @@
  * @link      https://www.open-emr.org
  * @author    Kevin McCormick Longview, Texas
  * @author    Stephen Nielson <snielson@discoverandchange.com>
+ * @author    Jerry Padgett <sjpadgett@gmail.com>
  * @copyright Copyright (c) Kevin McCormick Longview, Texas
  * @copyright Copyright (c) 2023 Discover and Change, Inc. <snielson@discoverandchange.com>
  * @license   https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
@@ -16,6 +17,52 @@
 
 class FileUtils
 {
+    /**
+     * Map of file extensions to MIME types.
+     */
+    private static array $mimeTypes = [
+        'txt' => 'text/plain',
+        'htm' => 'text/html',
+        'html' => 'text/html',
+        'php' => 'text/html',
+        'css' => 'text/css',
+        'js' => 'application/javascript',
+        'json' => 'application/json',
+        'xml' => 'application/xml',
+        'swf' => 'application/x-shockwave-flash',
+        'flv' => 'video/x-flv',
+        'png' => 'image/png',
+        'jpe' => 'image/jpeg',
+        'jpeg' => 'image/jpeg',
+        'jpg' => 'image/jpeg',
+        'gif' => 'image/gif',
+        'bmp' => 'image/bmp',
+        'ico' => 'image/vnd.microsoft.icon',
+        'tiff' => 'image/tiff',
+        'tif' => 'image/tiff',
+        'svg' => 'image/svg+xml',
+        'svgz' => 'image/svg+xml',
+        'zip' => 'application/zip',
+        'rar' => 'application/x-rar-compressed',
+        'exe' => 'application/x-msdownload',
+        'msi' => 'application/x-msdownload',
+        'cab' => 'application/vnd.ms-cab-compressed',
+        'mp3' => 'audio/mpeg',
+        'qt' => 'video/quicktime',
+        'mov' => 'video/quicktime',
+        'pdf' => 'application/pdf',
+        'psd' => 'image/vnd.adobe.photoshop',
+        'ai' => 'application/postscript',
+        'eps' => 'application/postscript',
+        'ps' => 'application/postscript',
+        'doc' => 'application/msword',
+        'rtf' => 'application/rtf',
+        'xls' => 'application/vnd.ms-excel',
+        'ppt' => 'application/vnd.ms-powerpoint',
+        'odt' => 'application/vnd.oasis.opendocument.text',
+        'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
+    ];
+
     /**
      * adapted from http://scratch99.com/web-development/javascript/convert-bytes-to-mb-kb/
      *
@@ -24,7 +71,7 @@ class FileUtils
      *
      * @return string
      */
-    public static function getHumanReadableFileSize($bytes)
+    public static function getHumanReadableFileSize($bytes): string
     {
         $sizes = array('Bytes', 'KB', 'MB', 'GB', 'TB');
         if ($bytes == 0) {
@@ -39,4 +86,75 @@ public static function getHumanReadableFileSize($bytes)
             return round($bytes / pow(1024, $i), 1) . ' ' . $sizes[$i];
         }
     }
+
+    /**
+     * Determines the MIME type of file or content.
+     *
+     * @param string $filePath
+     * @param string $content
+     * @return array
+     */
+    public static function fileGetMimeType($filePath, &$content): false|array
+    {
+        $f_info = finfo_open(FILEINFO_MIME_TYPE);
+        if (!empty($content)) {
+            $mimeType = finfo_buffer($f_info, $content);
+            finfo_close($f_info);
+            // Check if filePath has an extension, if not add it based on MIME type
+            $filePath = self::ensureExtension($filePath, $mimeType);
+            return ['type' => $mimeType, 'filePath' => $filePath];
+        }
+
+        if (!empty($filePath) && !file_exists($filePath)) {
+            finfo_close($f_info);
+            return false;
+        }
+
+        $mimeType = finfo_file($f_info, $filePath);
+        finfo_close($f_info);
+        // Check if filePath has an extension, if not add it based on MIME type
+        $filePath = self::ensureExtension($filePath, $mimeType);
+
+        return ['type' => $mimeType, 'filePath' => $filePath];
+    }
+
+    /**
+     * Retrieves the MIME type based on the file extension.
+     *
+     * @param string $extension
+     * @return string
+     */
+    public static function getMimeTypeFromExtension($extension, $default = 'text/plain'): string
+    {
+        return self::$mimeTypes[strtolower($extension)] ?? $default;
+    }
+
+    /**
+     * Retrieves the file extension based on the MIME type.
+     *
+     * @param string $mimeType
+     * @return string
+     */
+    public static function getExtensionFromMimeType($mimeType): string
+    {
+        $extension = array_search(strtolower($mimeType), self::$mimeTypes);
+        return $extension !== false ? $extension : '';
+    }
+
+    /**
+     * Ensures the file path has an appropriate extension based on the MIME type.
+     *
+     * @param string $filePath
+     * @param string $mimeType
+     * @return string
+     */
+    public static function ensureExtension($filePath, $mimeType): string
+    {
+        $pathInfo = pathinfo($filePath);
+        if (empty($pathInfo['extension'])) {
+            $extension = self::getExtensionFromMimeType($mimeType);
+            $filePath .= '.' . $extension;
+        }
+        return $filePath;
+    }
 }