diff --git a/brian_dashboard_manager/grafana/provision.py b/brian_dashboard_manager/grafana/provision.py
index 54b5bf90391a38e9cf48f87e5610f0bbba0ee793..d0a21f35dc694acbbe6c207c0aeee37cc3212932 100644
--- a/brian_dashboard_manager/grafana/provision.py
+++ b/brian_dashboard_manager/grafana/provision.py
@@ -23,7 +23,7 @@ from brian_dashboard_manager.grafana.datasource import \
 from brian_dashboard_manager.grafana.folder import find_folder, \
     delete_folder
 from brian_dashboard_manager.inventory_provider.interfaces import \
-    get_interfaces
+    get_gws_direct, get_interfaces
 from brian_dashboard_manager.templating.nren_access import generate_nrens
 
 from brian_dashboard_manager.templating.helpers import is_re_customer, \
@@ -34,6 +34,8 @@ from brian_dashboard_manager.templating.helpers import is_re_customer, \
     get_interface_data, parse_backbone_name, parse_phy_upstream_name, \
     get_dashboard_data, get_aggregate_interface_data
 
+from brian_dashboard_manager.templating.gws import generate_gws
+
 from brian_dashboard_manager.templating.render import render_dashboard
 
 logger = logging.getLogger(__name__)
@@ -282,6 +284,7 @@ def provision(config):
                                       excluded_interfaces, datasource_name,
                                       exclude)
                 provisioned.append(res)
+
             for result in provisioned:
                 folder = result.result()
                 if folder is None:
@@ -291,6 +294,36 @@ def provision(config):
                         continue
                     dash_list[dashboard.get('uid')] = True
 
+        # fetch GWS direct data and provision related dashboards
+        logger.info('Provisioning GWS Direct dashboards')
+        folder_name = 'GWS Direct'
+        exclude_gws = excluded_folders.get(folder_name, [])
+        exclude_gws = list(map(lambda f: f.lower(), exclude_gws))
+        if isinstance(exclude_gws, bool) and exclude_gws:
+            # don't provision GWS Direct folder
+            delete_folder(token_request, folder_name)
+        else:
+            folder = find_folder(token_request, folder_name)
+            with ProcessPoolExecutor(max_workers=4) as executor:
+                gws_data = get_gws_direct(config['inventory_provider'])
+                provisioned = []
+
+                for dashboard in generate_gws(gws_data, datasource_name):
+                    rendered = render_dashboard(dashboard)
+                    if rendered.get('title').lower() in exclude_gws:
+                        executor.submit(delete_dashboard, token_request,
+                                        rendered, folder['id'])
+                        continue
+                    provisioned.append(executor.submit(create_dashboard,
+                                                       token_request,
+                                                       rendered, folder['id']))
+
+                for result in provisioned:
+                    dashboard = result.result()
+                    if dashboard is None:
+                        continue
+                    dash_list[dashboard.get('uid')] = True
+
         aggregate_dashboards = {
             'CLS PEERS': {
                 'predicate': is_cls_peer,
@@ -319,7 +352,6 @@ def provision(config):
         if isinstance(exclude_agg, bool) and exclude_agg:
             # don't provision aggregate folder
             delete_folder(token_request, 'Aggregates')
-            pass
         else:
             with ProcessPoolExecutor(max_workers=4) as executor:
                 provisioned = []
diff --git a/brian_dashboard_manager/inventory_provider/interfaces.py b/brian_dashboard_manager/inventory_provider/interfaces.py
index a27ceecfaee82f49d18c984e012665ac9ff6050e..8f4a91627b47c869ff7d865e23398f2d973318f5 100644
--- a/brian_dashboard_manager/inventory_provider/interfaces.py
+++ b/brian_dashboard_manager/inventory_provider/interfaces.py
@@ -47,3 +47,10 @@ def get_interfaces(host):  # pragma: no cover
         return interface
     enriched = list(map(enrich, interfaces))
     return enriched
+
+
+def get_gws_direct(host):
+    r = requests.get(f'{host}/poller/gws/direct')
+    r.raise_for_status()
+    interfaces = r.json()
+    return interfaces
diff --git a/brian_dashboard_manager/templating/gws.py b/brian_dashboard_manager/templating/gws.py
new file mode 100644
index 0000000000000000000000000000000000000000..e9f4b1353244bb31b67dc399769882afe20daa35
--- /dev/null
+++ b/brian_dashboard_manager/templating/gws.py
@@ -0,0 +1,46 @@
+from typing import DefaultDict
+from brian_dashboard_manager.templating.helpers import get_dashboard_data
+
+
+def get_interface_data(interfaces):
+    result = DefaultDict(list)
+
+    for interface in interfaces:
+
+        isp = interface.get('isp')
+        nren = interface.get('nren')
+
+        # identifier for interface, as we don't have their names
+        interface_tag = interface.get('tag')
+
+        counters = interface.get('counters')
+
+        skip = True
+
+        for counter in counters:
+            if counter.get('field') in ['traffic_in', 'traffic_out']:
+                skip = False
+
+        if skip:
+            # only process interfaces where we are polling traffic data
+            continue
+
+        gws_measurement = 'gwsd_rates'
+        panel_title = f'{nren} - {interface_tag} - {{}}'
+        result[f'GWS Direct - {isp}'].append({
+            'isp': isp,
+            'nren': nren,
+            'measurement': gws_measurement,
+            'title': panel_title,
+            'interface_tag': interface_tag,
+            'hostname': interface.get('hostname'),
+            'has_v6': False
+        })
+    return result
+
+
+def generate_gws(gws_data, datasource):
+
+    panel_data = get_interface_data(gws_data)
+    for dashboard in get_dashboard_data(panel_data, datasource, 'GWS_DIRECT'):
+        yield dashboard
diff --git a/brian_dashboard_manager/templating/helpers.py b/brian_dashboard_manager/templating/helpers.py
index d2001f1156f5299a477bfb21c88746ce98ed1e85..5ce022c39c01206662da9017ef85c27f4cb02a77 100644
--- a/brian_dashboard_manager/templating/helpers.py
+++ b/brian_dashboard_manager/templating/helpers.py
@@ -294,7 +294,9 @@ def get_panel_fields(panel, panel_type, datasource):
 
     def get_target_data(alias, field):
         return {
-            **panel,  # panel has target hostname and interface
+            # panel includes identifying information
+            # such as hostname, interface, etc.
+            **panel,
             'alias': alias,
             'refId': next(letters),
             'select_field': field,
@@ -344,9 +346,9 @@ def get_dashboard_data(data, datasource, tag, errors=False):
                     {**panel, **next(gridPos)}, 'errors', datasource))
         return result
 
-    for peer, panels in data.items():
+    for dashboard_name, panels in data.items():
         result = {
-            'title': peer,
+            'title': dashboard_name,
             'datasource': datasource,
             'panels': get_panel_definitions(panels, datasource),
         }
diff --git a/brian_dashboard_manager/templating/templates/shared/panel_target.json.j2 b/brian_dashboard_manager/templating/templates/shared/panel_target.json.j2
index b98bd3c3b5e335c6c7f798ee1ac2ad8eead80143..0bcd9f694d3153a187f63012bb032a8bbc796cb4 100644
--- a/brian_dashboard_manager/templating/templates/shared/panel_target.json.j2
+++ b/brian_dashboard_manager/templating/templates/shared/panel_target.json.j2
@@ -12,7 +12,11 @@
         }
         {% endif %}
     ],
+    {% if measurement %}
+    "measurement": "{{ measurement }}",
+    {% else %}
     "measurement": "interface_rates",
+    {% endif %}
     "orderByTime": null,
     "policy": null,
     "refId": "{{ refId }}",
@@ -41,6 +45,7 @@
         ]
     ],
     "tags": [
+        {% if not isp %}
         {
             "condition": null,
             "key": "hostname",
@@ -53,5 +58,25 @@
             "operator": "=",
             "value": "{{ interface }}"
         }
+        {% else %}
+        {
+            "condition": null,
+            "key": "tag",
+            "operator": "=",
+            "value": "{{ interface_tag }}"
+        },
+        {
+            "condition": "AND",
+            "key": "isp",
+            "operator": "=",
+            "value": "{{ isp }}"
+        },
+        {
+            "condition": "AND",
+            "key": "nren",
+            "operator": "=",
+            "value": "{{ nren }}"
+        }
+        {% endif %}
     ]
 }
\ No newline at end of file
diff --git a/test/test_gws_direct.py b/test/test_gws_direct.py
new file mode 100644
index 0000000000000000000000000000000000000000..688db385a1a0d1371876168d4f46b4066ed5cb5b
--- /dev/null
+++ b/test/test_gws_direct.py
@@ -0,0 +1,133 @@
+import responses
+import json
+from brian_dashboard_manager.templating.gws import generate_gws
+from brian_dashboard_manager.inventory_provider.interfaces import \
+    get_gws_direct
+
+
+TEST_DATA = [
+    {
+        "nren": "ARNES",
+        "isp": "Cogent",
+        "hostname": "88.200.0.63",
+        "tag": "a",
+        "counters": [
+            {
+                "field": "discards_in",
+                "oid": "1.3.6.1.2.1.2.2.1.13.533",
+                "community": "gn2nocT3st"
+            },
+            {
+                "field": "discards_out",
+                "oid": "1.3.6.1.2.1.2.2.1.19.533",
+                "community": "gn2nocT3st"
+            },
+            {
+                "field": "errors_in",
+                "oid": "1.3.6.1.2.1.2.2.1.14.533",
+                "community": "gn2nocT3st"
+            },
+            {
+                "field": "errors_out",
+                "oid": "1.3.6.1.2.1.2.2.1.20.533",
+                "community": "gn2nocT3st"
+            }
+        ]
+    },
+    {
+        "nren": "ARNES",
+        "isp": "Cogent",
+        "hostname": "88.200.0.63",
+        "tag": "b",
+        "counters": [
+            {
+                "field": "traffic_in",
+                "oid": "1.3.6.1.2.1.31.1.1.1.6.531",
+                "community": "gn2nocT3st"
+            },
+            {
+                "field": "traffic_out",
+                "oid": "1.3.6.1.2.1.31.1.1.1.10.531",
+                "community": "gn2nocT3st"
+            }
+        ]
+    },
+    {
+        "nren": "ARNES",
+        "isp": "Cogent",
+        "hostname": "88.200.0.63",
+        "tag": "c",
+        "counters": [
+            {
+                "field": "traffic_in",
+                "oid": "1.3.6.1.2.1.31.1.1.1.6.525",
+                "community": "gn2nocT3st"
+            },
+            {
+                "field": "traffic_out",
+                "oid": "1.3.6.1.2.1.31.1.1.1.10.525",
+                "community": "gn2nocT3st"
+            }
+        ]
+    },
+    {
+        "nren": "ARNES",
+        "isp": "Cogent",
+        "hostname": "88.200.0.63",
+        "tag": "d",
+        "counters": [
+            {
+                "field": "traffic_in",
+                "oid": "1.3.6.1.2.1.31.1.1.1.6.553",
+                "community": "gn2nocT3st"
+            },
+            {
+                "field": "traffic_out",
+                "oid": "1.3.6.1.2.1.31.1.1.1.10.553",
+                "community": "gn2nocT3st"
+            }
+        ]
+    },
+    {
+        "nren": "ARNES",
+        "isp": "Telia",
+        "hostname": "62.40.124.6",
+        "tag": "a",
+        "counters": [
+            {
+                "field": "traffic_in",
+                "oid": "1.3.6.1.2.1.31.1.1.1.6.611",
+                "community": "gn2nocT3st"
+            },
+            {
+                "field": "traffic_out",
+                "oid": "1.3.6.1.2.1.31.1.1.1.10.611",
+                "community": "gn2nocT3st"
+            }
+        ]
+    }
+]
+
+
+@responses.activate
+def test_gws(data_config, mocker, client):
+
+    def get_callback(request):
+        return 200, {}, json.dumps(TEST_DATA)
+
+    responses.add_callback(
+        method=responses.GET,
+        url=f"{data_config['inventory_provider']}/poller/gws/direct",
+        callback=get_callback)
+
+    gws_data = get_gws_direct(data_config['inventory_provider'])
+
+    dashboards = list(generate_gws(gws_data, 'testdatasource'))
+
+    assert len(dashboards) == 2
+
+    assert dashboards[0]['title'] == 'GWS Direct - Cogent'
+    assert len(dashboards[0]['panels']) == 3
+
+    assert dashboards[1]['title'] == 'GWS Direct - Telia'
+    assert len(dashboards[1]['panels']) == 1
diff --git a/test/test_update.py b/test/test_update.py
index 0512ebcebc934133fb3f53fadcc2ad46f0b990e0..29b955c71b2b95aa5118a13d78bec06d4087c379 100644
--- a/test/test_update.py
+++ b/test/test_update.py
@@ -460,6 +460,10 @@ def test_provision(data_config, mocker, client):
     _mocked_get_dashboard_definitions = mocker.patch(
         'brian_dashboard_manager.grafana.provision.get_dashboard_definitions')
 
+    _mocked_gws = mocker.patch(
+        'brian_dashboard_manager.grafana.provision.get_gws_direct')
+    _mocked_gws.return_value = []
+
     UID = 1
     ID = 1
     VERSION = 1