diff --git a/app/controllers/reservation.py b/app/controllers/reservation.py
index f6fd4380d91e01142c7c8da183fc70d98f79c026..b9c6d8080d73ec3d13345eab989cbd749fca31ad 100644
--- a/app/controllers/reservation.py
+++ b/app/controllers/reservation.py
@@ -20,14 +20,16 @@ class ReservationController():
             if not user.is_confirmed:
                 raise UserNotConfirmedError
 
-            record = Reservation()
-            record.user = user
-            record.time_start = datetime.now()
-            record.time_end = datetime.now() + current_app.config.get('RESERVATION_DURATION', timedelta(hours=1))
+            reservation = Reservation()
+            reservation.user = user
+            reservation.time_start = datetime.now()
+            reservation.time_end = datetime.now() + current_app.config.get('RESERVATION_DURATION', timedelta(hours=1))
 
-            db.session.add(record)
+            db.session.add(reservation)
             db.session.commit()
 
+            return reservation
+
 
     @classmethod
     def has_active_reservation(cls, user):
diff --git a/app/controllers/user.py b/app/controllers/user.py
index 8f3ca6e316719f11bc2b2cb6a7766236dde701a6..49653b607c14fa1355dc978f2fc2615187220de4 100644
--- a/app/controllers/user.py
+++ b/app/controllers/user.py
@@ -29,7 +29,7 @@ class UserController():
                 debug=False) or \
                 User.query.filter(User.email == email).count():
             errors.append('email')
-        if telegram_id is not None and User.query.filter(User.telegram_id == telegram_id):
+        if telegram_id is not None and User.query.filter(User.telegram_id == telegram_id).count() > 0:
             errors.append('telegram_id')
         if telegram_id is None and (password is None or len(password) == 0):
             errors.append('password')
@@ -62,7 +62,19 @@ class UserController():
         db.session.add(user)
         db.session.commit()
 
-        cls._send_confirm_email(user)
+        cls.send_confirm_email(user)
+
+
+    @staticmethod
+    def update(user, name=None, email=None, telegram_chat_id=None):
+        if name:
+            user.name = name
+        if email:
+            user.email = email
+            user.is_confirmed = False
+        if telegram_chat_id:
+            user.telegram_chat_id = telegram_chat_id
+        db.session.commit()
 
 
     @staticmethod
@@ -75,6 +87,11 @@ class UserController():
         return User.query.filter(User.email == email).first()
 
 
+    @staticmethod
+    def get_by_telegram_user_id(telegram_user_id):
+        return User.query.filter(User.telegram_id == telegram_user_id).first()
+
+
     @classmethod
     def verify_confirm_email(cls, email, token):
         user = cls.get_by_email(email)
@@ -139,19 +156,35 @@ class UserController():
         RecordController.terminate(record)
 
 
+    @classmethod
+    def unlink_telegram_user_id(cls, telegram_user_id):
+        user = cls.get_by_telegram_user_id(telegram_user_id)
+
+        if user:
+            user.telegram_id = None
+            user.telegram_chat_id = None
+            db.session.commit()
+
+
     @staticmethod
-    def send_password_reset_email(user):
+    def get_password_reset_link(user):
         user.generate_new_token()
         db.session.commit()
+        return url_for('login.password_reset', token=user.token, email=user.email, _external=True)
+
+
+    @classmethod
+    def send_password_reset_email(cls, user):
+        password_reset_link = cls.get_password_reset_link(user)
         msg = Message(subject='[Bastli Bouncer] Reset Password',
-                  body=render_template('email/password_reset.txt', user=user),
+                  body=render_template('email/password_reset.txt', user=user, password_reset_link=password_reset_link),
                   recipients=[user.email])
-        print(url_for('login.password_reset', token=user.token, email=user.email, _external=True))
+        print(password_reset_link)
         mail.send(msg)
 
 
     @staticmethod
-    def _send_confirm_email(user):
+    def send_confirm_email(user):
         msg = Message(
             subject='[Bastli Bouncer] Confirm Email Address',
             body=render_template('email/confirm.txt', user=user),
diff --git a/app/templates/email/password_reset.txt b/app/templates/email/password_reset.txt
index 7edb49afb0e271a1e42a2c1fe6c902d8724a7551..ed817249c56d32dd252aa4643be758674839aa0e 100644
--- a/app/templates/email/password_reset.txt
+++ b/app/templates/email/password_reset.txt
@@ -5,6 +5,6 @@ If that was not you, this message can be ignored.
 
 Please follow the instructions at the link below.
 
-{{ url_for('login.password_reset', token=user.token, email=user.email, _external=True) }}
+{{ password_reset_link }}
 
 The link will expire after some time.
diff --git a/bot/bot.py b/bot/bot.py
index e9fd8f2982977598a92743975ab984cd8544ff0d..869627c7feb5fa8eee9a25e39b4913f54f4b68fa 100644
--- a/bot/bot.py
+++ b/bot/bot.py
@@ -1,11 +1,9 @@
 from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
 
 from .commands.help import help_command, start_command
-from .commands.free_workplaces import freeworkplaces_command
-
-# def echo(update, context):
-#     """Echo the user message."""
-#     update.message.reply_text(update.message.text)
+from .commands.workplace import freeworkplaces_command, reserve_command, cancel_command, enter_command, leave_command
+from .commands.user import status_command, resetpassword_command, resendconfirmationemail_command
+from .commands.register import register_conv_handler
 
 
 def create_bot(token):
@@ -14,12 +12,16 @@ def create_bot(token):
     # Get the dispatcher to register handlers
     dp = updater.dispatcher
 
-    # on different commands - answer in Telegram
-    dp.add_handler(CommandHandler("start", start_command))
-    dp.add_handler(CommandHandler("help", help_command))
-    dp.add_handler(CommandHandler("freeworkplaces", freeworkplaces_command))
-
-    # on noncommand i.e message - echo the message on Telegram
-    # dp.add_handler(MessageHandler(Filters.text, echo))
+    dp.add_handler(CommandHandler('start', start_command))
+    dp.add_handler(CommandHandler('help', help_command))
+    dp.add_handler(CommandHandler('freeworkplaces', freeworkplaces_command))
+    dp.add_handler(CommandHandler('resetpassword', resetpassword_command))
+    dp.add_handler(CommandHandler('resendconfirmationemail', resendconfirmationemail_command))
+    dp.add_handler(CommandHandler('status', status_command))
+    dp.add_handler(CommandHandler('reserve', reserve_command))
+    dp.add_handler(CommandHandler('cancel', cancel_command))
+    dp.add_handler(CommandHandler('enter', enter_command))
+    dp.add_handler(CommandHandler('leave', leave_command))
+    dp.add_handler(register_conv_handler)
 
     return updater
diff --git a/bot/commands/free_workplaces.py b/bot/commands/free_workplaces.py
deleted file mode 100644
index 9613dc7e9443665b68c47107e78a22f6179a0936..0000000000000000000000000000000000000000
--- a/bot/commands/free_workplaces.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from telegram import ParseMode
-from app.controllers import FreeWorkplacesController
-from app import app_context
-
-def freeworkplaces_command(update, context):
-    """Send a message when the command /freeworkplaces is issued."""
-    with app_context():
-        free_workplaces = FreeWorkplacesController.get_free_workplaces()
-    response  = '*' + str(free_workplaces) + '* workplaces are currently available.'
-    update.message.reply_text(response, parse_mode=ParseMode.MARKDOWN)
diff --git a/bot/commands/help.py b/bot/commands/help.py
index 8870bf52a4e5d292fa6e6fe620e5aa4bf27d4106..4759475f3046bf31c888f8ba5022887ab5492ecc 100644
--- a/bot/commands/help.py
+++ b/bot/commands/help.py
@@ -1,21 +1,24 @@
-from telegram import ParseMode
+from telegram import ParseMode, ReplyKeyboardRemove
 
 def start_command(update, context):
     """Send a message when the command /start is issued."""
-    start_text  = 'Hi!\n\n'
+    name = update.message.from_user.first_name
+    start_text  = 'Hi ' + name + '!\n\n'
     start_text += 'I\'m the Bastli bouncer bot.\n'
     start_text += 'My job is to ensure that the workshop doesn\'t get crowded and to maintain the user list.\n\n'
     start_text += 'Please follow the rules below:\n\n'
     start_text += '*Rule 1*\n'
-    start_text += 'Register yourself with /register [email] [name]\n\n'
+    start_text += 'Register yourself with /register.\n\n'
     start_text += '*Rule 2*\n'
-    start_text += 'Check for free workplaces and reserve one before you come here.\n\n'
+    start_text += 'Check for free workplaces and /reserve one before you come here.\n\n'
     start_text += '*Rule 3*\n'
-    start_text += 'Send /enter when you enter the workshop\n\n'
+    start_text += 'Send /enter when you enter the workshop.\n\n'
     start_text += '*Rule 4*\n'
-    start_text += 'Send /leave when you leave the workshop\n\n\n'
+    start_text += 'Send /leave when you leave the workshop.\n\n\n'
     start_text += 'See /help for more information about the commands.'
-    update.message.reply_text(start_text, parse_mode=ParseMode.MARKDOWN)
+    update.message.reply_text(start_text,
+                              parse_mode=ParseMode.MARKDOWN,
+                              reply_markup=ReplyKeyboardRemove())
 
 
 def help_command(update, context):
@@ -23,8 +26,8 @@ def help_command(update, context):
     help_text  = '*Bastli Bouncer bot usage:*\n\n'
     help_text += '/freeworkplaces\n'
     help_text += '    _Get available workplaces._\n'
-    help_text += '/register [email] [name]\n'
-    help_text += '    _Create an account._\n'
+    help_text += '/register\n'
+    help_text += '    _Initiate account creation._\n'
     help_text += '/resetpassword\n'
     help_text += '    _Request password reset._\n'
     help_text += '/status\n'
@@ -37,4 +40,6 @@ def help_command(update, context):
     help_text += '    _Request for entry._\n'
     help_text += '/leave\n'
     help_text += '    _Free the workspace._\n'
-    update.message.reply_text(help_text, parse_mode=ParseMode.MARKDOWN)
+    update.message.reply_text(help_text,
+                              parse_mode=ParseMode.MARKDOWN,
+                              reply_markup=ReplyKeyboardRemove())
diff --git a/bot/commands/register.py b/bot/commands/register.py
new file mode 100644
index 0000000000000000000000000000000000000000..2e9315bf0022f8d0ae8ebd949132fbcd4d69f147
--- /dev/null
+++ b/bot/commands/register.py
@@ -0,0 +1,343 @@
+from telegram import ParseMode, ReplyKeyboardMarkup, ReplyKeyboardRemove
+from telegram.ext import CommandHandler, MessageHandler, Filters, ConversationHandler
+from validate_email import validate_email
+from app.controllers import FreeWorkplacesController, UserController
+from app import app_context
+
+# Conversation states
+REGISTER_TYPE, EMAIL, NAME, CONFIRM, PASSWORD = range(5)
+
+
+def register_command(update, context):
+    try:
+        with app_context():
+            user = UserController.get_by_telegram_user_id(update.message.from_user.id)
+            
+        if user:
+            response_text = 'Your telegram user is already linked to an existing account (' + user.email + ').\n\n'
+
+            if user.has_password:
+                response_text += (
+                    'You can update the account information or '
+                    'unlink your telegram user from the account.\n')
+                reply_keyboard = [['Update', 'Unlink', 'Cancel']]
+            else:
+                response_text += (
+                    'You can update the account information instead.\n'
+                    'To unlink your telegram user from the account, '
+                    'you must set a password first.')
+                reply_keyboard = [['Update', 'Set Password', 'Cancel']]
+
+            update.message.reply_text(response_text,
+                                    reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=False))
+            return REGISTER_TYPE
+        else:
+            update.message.reply_text(
+                'I will guide you through the interactive registration process now.\n'
+                'Send /cancel to abort the registration at any time.\n\n'
+                'What is your email address?')
+            return EMAIL
+    except:
+        return abort_error(update, context)
+
+
+def register_type(update, context):
+    try:
+        reply = update.message.text
+        with app_context():
+            if reply == 'Update':
+                context.user_data['register_update'] = True
+
+                user = UserController.get_by_telegram_user_id(update.message.from_user.id)
+
+                response_text = (
+                    'Send /cancel to abort the update process at any time. '
+                    'The data will not be updated until the end of the process.\n\n'
+                    'The stored email address is *' + user.email + '*.\n'
+                    'Send me the new email address or /skip if it is still up to date.')
+                update.message.reply_text(response_text,
+                                        parse_mode=ParseMode.MARKDOWN,
+                                        reply_markup=ReplyKeyboardRemove())
+                return EMAIL
+            elif reply == 'Unlink':
+                UserController.unlink_telegram_user_id(update.message.from_user.id)
+                response_text = (
+                    'Your are free! Now, let\'s continue with the registration.\n'
+                    'Send /cancel to abort the registration at any time.\n\n'
+                    'What is your email address?')
+                update.message.reply_text(response_text,
+                                          parse_mode=ParseMode.MARKDOWN,
+                                          reply_markup=ReplyKeyboardRemove())
+                return EMAIL
+            elif reply == 'Set Password':
+                user = UserController.get_by_telegram_user_id(update.message.from_user.id)
+                if user:
+                    password_reset_link = UserController.get_password_reset_link(user)
+                    print(password_reset_link)
+                    response_text = (
+                        '[Click here](' + password_reset_link + ') to set a new password.\n\n'
+                        'Send /register afterwards to start the registration process again.')
+                else:
+                    response_text = (
+                        'Ooops, there\'s something wrong.\n'
+                        'I could not find any information about your account anymore.'
+                        'Send /register to start the registration process again.')
+                update.message.reply_text(response_text,
+                                          parse_mode=ParseMode.MARKDOWN,
+                                          reply_markup=ReplyKeyboardRemove())
+                context.user_data.clear()
+                return ConversationHandler.END
+            elif reply == 'Cancel':
+                return cancel(update, context)
+            else:
+                update.message.reply_text('I did not understand your answer.\n'
+                                          'Please choose an option from the reply keyboard.')
+                return REGISTER_TYPE
+    except:
+        return abort_error(update, context)
+
+
+def email_skip(update, context):
+    try:
+        if 'register_update' in context.user_data:
+            with app_context():
+                user = UserController.get_by_telegram_user_id(update.message.from_user.id)
+            response_text = (
+                'Great! I know you as *' + user.name + '*.\n'
+                'Send me another name or /skip if you don\'t want to change it.')
+            update.message.reply_text(response_text,
+                                      parse_mode=ParseMode.MARKDOWN,
+                                      reply_markup=ReplyKeyboardRemove())
+            return NAME
+        else:
+            response_text = (
+                'You prankster, you! There is no way to skip this step!\n'
+                'Tell me your email address or send /cancel to abort the registration.')
+            update.message.reply_text(response_text,
+                                      parse_mode=ParseMode.MARKDOWN,
+                                      reply_markup=ReplyKeyboardRemove())
+            return EMAIL
+    except:
+        return abort_error(update, context)
+
+def email(update, context):
+    try:
+        email = update.message.text
+
+        with app_context():
+            if not validate_email(email_address=email, \
+                        check_regex=True, \
+                        check_mx=False, \
+                        use_blacklist=True, \
+                        debug=False):
+                response_text = (
+                    'Ooops, that doesn\'t seem to be a valid email address. Please try again.\n'
+                    '_Send /cancel to abort the registration process._')
+                update.message.reply_text(response_text,
+                                          parse_mode=ParseMode.MARKDOWN,
+                                          reply_markup=ReplyKeyboardRemove())
+                return EMAIL
+            elif UserController.get_by_email(email) is not None:
+                response_text  = (
+                    'Ooops, your email address is already taken. Please take another one.\n'
+                    '_Send /cancel to abort the registration process._')
+                update.message.reply_text(response_text,
+                                          parse_mode=ParseMode.MARKDOWN,
+                                          reply_markup=ReplyKeyboardRemove())
+                return EMAIL
+
+        context.user_data['register_email'] = email
+
+        if 'register_update' in context.user_data:
+            user = UserController.get_by_telegram_user_id(update.message.from_user.id)
+            response_text = (
+                'Great! I know you as *' + user.name + '*.\n'
+                'Send me another name or /skip if you don\'t want to change it.')
+        else:
+            response_text = 'Great! What\'s your name?'
+
+        update.message.reply_text(response_text,
+                                  parse_mode=ParseMode.MARKDOWN,
+                                  reply_markup=ReplyKeyboardRemove())
+        return NAME
+    except:
+        return abort_error(update, context)
+
+
+def name_skip(update, context):
+    try:
+        if 'register_update' in context.user_data:
+            return send_confirm_message(update, context)
+        else:
+            response_text = (
+                'You prankster, you! There is no way to skip this step!\n'
+                'Tell me your name or send /cancel to abort the registration.')
+            update.message.reply_text(response_text,
+                                      reply_markup=ReplyKeyboardRemove())
+            return NAME
+    except:
+        return abort_error(update, context)
+
+def name(update, context):
+    try:
+        name = update.message.text
+
+        with app_context():
+            if len(name) < 3:
+                response_text = (
+                    'Ooops, that name seems to be a little too short! Please give me your full name.\n'
+                    '_Send /cancel to abort the registration process._')
+                update.message.reply_text(response_text,
+                                          parse_mode=ParseMode.MARKDOWN,
+                                          reply_markup=ReplyKeyboardRemove())
+                return NAME
+
+        context.user_data['register_name'] = name
+
+        return send_confirm_message(update, context)
+    except:
+        return abort_error(update, context)
+
+
+def send_confirm_message(update, context):
+    update_text = None
+
+    if 'register_update' in context.user_data:
+        if 'register_name' in context.user_data:
+            update_text = 'I will change the name to "' + context.user_data['register_name'] + '"'
+        if 'register_email' in context.user_data:
+            if update_text:
+                update_text += ' and the associated email address to "' + context.user_data['register_email'] + '"'
+            else:
+                update_text = 'I will change the email address to "' + context.user_data['register_email'] + '"'
+        
+        if not update_text:
+            response_text = (
+                'Great! There is nothing to update!\n'
+                'Thank you for reviewing the account information.')
+            update.message.reply_text(response_text,
+                                    parse_mode=ParseMode.MARKDOWN,
+                                    reply_markup=ReplyKeyboardRemove())
+            context.user_data.clear()
+            return ConversationHandler.END
+        else:
+            update_text += '.'
+    else:
+        update_text = (
+            'I will create a new account with the name "' + context.user_data['register_name'] + '" '
+            'and the email address "' + context.user_data['register_email'] + '".')
+
+    response_text = (
+        'Great!\n\n' + update_text + '\n\n'
+        'Is that okay?')
+    reply_keyboard = [['Yes', 'No']]
+    update.message.reply_text(response_text,
+                              reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=False))
+    return CONFIRM
+
+
+def confirm(update, context):
+    try:
+        reply = update.message.text
+        if reply == 'Yes':
+            telegram_chat_id = update.message.chat_id
+            telegram_user_id = update.message.from_user.id
+            email = context.user_data['register_email'] if 'register_email' in context.user_data else None
+            name = context.user_data['register_name'] if 'register_name' in context.user_data else None
+
+            with app_context():
+                if 'register_update' in context.user_data:
+                    user = UserController.get_by_telegram_user_id(telegram_user_id)
+
+                    if user:
+                        UserController.update(user, name, email, telegram_chat_id)
+                        response_text = 'Successfully updated the account information.'
+                        if 'register_email' in context.user_data:
+                            response_text += (
+                                '\n\nYour account has been locked '
+                                'until you confirm the new email address.\n'
+                                'Please check your inbox.')
+                        update.message.reply_text(response_text, reply_markup=ReplyKeyboardRemove())
+                        context.user_data.clear()
+                        return ConversationHandler.END
+                    else:
+                        return abort_error(update, context)
+                else:
+                    UserController.create(name, email, telegram_user_id, telegram_chat_id)
+
+                    response_text = (
+                        'Successfully created your account.\n\n'
+                        'Do you want to set a password for your account?')
+                    reply_keyboard = [['Yes', 'No']]
+                    update.message.reply_text(response_text,
+                        reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=False))
+                    return PASSWORD
+        else:
+            if 'register_update' in context.user_data:
+                response_text = 'I did not change any account information. Aborted.'
+            else:
+                response_text = (
+                    'I did not create a new account.\n\n'
+                    'Restart the registration process with /register.')
+
+            update.message.reply_text(response_text, reply_markup=ReplyKeyboardRemove())
+            context.user_data.clear()
+            return ConversationHandler.END
+    except:
+        return abort_error(update, context)
+
+
+def password(update, context):
+    try:
+        reply = update.message.text
+        if reply.startswith('/cancel'):
+            return
+        if reply == 'Yes':
+            with app_context():
+                user = UserController.get_by_telegram_user_id(update.message.from_user.id)
+                password_reset_link = UserController.get_password_reset_link(user)
+                response_text = (
+                    '[Click here](' + password_reset_link + ') to set a password.\n\n'
+                    '_You have reached the end of the registration process._')
+        else:
+            response_text = 'You have reached the end of the registration process.'
+
+        update.message.reply_text(response_text,
+                                  parse_mode=ParseMode.MARKDOWN,
+                                  reply_markup=ReplyKeyboardRemove())
+        context.user_data.clear()
+        return ConversationHandler.END 
+    except:
+        return abort_error(update, context)
+
+
+def cancel(update, context):
+    update.message.reply_text('The registration process has been cancelled.',
+                              reply_markup=ReplyKeyboardRemove())
+    context.user_data.clear()
+    return ConversationHandler.END
+
+
+def abort_error(update, context):
+    update.message.reply_text((
+        'Ooops, something went horribly wrong on my side.\n'
+        'The registration had to be aborted.\n'
+        'Restart the registration with /register.'),
+        reply_markup=ReplyKeyboardRemove())
+    context.user_data.clear()
+    return ConversationHandler.END
+
+register_conv_handler = ConversationHandler(
+    entry_points=[CommandHandler('register', register_command)],
+    states={
+        # REGISTER_TYPE: [MessageHandler(Filters.regex('^(Update|Set Password|Unlink|Cancel)$'), gender)],
+        REGISTER_TYPE: [MessageHandler(Filters.regex('^(?!/cancel).*$'), register_type)],
+        EMAIL: [CommandHandler('skip', email_skip),
+                MessageHandler(Filters.regex('^(?!/cancel).*$'), email)],
+        NAME: [CommandHandler('skip', name_skip),
+               MessageHandler(Filters.regex('^(?!/cancel).*$'), name)],
+        CONFIRM: [MessageHandler(Filters.regex('^(?!/cancel).*$'), confirm)],
+        PASSWORD: [MessageHandler(Filters.regex('^(?!/cancel).*$'), password)]
+    },
+    fallbacks=[CommandHandler('cancel', cancel)]
+)
diff --git a/bot/commands/user.py b/bot/commands/user.py
new file mode 100644
index 0000000000000000000000000000000000000000..96c8bb2cf27cca9c9629ad511875555b7158fa41
--- /dev/null
+++ b/bot/commands/user.py
@@ -0,0 +1,75 @@
+from telegram import ParseMode, ReplyKeyboardRemove
+from app.controllers import UserController, ReservationController, RecordController
+from app import app_context
+
+def resetpassword_command(update, context):
+    """Send a message when the command /resetpassword is issued."""
+    with app_context():
+        user = UserController.get_by_telegram_user_id(update.message.from_user.id)
+        if user:
+            password_reset_link = UserController.get_password_reset_link(user)
+            response_text = '[Click here](' + password_reset_link + ') to set a new password.'
+        else:
+            response_text = (
+                'You telegram user is not associated with an account!\n'
+                'Please /register first.')
+    update.message.reply_text(response_text,
+                              parse_mode=ParseMode.MARKDOWN,
+                              reply_markup=ReplyKeyboardRemove())
+
+
+def resendconfirmationemail_command(update, context):
+    """Send a message when the command /resendconfirmationemail is issued."""
+    with app_context():
+        user = UserController.get_by_telegram_user_id(update.message.from_user.id)
+        if user:
+            if user.is_confirmed:
+                response_text = 'Your email address is already confirmed!'
+            else:
+                UserController.send_confirm_email(user)
+                response_text = (
+                    'An email with instructions have been sent to ' + user.email + '.\n'
+                    'Please check your inbox.')
+        else:
+            response_text = (
+                'You telegram user is not associated with an account!\n'
+                'Please /register first.')
+    update.message.reply_text(response_text,
+                              parse_mode=ParseMode.MARKDOWN,
+                              reply_markup=ReplyKeyboardRemove())
+
+
+def status_command(update, context):
+    """Send a message when the command /status is issued."""
+    with app_context():
+        user = UserController.get_by_telegram_user_id(update.message.from_user.id)
+        if user:
+            response_text = (
+                'Account of ' + user.name + ' (' + user.email + ')\n\n')
+            if not user.is_confirmed:
+                response_text += (
+                    'Your account is locked until the email address is confirmed!\n\n'
+                    'Request another confirmation email with /resendconfirmationemail.')
+            else:
+                reservation = ReservationController.get_active_reservation(user)
+                record = RecordController.get_active_record(user)
+
+                if record:
+                    response_text += (
+                        'You have reported to be at the workshop.\n'
+                        'Do you want to make the workplace free for others and /leave?')
+                elif reservation:
+                    response_text += (
+                        'You have reserved a workplace until ' + reservation.time_end.strftime("%H:%M") + '\n'
+                        '/cancel reservation or /enter workshop.')
+                else:
+                    response_text += (
+                        'It\'s really boring here!\n\n'
+                        'Do you want to /reserve a workplace or /enter the workshop?')
+        else:
+            response_text = (
+                'You telegram user is not associated with an account!\n'
+                'Please /register first.')
+    update.message.reply_text(response_text,
+                              parse_mode=ParseMode.MARKDOWN,
+                              reply_markup=ReplyKeyboardRemove())
\ No newline at end of file
diff --git a/bot/commands/workplace.py b/bot/commands/workplace.py
new file mode 100644
index 0000000000000000000000000000000000000000..62400fb8c62d02b3c8c4590bd57b8266169e173e
--- /dev/null
+++ b/bot/commands/workplace.py
@@ -0,0 +1,129 @@
+from telegram import ParseMode, ReplyKeyboardRemove
+from app.controllers import FreeWorkplacesController, UserController, ReservationController, RecordController
+from app.exceptions import NoFreeWorkplaceError, ActiveReservationExistsError, ActiveRecordExistsError, NoActiveRecordError
+from app import app_context
+
+
+def freeworkplaces_command(update, context):
+    """Send a message when the command /freeworkplaces is issued."""
+    with app_context():
+        free_workplaces = FreeWorkplacesController.get_free_workplaces()
+    response_text  = '*' + str(free_workplaces) + '* workplaces are currently available.'
+    if 'email' in context.user_data:
+        response_text += '\n\nYour email address is ' + context.user_data['email']
+    update.message.reply_text(response_text,
+                              parse_mode=ParseMode.MARKDOWN,
+                              reply_markup=ReplyKeyboardRemove())
+
+
+def reserve_command(update, context):
+    """Send a message when the command /reserve is issued."""
+    with app_context():
+        user = UserController.get_by_telegram_user_id(update.message.from_user.id)
+        if user and user.is_confirmed:
+            try:
+                reservation = UserController.reserve(user)
+                response_text = (
+                    'You have a reserved workplace until ' + reservation.time_end.strftime("%H:%M") + '\n'
+                    '/cancel reservation or /enter workshop.')
+            except NoFreeWorkplaceError:
+                response_text = (
+                    'There are no workplaces available at the moment. '
+                    'Please try again later.')
+            except ActiveReservationExistsError:
+                reservation = ReservationController.get_active_reservation(user)
+                response_text = (
+                    'You already have a reservation valid until ' + reservation.time_end.strftime("%H:%M") + '\n'
+                    '/cancel reservation or /enter workshop.')
+            except ActiveRecordExistsError:
+                record = RecordController.get_active_record(user)
+                response_text = (
+                    'You already reported to be at the workshop!\n'
+                    'Send /leave if you are not there anymore.')
+        elif user and not user.is_confirmed:
+            response_text = (
+                'Your email address ' + user.email + ' is not confirmed yet!\n'
+                'Please confirm your email address first. '
+                'If you didn\'t get an email, /resendconfirmationemail.')
+        else:
+            response_text = (
+                'Your telegram user is not linked to an account!\n'
+                'Please /register first and confirm your email address.')
+    update.message.reply_text(response_text,
+                              parse_mode=ParseMode.MARKDOWN,
+                              reply_markup=ReplyKeyboardRemove())
+
+
+def cancel_command(update, context):
+    """Send a message when the command /cancel is issued."""
+    with app_context():
+        user = UserController.get_by_telegram_user_id(update.message.from_user.id)
+        if user:
+            reservation = ReservationController.get_active_reservation(user)
+
+            if reservation:
+                ReservationController.cancel(reservation)
+                response_text = 'Your reservation has been cancelled.'
+            else:
+                response_text = 'You do not have a reservation!'
+        else:
+            response_text = (
+                'Your telegram user is not linked to an account!\n'
+                'Please /register first and confirm your email address.')
+    update.message.reply_text(response_text,
+                              parse_mode=ParseMode.MARKDOWN,
+                              reply_markup=ReplyKeyboardRemove())
+
+
+def enter_command(update, context):
+    """Send a message when the command /enter is issued."""
+    with app_context():
+        user = UserController.get_by_telegram_user_id(update.message.from_user.id)
+        if user and user.is_confirmed:
+            try:
+                UserController.start_record(user)
+
+                response_text = 'You may enter the workshop now.'
+            except NoFreeWorkplaceError:
+                response_text = (
+                    'There are no workplaces available at the moment. '
+                    'Please try again later.')
+            except ActiveRecordExistsError:
+                response_text = 'You already reported to be at the workshop!'
+        elif user and not user.is_confirmed:
+            response_text = (
+                'Your email address ' + user.email + ' is not confirmed yet!\n'
+                'Please confirm your email address first. '
+                'If you didn\'t get an email, /resendconfirmationemail.')
+        else:
+            response_text = (
+                'Your telegram user is not linked to an account!\n'
+                'Please /register first and confirm your email address.')
+    update.message.reply_text(response_text,
+                              parse_mode=ParseMode.MARKDOWN,
+                              reply_markup=ReplyKeyboardRemove())
+
+
+def leave_command(update, context):
+    """Send a message when the command /leave is issued."""
+    with app_context():
+        user = UserController.get_by_telegram_user_id(update.message.from_user.id)
+        if user:
+            try:
+                UserController.terminate_record(user)
+
+                response_text = 'Thank you for the visit. Stay healthy.'
+            except NoActiveRecordError:
+                response_text = (
+                    'You did not report to be at the workshop!\n'
+                    'If that was a mistake, send an email to '
+                    'info@bastli.ethz.ch with the time when you were here.')
+            except ActiveRecordExistsError:
+                response_text = 'You already reported to be at the workshop!'
+        else:
+            response_text = (
+                'Your telegram user is not linked to an account!\n'
+                'Please /register first and confirm your email address.')
+    update.message.reply_text(response_text,
+                              parse_mode=ParseMode.MARKDOWN,
+                              reply_markup=ReplyKeyboardRemove())