diff --git a/inventory_provider/db/ims_data.py b/inventory_provider/db/ims_data.py
index 11dc04dfe2199df5201052fb82c30b40d6e4b3d5..48ab115c15711abc3b7a14659a80e3139748250f 100644
--- a/inventory_provider/db/ims_data.py
+++ b/inventory_provider/db/ims_data.py
@@ -5,10 +5,46 @@ from collections import OrderedDict
 from inventory_provider import environment
 from inventory_provider.db import ims
 from inventory_provider.db.ims import InventoryStatus
-
 environment.setup_logging()
 logger = logging.getLogger(__name__)
 
+# Dashboard V3
+
+
+def lookup_pop_info(ds, hostname):
+    site_nav_props = [
+        ims.SITE_PROPERTIES['City'],
+        ims.SITE_PROPERTIES['SiteAliases'],
+        ims.SITE_PROPERTIES['Country']
+    ]
+
+    node = ds.get_entity_by_name('Node', hostname)
+    if not node:
+        return None
+    site = ds.get_entity_by_id('Site', node['SiteId'], site_nav_props, True)
+    city = site['City']
+    abbreviation = ''
+    try:
+        abbreviation = site['SiteAliases'][0]['AliasName']
+    except IndexError:
+        pass  # no alias - ignore silently
+    eq = {
+            'equipment-name': node['Name'],
+            'status': InventoryStatus(node['InventoryStatusId']).name,
+            'pop': {
+                'name': site['Name'],
+                'city': city['Name'],
+                'country': city['Country']['Name'],
+                'abbreviation': abbreviation,
+                'longitude': site['Longitude'],
+                'latitude': site['Latitude'],
+            }
+        }
+    return eq
+
+
+# End of Dashboard V3 stuff
+
 INTERNAL_POP_NAMES = {
     'Cambridge OC',
     'DANTE Lab',
@@ -81,38 +117,6 @@ def lookup_lg_routers(ds):
             yield(eq)
 
 
-def lookup_pop_info(ds, hostname):
-    site_nav_props = [
-        ims.SITE_PROPERTIES['City'],
-        ims.SITE_PROPERTIES['SiteAliases'],
-        ims.SITE_PROPERTIES['Country']
-    ]
-
-    node = ds.get_entity_by_name('Node', hostname)
-    if not node:
-        return None
-    site = ds.get_entity_by_id('Site', node['SiteId'], site_nav_props, True)
-    city = site['City']
-    abbreviation = ''
-    try:
-        abbreviation = site['SiteAliases'][0]['AliasName']
-    except IndexError:
-        pass  # no alias - ignore silently
-    eq = {
-            'equipment-name': node['Name'],
-            'status': InventoryStatus(node['InventoryStatusId']).name,
-            'pop': {
-                'name': site['Name'],
-                'city': city['Name'],
-                'country': city['Country']['Name'],
-                'abbreviation': abbreviation,
-                'longitude': site['Longitude'],
-                'latitude': site['Latitude'],
-            }
-        }
-    return eq
-
-
 def otrs_get_customer_company_rows(ds):
     yield ['customer_id', 'name', 'street', 'zip', 'city', 'country', 'url',
            'comments']
@@ -215,7 +219,7 @@ def otrs_get_vendor_contacts(ds):
         yield t_customer_user
 
 
-def otrs_get_customer_users_rows(ds):
+def otrs_get_customer_users_rows(ds, return_duplicates=False):
     yield ['email', 'username', 'customer_id', 'customer_id_2', 'title',
            'firstname', 'lastname', 'phone', 'fax', 'mobile', 'street', 'zip',
            'city', 'country', 'comments']
@@ -229,18 +233,59 @@ def otrs_get_customer_users_rows(ds):
             if c['email'] not in yielded_customer_emails:
                 yield c
 
-    sorted_cus = sorted(get_all_cus_user_rows(), key=lambda x: x['email'])
+    def weed_duplicates(duplicates):
+        # this is here to allow for additional rules
+        id_rank = {
+            'geant': 1
+        }
+        top_rank = -1
+        top_ranked = None
+        for d in duplicates:
+            rank = id_rank.get(d['customer_id'].lower(), 0)
+            if rank > top_rank:
+                top_rank = rank
+                top_ranked = []
+            if rank == top_rank:
+                top_ranked.append(d)
+        return top_ranked
+
+    cus_by_email = {}
     duplicate_emails = set()
-    previous_email = None
-    for cu in sorted_cus:
-        if cu['email'] == previous_email:
-            duplicate_emails.add(previous_email)
-        previous_email = cu['email']
+    for cu in get_all_cus_user_rows():
+        email = cu['email']
+        cus_for_email = cus_by_email.get(email, [])
+        if cus_for_email:
+            duplicate_emails.add(email)
+        cus_for_email.append(cu)
+        cus_by_email[email] = cus_for_email
+
+    remaining_duplicates = []
     if duplicate_emails:
-        logger.error('Duplicate emails found in OTRS customer-user export: '
-                     f'{duplicate_emails}')
-        # raise KeyError('Duplicate emails found in OTRS customer-user export: '  # noqa
-        #                f'{duplicate_emails}')
-
-    for cu in sorted_cus:
-        yield list(cu.values())
+        logger.info('Duplicate emails found in OTRS customer-user export: '
+                    f'{duplicate_emails} - attempting to weed')
+        # weeded = []
+        for email in duplicate_emails:
+            weeded = weed_duplicates(cus_by_email.pop(email))
+            if len(weeded) == 1:
+                cus_by_email[email] = weeded
+            else:
+                remaining_duplicates.extend(
+                    sorted(
+                        [list(w.values()) for w in weeded], key=lambda x: x[2])
+                )
+
+        if remaining_duplicates:
+            # need guidance what to do if this occurs, should we pick the first
+            # one for each, or ignore them completely? Ignoring them for now
+            logger.error('Duplicate emails remain after weeding, '
+                         f'{"including" if return_duplicates else "excluding"}'
+                         ' duplicates in returned data: ')
+
+            for rd in remaining_duplicates:
+                logger.debug(rd)
+
+    for email, details in sorted(cus_by_email.items()):
+        yield list(details[0].values())
+
+    if return_duplicates:
+        yield from remaining_duplicates
diff --git a/inventory_provider/routes/ims_otrs.py b/inventory_provider/routes/ims_otrs.py
index cbae9e9e740f7de090b20b542e9f4439cc89af88..b03fec117fd2e6e70408e923d5a855f5d35e8cc8 100644
--- a/inventory_provider/routes/ims_otrs.py
+++ b/inventory_provider/routes/ims_otrs.py
@@ -18,16 +18,11 @@ def after_request(resp):
     return common.after_request(resp)
 
 
-def get_otrs_data(fn):
-    ims_config = current_app.config['INVENTORY_PROVIDER_CONFIG']["ims"]
-    ds = IMS(
-        ims_config['api'],
-        ims_config['username'],
-        ims_config['password'])
+def get_otrs_output(result):
 
     with StringIO() as sio:
         writer = csv.writer(sio, delimiter='^')
-        writer.writerows(fn(ds))
+        writer.writerows(result)
         data = sio.getvalue()
     return Response(
         response=data,
@@ -37,17 +32,24 @@ def get_otrs_data(fn):
 
 @routes.route('customer-companies')
 def get_customer_companies_data():
-    return get_otrs_data(ims_data.otrs_get_customer_company_rows)
+    ims_config = current_app.config['INVENTORY_PROVIDER_CONFIG']["ims"]
+    ds = IMS(ims_config['api'], ims_config['username'], ims_config['password'])
+    return get_otrs_output(ims_data.otrs_get_customer_company_rows(ds))
 
 
 @routes.route('customer-users')
 def get_customer_users_data():
-    return get_otrs_data(ims_data.otrs_get_customer_users_rows)
+    ims_config = current_app.config['INVENTORY_PROVIDER_CONFIG']["ims"]
+    ds = IMS(ims_config['api'], ims_config['username'], ims_config['password'])
+    return_duplicates = request.args.get('duplicates', 'f').lower() == 'true'
+    return get_otrs_output(ims_data.otrs_get_customer_users_rows(
+        ds, return_duplicates=return_duplicates))
 
 
 @routes.route('export')
 def send_exports():
     files_value = request.args.get('files', None)
+    duplicates = request.args.get('duplicates', 'f').lower() == 'true'
     if files_value:
         try:
             files_value = int(files_value)
@@ -61,7 +63,8 @@ def send_exports():
                 response=html.escape(f'Bad value for <files> {files_value}'),
                 status=requests.codes.bad_request,
                 mimetype="text/html")
-    task = export_data_for_otrs.delay(files_value)
+    task = export_data_for_otrs.delay(
+        files_to_export=files_value, export_duplicates=duplicates)
     return Response(
         response=task.id,
         status=requests.codes.ok,
diff --git a/inventory_provider/routes/otrs_jobs.py b/inventory_provider/routes/otrs_jobs.py
deleted file mode 100644
index 6de4678de1b8d27c5cb373d9f5208bf3ea6a5e0b..0000000000000000000000000000000000000000
--- a/inventory_provider/routes/otrs_jobs.py
+++ /dev/null
@@ -1,37 +0,0 @@
-import html
-
-import requests
-from flask import Blueprint, request, Response
-
-from inventory_provider.routes import common
-from inventory_provider.tasks.ims_worker import OTRSFiles, export_data_for_otrs
-
-routes = Blueprint("otrs", __name__)
-
-
-@routes.after_request
-def after_request(resp):
-    return common.after_request(resp)
-
-
-@routes.route('export')
-def send_exports():
-    files_value = request.args.get('files', None)
-    if files_value:
-        try:
-            files_value = int(files_value)
-        except ValueError:
-            return Response(
-                response=html.escape('<files> should be an Integer'),
-                status=requests.codes.bad_request,
-                mimetype="text/html")
-        if files_value < 0 or files_value > sum(OTRSFiles):
-            return Response(
-                response=html.escape(f'Bad value for <files> {files_value}'),
-                status=requests.codes.bad_request,
-                mimetype="text/html")
-    task = export_data_for_otrs.delay(files_value)
-    return Response(
-        response=task.id,
-        status=requests.codes.ok,
-        mimetype="text/html")
diff --git a/inventory_provider/tasks/ims_worker.py b/inventory_provider/tasks/ims_worker.py
index fdab630d459dbe9b7b082e3d920eae58996562ce..26f7f09dccb3a2be7544604d73116d0228a7eaf7 100644
--- a/inventory_provider/tasks/ims_worker.py
+++ b/inventory_provider/tasks/ims_worker.py
@@ -47,13 +47,12 @@ class OTRSFiles(IntFlag):
 
 @app.task(base=InventoryTask, bind=True, name='export_data_for_otrs')
 @log_task_entry_and_exit
-def export_data_for_otrs(self, files_to_export=None):
+def export_data_for_otrs(self, files_to_export=None, export_duplicates=False):
     if files_to_export:
         files_to_export = OTRSFiles(files_to_export)
     else:
         files_to_export = set(OTRSFiles)
 
-    logger.debug('>>> export_data_for_otrs_ims')
     ims_config = InventoryTask.config["ims"]
     otrs_config = InventoryTask.config["otrs-export"]
 
@@ -78,7 +77,8 @@ def export_data_for_otrs(self, files_to_export=None):
             cus_usr_path = temp_path.joinpath(f'{prefix}customer_user.csv')
             with open(cus_usr_path, 'w+') as f:
                 writer = csv.writer(f, delimiter='^')
-                writer.writerows(ims_data.otrs_get_customer_users_rows(ds))
+                writer.writerows(ims_data.otrs_get_customer_users_rows(
+                    ds, return_duplicates=export_duplicates))
 
         command = command_template.format(
             user=otrs_config['username'],
@@ -88,5 +88,3 @@ def export_data_for_otrs(self, files_to_export=None):
             destination=otrs_config['destination']
         )
         subprocess.run(command, shell=True, check=True)
-
-    logger.debug('<<< export_data_for_otrs_ims')