diff --git a/CHANGELOG b/CHANGELOG
index e25e8a531c51aff9cdc1ea889aef62ceccdd9007..d0f989e70d11eff1a4ebb31f0035e3811f07818b 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -12,6 +12,8 @@ smasch (1.0.0~alpha.1-0) unstable; urgency=low
   * small improvement: all configuration options that are not obligatory are 
     moved to configuration panel (#343)
   * small improvement: 2FA can be configured to be required option (#358)
+  * small improvement: redcap API token is not visible in configuration panel
+    (#359)
   
  -- Piotr Gawron <piotr.gawron@uni.lu>  Tue, 10 Nov 2020 14:00:00 +0200
 
diff --git a/smash/web/api_views/configuration.py b/smash/web/api_views/configuration.py
index 12c420dc66c6bed578984b5929b3a3cbf59beba9..d351af3459cc54555a6210fb8d092c3df3f5d27d 100644
--- a/smash/web/api_views/configuration.py
+++ b/smash/web/api_views/configuration.py
@@ -1,6 +1,7 @@
 from django.http import JsonResponse
 
 from web.models import ConfigurationItem
+from web.models.constants import VALUE_TYPE_PASSWORD
 
 
 def configuration_items(request):
@@ -18,10 +19,14 @@ def configuration_items(request):
 
     data = []
     for configuration_item in sliced_items:
+        value = configuration_item.value
+        if configuration_item.value_type == VALUE_TYPE_PASSWORD:
+            value = ''
         data.append({
             "id": configuration_item.id,
             "name": configuration_item.name,
-            "value": configuration_item.value
+            "value": value,
+            "value_type": configuration_item.value_type,
         })
     return JsonResponse({
         "draw": draw,
diff --git a/smash/web/migrations/0182_auto_20201126_1042.py b/smash/web/migrations/0182_auto_20201126_1042.py
new file mode 100644
index 0000000000000000000000000000000000000000..6260bec1fd0e60270202d30b62c1955c04ed7b7c
--- /dev/null
+++ b/smash/web/migrations/0182_auto_20201126_1042.py
@@ -0,0 +1,42 @@
+# Generated by Django 2.0.13 on 2020-11-26 10:42
+
+import django.core.files.storage
+from django.db import migrations, models
+
+from web.models.constants import REDCAP_TOKEN_CONFIGURATION_TYPE, VALUE_TYPE_PASSWORD, NEXMO_API_KEY, NEXMO_API_SECRET
+
+
+def configuration_items(apps, item_type):
+    # We can't import the ConfigurationItem model directly as it may be a newer
+    # version than this migration expects. We use the historical version.
+
+    # noinspection PyPep8Naming
+    ConfigurationItem = apps.get_model("web", "ConfigurationItem")
+    items = ConfigurationItem.objects.filter(
+        type__in=[REDCAP_TOKEN_CONFIGURATION_TYPE, NEXMO_API_KEY, NEXMO_API_SECRET]).all()
+    for item in items:
+        item.value_type = VALUE_TYPE_PASSWORD
+        item.save()
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        ('web', '0181_worker_privacy_notice_accepted'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='configurationitem',
+            name='value_type',
+            field=models.CharField(choices=[('PASSWORD', 'Password'), ('TEXT', 'Text')], default='TEXT', max_length=32,
+                                   verbose_name='Value type'),
+        ),
+        migrations.AlterField(
+            model_name='studysubject',
+            name='referral_letter',
+            field=models.FileField(blank=True, null=True,
+                                   storage=django.core.files.storage.FileSystemStorage(location='~/tmp/upload'),
+                                   upload_to='referral_letters', verbose_name='Referral letter'),
+        ),
+        migrations.RunPython(configuration_items),
+    ]
diff --git a/smash/web/migrations/0182_auto_20201126_1154.py b/smash/web/migrations/0183_auto_20201126_1154.py
similarity index 97%
rename from smash/web/migrations/0182_auto_20201126_1154.py
rename to smash/web/migrations/0183_auto_20201126_1154.py
index 3c7e93752fa5a3e1da48bf20888ce871c967c238..2e2670d0eb8e394244cba25128b47b456299a45f 100644
--- a/smash/web/migrations/0182_auto_20201126_1154.py
+++ b/smash/web/migrations/0183_auto_20201126_1154.py
@@ -7,7 +7,7 @@ from django.db import migrations, models
 class Migration(migrations.Migration):
 
     dependencies = [
-        ('web', '0181_worker_privacy_notice_accepted'),
+        ('web', '0182_auto_20201126_1042'),
     ]
 
     operations = [
diff --git a/smash/web/models/configuration_item.py b/smash/web/models/configuration_item.py
index 9879847cb62413f0914aa0de43160abef2210fd4..f873981e05d017326c26818d2e171c2b67dc101b 100644
--- a/smash/web/models/configuration_item.py
+++ b/smash/web/models/configuration_item.py
@@ -5,7 +5,8 @@ from django.db import models
 
 from web.models.constants import CANCELLED_APPOINTMENT_COLOR_CONFIGURATION_TYPE, \
     NO_SHOW_APPOINTMENT_COLOR_CONFIGURATION_TYPE, KIT_EMAIL_HOUR_CONFIGURATION_TYPE, \
-    KIT_EMAIL_DAY_OF_WEEK_CONFIGURATION_TYPE, KIT_DAILY_EMAIL_TIME_FORMAT_TYPE, KIT_DAILY_EMAIL_DAYS_PERIOD_TYPE
+    KIT_EMAIL_DAY_OF_WEEK_CONFIGURATION_TYPE, KIT_DAILY_EMAIL_TIME_FORMAT_TYPE, KIT_DAILY_EMAIL_DAYS_PERIOD_TYPE, \
+    VALUE_TYPE_CHOICES, VALUE_TYPE_TEXT
 
 
 class ConfigurationItem(models.Model):
@@ -24,6 +25,11 @@ class ConfigurationItem(models.Model):
     value = models.CharField(max_length=1024,
                              verbose_name='Value',
                              )
+    value_type = models.CharField(max_length=32,
+                                  choices=VALUE_TYPE_CHOICES,
+                                  verbose_name='Value type',
+                                  default=VALUE_TYPE_TEXT
+                                  )
 
     def __str__(self):
         return "%s %s" % (self.name, self.value)
diff --git a/smash/web/models/constants.py b/smash/web/models/constants.py
index 34f44a4886090fcfb416ce73e20e7ab3ecb06970..664604bbfe67215ed080c10bdde526a52a701e05 100644
--- a/smash/web/models/constants.py
+++ b/smash/web/models/constants.py
@@ -16,6 +16,15 @@ BOOL_CHOICES_WITH_NONE = (
     (False, 'No'),
     (None, 'N/A'),
 )
+
+VALUE_TYPE_PASSWORD = 'PASSWORD'
+VALUE_TYPE_TEXT = 'TEXT'
+VALUE_TYPE_CHOICES = (
+    (VALUE_TYPE_PASSWORD, 'Password'),
+    (VALUE_TYPE_TEXT, 'Text'),
+)
+
+
 SUBJECT_TYPE_CHOICES_CONTROL = 'C'
 SUBJECT_TYPE_CHOICES_PATIENT = 'P'
 SUBJECT_TYPE_CHOICES = {
diff --git a/smash/web/tests/api_views/test_configuration_item.py b/smash/web/tests/api_views/test_configuration_item.py
index 0628c1715d1d65cba82b1676ff4f7e33b1584484..e0a88dbfa405aad25d02c187aed7db344bf8ed70 100644
--- a/smash/web/tests/api_views/test_configuration_item.py
+++ b/smash/web/tests/api_views/test_configuration_item.py
@@ -4,7 +4,7 @@
 from django.urls import reverse
 
 from web.models import ConfigurationItem
-from web.models.constants import CANCELLED_APPOINTMENT_COLOR_CONFIGURATION_TYPE
+from web.models.constants import CANCELLED_APPOINTMENT_COLOR_CONFIGURATION_TYPE, VALUE_TYPE_PASSWORD
 from web.tests import LoggedInTestCase
 from web.tests.functions import create_configuration_item
 
@@ -15,6 +15,18 @@ class TestConfigurationItemApi(LoggedInTestCase):
         response = self.client.get(reverse('web.api.configuration_items'))
         self.assertEqual(response.status_code, 200)
 
+    def test_password_item(self):
+        item = ConfigurationItem.objects.create()
+        item.type = "TEST"
+        item.value = "secret_password"
+        item.name = "yyy"
+        item.value_type = VALUE_TYPE_PASSWORD
+        item.save()
+        response = self.client.get(reverse('web.api.configuration_items'),  {'length': "100"})
+        self.assertFalse(item.value in response.content.decode("utf-8"))
+        self.assertTrue(item.value_type in response.content.decode("utf-8"))
+        self.assertEqual(response.status_code, 200)
+
     def test_configuration_modify_invalid(self):
         response = self.client.get(reverse('web.api.update_configuration_item'))
         self.assertEqual(response.status_code, 200)