diff --git a/smash/smash/settings.py b/smash/smash/settings.py
index 86456861f9b30ff0d43c6dcf224ec70038765597..98f536269cec9778a13523b618cfd12347eb5896 100644
--- a/smash/smash/settings.py
+++ b/smash/smash/settings.py
@@ -72,7 +72,8 @@ TEMPLATES = [
 
 CRON_CLASSES = [
     'web.views.kit.KitRequestEmailSendJob',
-    'web.redcap_connector.RedCapRefreshJob'
+    'web.redcap_connector.RedCapRefreshJob',
+    'web.voucher_expiry_job',
 ]
 
 # Password validation
diff --git a/smash/web/views/voucher.py b/smash/web/views/voucher.py
index 4ca8361cbfb391c347a85db4a4ad38e1bef77266..b33538f74cc37d2020493bd5626067d48fd86f3a 100644
--- a/smash/web/views/voucher.py
+++ b/smash/web/views/voucher.py
@@ -6,10 +6,12 @@ from django.urls import reverse_lazy
 from django.views.generic import CreateView
 from django.views.generic import ListView
 from django.views.generic import UpdateView
+from django_cron import CronJobBase, Schedule
 
+from views.notifications import get_today_midnight_date
 from web.forms import VoucherForm
 from web.models import Voucher, StudySubject
-from web.models.constants import GLOBAL_STUDY_ID
+from web.models.constants import GLOBAL_STUDY_ID, VOUCHER_STATUS_NEW, VOUCHER_STATUS_EXPIRED
 from . import WrappedView
 
 logger = logging.getLogger(__name__)
@@ -64,3 +66,18 @@ class VoucherEditView(SuccessMessageMixin, UpdateView, WrappedView):
 
     def get_study_subject_id(self):
         return Voucher.objects.get(id=self.kwargs['pk']).study_subject.id
+
+
+class ExpireVouchersJob(CronJobBase):
+    RUN_EVERY_MINUTES = 120
+    schedule = Schedule(run_every_mins=RUN_EVERY_MINUTES)
+    code = 'web.voucher_expiry_job'  # a unique code
+
+    # noinspection PyMethodMayBeStatic
+    def do(self):
+        due_date = get_today_midnight_date()
+        vouchers = Voucher.objects.filter(status=VOUCHER_STATUS_NEW, expiry_date__lte=due_date)
+        count = vouchers.count()
+        if count > 0:
+            vouchers.update(status=VOUCHER_STATUS_EXPIRED)
+        return count + " vouchers expired"