diff --git a/addons/calendar_sms/__init__.py b/addons/calendar_sms/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..dc5e6b693d19dcacd224b7ab27b26f75e66cb7b2
--- /dev/null
+++ b/addons/calendar_sms/__init__.py
@@ -0,0 +1,4 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from . import models
diff --git a/addons/calendar_sms/__manifest__.py b/addons/calendar_sms/__manifest__.py
new file mode 100644
index 0000000000000000000000000000000000000000..0e734074aaa4ec9a84b5781b6950d7f375769951
--- /dev/null
+++ b/addons/calendar_sms/__manifest__.py
@@ -0,0 +1,16 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+{
+    'name': "Calendar SMS",
+    'summary': 'Send text messages as event reminders',
+    'description': "Send text messages as event reminders",
+    'category': 'Hidden',
+    'version': '1.0',
+    'depends': ['calendar', 'sms'],
+    'data': [
+        'views/calendar_views.xml',
+    ],
+    'application': False,
+    'auto_install': True,
+}
diff --git a/addons/calendar_sms/i18n/calendar_sms.pot b/addons/calendar_sms/i18n/calendar_sms.pot
new file mode 100644
index 0000000000000000000000000000000000000000..073430308ff663532d665214dad14c40f05f1f7e
--- /dev/null
+++ b/addons/calendar_sms/i18n/calendar_sms.pot
@@ -0,0 +1,49 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+#	* calendar_sms
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 10.saas~18+e\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2017-09-20 11:33+0000\n"
+"PO-Revision-Date: 2017-09-20 11:33+0000\n"
+"Last-Translator: <>\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: \n"
+
+#. module: calendar_sms
+#: model:ir.model,name:calendar_sms.model_calendar_event
+msgid "Event"
+msgstr ""
+
+#. module: calendar_sms
+#: model:ir.model,name:calendar_sms.model_calendar_alarm
+msgid "Event alarm"
+msgstr ""
+
+#. module: calendar_sms
+#: code:addons/calendar_sms/models/calendar.py:23
+#, python-format
+msgid "Event reminder: %s on %s."
+msgstr ""
+
+#. module: calendar_sms
+#: code:addons/calendar_sms/models/calendar.py:24
+#, python-format
+msgid "SMS text message reminder sent !"
+msgstr ""
+
+#. module: calendar_sms
+#: model:ir.actions.act_window,name:calendar_sms.sms_message_send_action_mutli
+msgid "Send SMS to attendees"
+msgstr ""
+
+#. module: calendar_sms
+#: model:ir.model,name:calendar_sms.model_calendar_alarm_manager
+msgid "calendar.alarm_manager"
+msgstr ""
+
diff --git a/addons/calendar_sms/models/__init__.py b/addons/calendar_sms/models/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..a71750ab34d296f5354481d53b6c2f566dbd74e5
--- /dev/null
+++ b/addons/calendar_sms/models/__init__.py
@@ -0,0 +1,4 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from . import calendar
diff --git a/addons/calendar_sms/models/calendar.py b/addons/calendar_sms/models/calendar.py
new file mode 100644
index 0000000000000000000000000000000000000000..d22200b9363235ee5cb98db1bb605de745d73de1
--- /dev/null
+++ b/addons/calendar_sms/models/calendar.py
@@ -0,0 +1,75 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+import logging
+
+from odoo import api, fields, models, _
+
+_logger = logging.getLogger(__name__)
+
+
+class CalendarEvent(models.Model):
+    _inherit = 'calendar.event'
+
+    def _get_default_sms_recipients(self):
+        """ Method overriden from mail.thread (defined in the sms module).
+            SMS text messages will be sent to attendees that haven't declined the event(s).
+        """
+        return self.mapped('attendee_ids').filtered(lambda att: att.state != 'declined').mapped('partner_id')
+
+    def _do_sms_reminder(self):
+        """ Send an SMS text reminder to attendees that haven't declined the event """
+        for event in self:
+            sms_msg = _("Event reminder: %s on %s.") % (event.name, event.start_datetime or event.start_date)
+            note_msg = _('SMS text message reminder sent !')
+            event.message_post_send_sms(sms_msg, note_msg=note_msg)
+
+
+class CalendarAlarm(models.Model):
+    _inherit = 'calendar.alarm'
+
+    type = fields.Selection(selection_add=[('sms', 'SMS Text Message')])
+
+
+class AlarmManager(models.AbstractModel):
+    _inherit = 'calendar.alarm_manager'
+
+    @api.model
+    def get_next_mail(self):
+        """ Cron method, overriden here to send SMS reminders as well
+        """
+        result = super(AlarmManager, self).get_next_mail()
+        now = fields.Datetime.now()
+        last_sms_cron = self.env['ir.config_parameter'].get_param('calendar_sms.last_sms_cron', default=now)
+        cron = self.env['ir.model.data'].get_object('calendar', 'ir_cron_scheduler_alarm')
+
+        interval_to_second = {
+            "weeks": 7 * 24 * 60 * 60,
+            "days": 24 * 60 * 60,
+            "hours": 60 * 60,
+            "minutes": 60,
+            "seconds": 1
+        }
+
+        cron_interval = cron.interval_number * interval_to_second[cron.interval_type]
+        events_data = self.get_next_potential_limit_alarm('sms', seconds=cron_interval)
+
+        for event in self.env['calendar.event'].browse(events_data):
+            max_delta = events_data[event.id]['max_duration']
+
+            if event.recurrency:
+                found = False
+                for event_start in event._get_recurrent_date_by_event():
+                    event_start = event_start.replace(tzinfo=None)
+                    last_found = self.do_check_alarm_for_one_date(event_start, event, max_delta, 0, 'sms', after=last_sms_cron, missing=True)
+                    for alert in last_found:
+                        event.browse(alert['event_id'])._do_sms_reminder()
+                        found = True
+                    if found and not last_found:  # if the precedent event had an alarm but not this one, we can stop the search for this event
+                        break
+            else:
+                event_start = fields.Datetime.from_string(event.start)
+                for alert in self.do_check_alarm_for_one_date(event_start, event, max_delta, 0, 'sms', after=last_sms_cron, missing=True):
+                    event.browse(alert['event_id'])._do_sms_reminder()
+        self.env['ir.config_parameter'].set_param('calendar_sms.last_sms_cron', now)
+        return result
diff --git a/addons/calendar_sms/views/calendar_views.xml b/addons/calendar_sms/views/calendar_views.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4682a7098bfd202b11936a1ba4706ca899bb21bf
--- /dev/null
+++ b/addons/calendar_sms/views/calendar_views.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<odoo>
+
+    <!-- Add action entry in the Action Menu for Events -->
+    <act_window id="sms_message_send_action_mutli"
+        name="Send SMS to attendees"
+        src_model="calendar.event"
+        res_model="sms.send_sms"
+        view_type="form"
+        view_mode="form"
+        key2="client_action_multi"
+        target="new"/>
+
+</odoo>
diff --git a/addons/sms/__init__.py b/addons/sms/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..2ae6446f9dc25fe7563ce0c6a4f1cc4904124cc5
--- /dev/null
+++ b/addons/sms/__init__.py
@@ -0,0 +1,5 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from . import models
+from . import wizard
diff --git a/addons/sms/__manifest__.py b/addons/sms/__manifest__.py
new file mode 100644
index 0000000000000000000000000000000000000000..18330c1d7936d83ca89f8b7339f7199dbe41b31e
--- /dev/null
+++ b/addons/sms/__manifest__.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+{
+    'name': 'SMS gateway',
+    'category': 'Tools',
+    'summary': 'SMS Text Messaging',
+    'description': """
+This module gives a framework for SMS text messaging
+----------------------------------------------------
+
+The service is provided by the In App Purchase Odoo platform.
+""",
+    'depends': ['base', 'iap', 'mail'],
+    'data': [
+        'wizard/send_sms_views.xml',
+        'views/res_partner_views.xml',
+    ],
+    'installable': True,
+    'auto_install': True,
+}
diff --git a/addons/sms/models/__init__.py b/addons/sms/models/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..7a75a7a5f6d8db50e84f7937b9baed3ec70b42c6
--- /dev/null
+++ b/addons/sms/models/__init__.py
@@ -0,0 +1,6 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from . import mail_thread
+from . import res_partner
+from . import sms_api
diff --git a/addons/sms/models/mail_thread.py b/addons/sms/models/mail_thread.py
new file mode 100644
index 0000000000000000000000000000000000000000..1fb62a72de4b36a71aca4e3df3142f660439fd22
--- /dev/null
+++ b/addons/sms/models/mail_thread.py
@@ -0,0 +1,59 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+import logging
+
+from odoo import models, _
+
+from odoo.addons.iap.models.iap import InsufficientCreditError
+
+_logger = logging.getLogger(__name__)
+
+
+class MailThread(models.AbstractModel):
+    _inherit = 'mail.thread'
+
+    def _get_default_sms_recipients(self):
+        """ This method will likely need to be overriden by inherited models.
+               :returns partners: recordset of res.partner
+        """
+        partners = self.env['res.partner']
+        if hasattr(self, 'partner_id'):
+            partners |= self.mapped('partner_id')
+        if hasattr(self, 'partner_ids'):
+            partners |= self.mapped('partner_ids')
+        return partners
+
+    def message_post_send_sms(self, sms_message, numbers=None, partners=None, note_msg=None, log_error=False):
+        """ Send an SMS text message and post an internal note in the chatter if successfull
+            :param sms_message: plaintext message to send by sms
+            :param partners: the numbers to send to, if none are given it will take those
+                                from partners or _get_default_sms_recipients
+            :param partners: the recipients partners, if none are given it will take those
+                                from _get_default_sms_recipients, this argument
+                                is ignored if numbers is defined
+            :param note_msg: message to log in the chatter, if none is given a default one
+                             containing the sms_message is logged
+        """
+        if not numbers:
+            if not partners:
+                partners = self._get_default_sms_recipients()
+
+                # Collect numbers, we will consider the message to be sent if at least one number can be found
+                numbers = list(set([i.mobile for i in partners if i.mobile]))
+
+        if numbers:
+            try:
+                self.env['sms.api']._send_sms(numbers, sms_message)
+                mail_message = note_msg or _('SMS message sent: %s') % sms_message
+
+            except InsufficientCreditError as e:
+                if not log_error:
+                    raise e
+                mail_message = _('Insufficient credit, unable to send SMS message: %s') % sms_message
+        else:
+            mail_message = _('No mobile number defined, unable to send SMS message: %s') % sms_message
+
+        for thread in self:
+            thread.message_post(body=mail_message)
+        return False
diff --git a/addons/sms/models/res_partner.py b/addons/sms/models/res_partner.py
new file mode 100644
index 0000000000000000000000000000000000000000..6d85ef2fe06453aeb1fa930227a838f728ed9d8c
--- /dev/null
+++ b/addons/sms/models/res_partner.py
@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import models
+
+
+class ResPartner(models.Model):
+    _inherit = 'res.partner'
+
+    def _get_default_sms_recipients(self):
+        """ Override of mail.thread method.
+            SMS recipients on partners are the partners themselves.
+        """
+        return self
diff --git a/addons/sms/models/sms_api.py b/addons/sms/models/sms_api.py
new file mode 100644
index 0000000000000000000000000000000000000000..1498ce54d98af9bc69a58ac4a7f676290762629c
--- /dev/null
+++ b/addons/sms/models/sms_api.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import api, fields, models
+from odoo.exceptions import UserError
+from odoo.addons.iap.models import iap
+
+DEFAULT_ENDPOINT = 'https://iap-sms.odoo.com'
+
+
+class SmsApi(models.AbstractModel):
+    _name = 'sms.api'
+
+    @api.model
+    def _send_sms(self, numbers, message):
+        """ Send sms
+        """
+        account = self.env['iap.account'].get('sms')
+        params = {
+            'account_token': account.account_token,
+            'numbers': numbers,
+            'message': message,
+        }
+        endpoint = self.env['ir.config_parameter'].sudo().get_param('sms.endpoint', DEFAULT_ENDPOINT)
+        r = iap.jsonrpc(endpoint + '/iap/message_send', params=params)
+        return True
diff --git a/addons/sms/views/res_partner_views.xml b/addons/sms/views/res_partner_views.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a35d5f10516512a64f19152e005ee8e2700cd09b
--- /dev/null
+++ b/addons/sms/views/res_partner_views.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+
+    <!-- Add action entry in the Action Menu for Partners -->
+    <record id="partner_form_send_sms_form_view" model="ir.ui.view">
+        <field name="name">res.partner.form.send.sms</field>
+        <field name="model">res.partner</field>
+        <field name="inherit_id" ref="base.view_partner_form"/>
+        <field name="priority">10</field>
+        <field name="arch" type="xml">
+            <xpath expr="//field[@name='phone']" position="replace">
+                <label for="phone"/>
+                <div class="o_row">
+                    <field name="phone" widget="phone"/>
+                    <button
+                        type="action"
+                        name="%(sms.send_sms_form_action)d"
+                        class="btn-xs btn-link mb4 fa fa-envelope-o"
+                        attrs="{'invisible':[('phone', '=', False)]}"
+                        context="{'field_name': 'phone'}"
+                    />
+                </div>
+            </xpath>
+            <xpath expr="//field[@name='mobile']" position="replace">
+                <label for="mobile"/>
+                <div class="o_row">
+                    <field name="mobile" widget="phone"/>
+                    <button
+                        type="action"
+                        name="%(sms.send_sms_form_action)d"
+                        class="btn-xs btn-link mb4 fa fa-envelope-o"
+                        attrs="{'invisible':[('mobile', '=', False)]}"
+                        context="{'field_name': 'mobile'}"
+                        />
+                    </div>
+                </xpath>
+        </field>
+    </record>
+
+</odoo>
diff --git a/addons/sms/wizard/__init__.py b/addons/sms/wizard/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..937147a0b4358d89587a181a4ee4b08729238292
--- /dev/null
+++ b/addons/sms/wizard/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+from . import send_sms
diff --git a/addons/sms/wizard/send_sms.py b/addons/sms/wizard/send_sms.py
new file mode 100644
index 0000000000000000000000000000000000000000..25fa0828934ea009878b38502a2216568738b2f0
--- /dev/null
+++ b/addons/sms/wizard/send_sms.py
@@ -0,0 +1,62 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+import logging
+
+
+from odoo import api, fields, models
+from odoo.exceptions import UserError
+from odoo.addons.iap.models import iap
+
+_logger = logging.getLogger(__name__)
+
+
+class SendSMS(models.TransientModel):
+    _name = 'sms.send_sms'
+
+    recipients = fields.Char('Recipients', required=True)
+    message = fields.Text('Message', required=True)
+
+    def _get_records(self, model):
+        if self.env.context.get('active_domain'):
+            records = model.search(self.env.context.get('active_domain'))
+        elif self.env.context.get('active_ids'):
+            records = model.browse(self.env.context.get('active_ids', []))
+        else:
+            records = model.browse(self.env.context.get('active_id', []))
+        return records
+
+    @api.model
+    def default_get(self, fields):
+        result = super(SendSMS, self).default_get(fields)
+
+        active_model = self.env.context.get('active_model')
+        model = self.env[active_model]
+
+        records = self._get_records(model)
+        if getattr(records, '_get_default_sms_recipients'):
+            partners = records._get_default_sms_recipients()
+            phone_numbers = []
+            no_phone_partners = []
+            for partner in records:
+                number = partner.mobile
+                if number:
+                    phone_numbers.append(number)
+                else:
+                    no_phone_partners.append(partner.name)
+            if len(partners) > 1:
+                if no_phone_partners:
+                    raise UserError(_('Missing mobile number for %s.') % ', '.join(no_phone_partners))
+            result['recipients'] = ', '.join(phone_numbers)
+        return result
+
+    def action_send_sms(self):
+        numbers = self.recipients.split(',')
+
+        active_model = self.env.context.get('active_model')
+        model = self.env[active_model]
+        records = self._get_records(model)
+        if getattr(records, 'message_post_send_sms'):
+            records.message_post_send_sms(self.message, numbers=numbers)
+        else:
+            self.env['sms.api']._send_sms(numbers, self.message)
+        return True
diff --git a/addons/sms/wizard/send_sms_views.xml b/addons/sms/wizard/send_sms_views.xml
new file mode 100644
index 0000000000000000000000000000000000000000..34746c32825ce9b23806834d5878f6ec31d120c8
--- /dev/null
+++ b/addons/sms/wizard/send_sms_views.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+
+    <record id="send_sms_view_form" model="ir.ui.view">
+        <field name="name">sms_send_sms.form</field>
+        <field name="model">sms.send_sms</field>
+        <field name="arch" type="xml">
+            <form string="Send an SMS">
+                <sheet>
+                    <group>
+                        <field name="recipients"/>
+                        <field name="message" widget="text"/>
+                    </group>
+                </sheet>
+                <footer>
+                    <group>
+                        <span>
+                            <button string="Send" type="object" class="oe_highlight" name="action_send_sms"/>
+                            <button string="Cancel" class="oe_link" special="cancel" />
+                        </span>
+                    </group>
+                </footer>
+            </form>
+        </field>
+    </record>
+    <record id="send_sms_form_action" model="ir.actions.act_window">
+        <field name="name">Send SMS</field>
+        <field name="res_model">sms.send_sms</field>
+        <field name="view_type">form</field>
+        <field name="view_mode">form</field>
+        <field name="target">new</field>
+    </record>
+
+    <!-- Add action entry in the Action Menu for Partners -->
+    <act_window id="send_sms_action"
+        name="Send SMS"
+        src_model="res.partner"
+        res_model="sms.send_sms"
+        view_type="form"
+        view_mode="form"
+        key2="client_action_multi"
+        target="new"/>
+
+</odoo>