diff --git a/heptapod/gitlab/__init__.py b/heptapod/gitlab/__init__.py
index b5091b393a304969af0264c2de9b01c7e4d8ec58_aGVwdGFwb2QvZ2l0bGFiL19faW5pdF9fLnB5..1c2e75c9b593d4303ccc78c9609f9e931d3783c6_aGVwdGFwb2QvZ2l0bGFiL19faW5pdF9fLnB5 100644
--- a/heptapod/gitlab/__init__.py
+++ b/heptapod/gitlab/__init__.py
@@ -247,6 +247,18 @@
         warnings = as_json.get('warnings')
         if warnings is not None:
             out_lines.extend(format_warnings(warnings))
+        # GitLab 12.3: MR links as messages with type 'basic',
+        # broadcasts and warnings as type 'alert'
+        # basic treatment, probably won't be very pretty, but good enough
+        # for now.
+        messages = as_json.get('messages', ())
+        for message in messages:
+            if message.get(u'type') == u'basic':
+                out_lines.append(message['message'].encode('utf-8'))
+        alert = '\n'.join(m['message'].encode('utf-8')
+                          for m in messages if m.get(u'type') == u'alert')
+        if alert:
+            out_lines.extend(format_alert_message(alert))
         return '\n'.join(out_lines)
 
     def pre_receive_params(self, changes, env=None):
diff --git a/heptapod/gitlab/tests/test_hooks_integration.py b/heptapod/gitlab/tests/test_hooks_integration.py
index b5091b393a304969af0264c2de9b01c7e4d8ec58_aGVwdGFwb2QvZ2l0bGFiL3Rlc3RzL3Rlc3RfaG9va3NfaW50ZWdyYXRpb24ucHk=..1c2e75c9b593d4303ccc78c9609f9e931d3783c6_aGVwdGFwb2QvZ2l0bGFiL3Rlc3RzL3Rlc3RfaG9va3NfaW50ZWdyYXRpb24ucHk= 100644
--- a/heptapod/gitlab/tests/test_hooks_integration.py
+++ b/heptapod/gitlab/tests/test_hooks_integration.py
@@ -121,7 +121,7 @@
     assert params['project'] == repo.root
 
 
-def test_post_receive_handle_response(tmpdir):
+def test_post_receive_handle_response_gl_12_2(tmpdir):
     wrapper = make_repo(tmpdir, 'proj.hg')
     repo = wrapper.repo
     hook = Hook('post-receive', repo)
@@ -169,6 +169,37 @@
     assert any(warnings in l for l in out)
 
 
+def test_post_receive_handle_response_gl_12_3(tmpdir):
+    wrapper = make_repo(tmpdir, 'proj.hg')
+    repo = wrapper.repo
+    hook = Hook('post-receive', repo)
+
+    out = hook.post_receive_handle_response(
+        # example taken from the functional tests
+        {u'messages': [
+            {u'message': (
+                u'To create a merge request '
+                u'for topic/default/ant, visit:\n'
+                u'http://localhost:3000/root/test_project/merge_requests/new'
+                u'?merge_request%5Bsource_branch%5D=topic%2Fdefault%2Fant'
+                ),
+             u'type': u'basic',
+             },
+            {u'message': u'This is a TESTING SERVER',
+             u'type': u'alert',
+             },
+            ]})
+    out = [l.strip() for l in out.splitlines() if l.strip()]
+    assert out == [
+        'To create a merge request for topic/default/ant, visit:',
+        'http://localhost:3000/root/test_project/merge_requests/new'
+        '?merge_request%5Bsource_branch%5D=topic%2Fdefault%2Fant',
+        '=' * 72,
+        'This is a TESTING SERVER',
+        '=' * 72,
+    ]
+
+
 def test_integration_post_receive(tmpdir, monkeypatch):
     records = []
     responses = [make_json_response(