diff --git a/lib/kolab_sync_data_calendar.php b/lib/kolab_sync_data_calendar.php
--- a/lib/kolab_sync_data_calendar.php
+++ b/lib/kolab_sync_data_calendar.php
@@ -185,20 +185,9 @@
         $config = $this->getFolderConfig($event['_mailbox']);
         $result = array();
 
-        // Timezone
         // Kolab Format 3.0 and xCal does support timezone per-date, but ActiveSync allows
         // only one timezone per-event. We'll use timezone of the start date
-        if ($event['start'] instanceof DateTime) {
-            $timezone = $event['start']->getTimezone();
-
-            if ($timezone && ($tz_name = $timezone->getName()) != 'UTC') {
-                $tzc = kolab_sync_timezone_converter::getInstance();
-
-                if ($tz_name = $tzc->encodeTimezone($tz_name, $event['start']->format('Y-m-d'))) {
-                    $result['timezone'] = $tz_name;
-                }
-            }
-        }
+        $result['timezone'] = kolab_sync_timezone_converter::encodeTimezoneFromDate($event['start']);
 
         // Calendar namespace fields
         foreach ($this->mapping as $key => $name) {
diff --git a/lib/kolab_sync_data_email.php b/lib/kolab_sync_data_email.php
--- a/lib/kolab_sync_data_email.php
+++ b/lib/kolab_sync_data_email.php
@@ -118,6 +118,48 @@
     }
 
     /**
+     * Encode a globalObjId according to https://interoperability.blob.core.windows.net/files/MS-ASEMAIL/%5bMS-ASEMAIL%5d-150526.pdf 2.2.2.3
+     *
+     * @param array $data An array with the data to encode
+     *
+     * @return string the encoded globalObjId
+     */
+    public static function encodeGlobalObjId(array $data): string
+    {
+        $classid = "040000008200e00074c5b7101a82e008";
+        $uid = $data['uid'];
+        $vcalid = "vCal-Uid\1\0\0\0{$uid}\0";
+
+        $packed = pack(
+            "H32nCCPx8Va*",
+            $classid,
+            $data['year'] ?? 0,
+            $data['month'] ?? 0,
+            $data['day'] ?? 0,
+            $data['now'] ?? 0,
+            strlen($vcalid),
+            $vcalid
+        );
+
+        return base64_encode($packed);
+    }
+
+    /**
+     * Decode a globalObjId according to https://interoperability.blob.core.windows.net/files/MS-ASEMAIL/%5bMS-ASEMAIL%5d-150526.pdf 2.2.2.3
+     *
+     * @param string the encoded globalObjId
+     *
+     * @return array An array with the decoded data
+     */
+    public static function decodeGlobalObjId(string $globalObjId): array
+    {
+        $unpackString = 'H32classid/nyear/Cmonth/Cday/Pnow/x8/Vbytecount/a*data';
+        $decoded = unpack($unpackString, base64_decode($globalObjId));
+        $decoded['uid'] = substr($decoded['data'], strlen("vCal-Uid\1\0\0\0"), -1);
+        return $decoded;
+    }
+
+    /**
      * Creates model object
      *
      * @param Syncroton_Model_SyncCollection $collection Collection data
@@ -339,6 +381,9 @@
         $result['nativeBodyType'] = $message->has_html_part() ? 2 : 1;
 
         // Message class
+        $result['messageClass'] = 'IPM.Note';
+        $result['contentClass'] = 'urn:content-classes:message';
+
         if ($headers->ctype == 'multipart/signed'
             && !empty($message->parts[1])
             && $message->parts[1]->mimetype == 'application/pkcs7-signature'
@@ -348,11 +393,61 @@
         else if ($headers->ctype == 'application/pkcs7-mime' || $headers->ctype == 'application/x-pkcs7-mime') {
             $result['messageClass'] = 'IPM.Note.SMIME';
         }
-        else {
-            $result['messageClass'] = 'IPM.Note';
-        }
+        else if ($event = $this->get_invitation_event_from_message($message)) {
+            $result['messageClass'] = 'IPM.Schedule.Meeting.Request';
+            $result['contentClass'] = 'urn:content-classes:calendarmessage';
+
+            $meeting = array();
+
+            $meeting['allDayEvent'] = $event['allday'] ?? null ? 1 : 0;
+            $meeting['startTime'] = $event['start'];
+            $meeting['dtStamp'] = $event['created'] ?? null;
+            $meeting['endTime'] = $event['end'] ?? null;
+
+            //TODO implement recurrences. We can't detect exceptions like this (don't know how), and the recurrences structure is different from event,
+            //so that also doesn't work like this.
+            // if (isset($event['recurrence']['EXCEPTIONS'])) {
+            //     $meeting['instanceType'] = Syncroton_Model_EmailMeetingRequest::TYPE_RECURRING_EXCEPTION;
+            //     $this->recurrence_from_kolab($collection, $event, $meeting);
+            // // } else if (isset($event['recurrence'])) {
+            // //     $meeting['instanceType'] = Syncroton_Model_EmailMeetingRequest::TYPE_RECURRING_SINGLE;
+            // //     $meeting['recurrenceId'] = set the date;
+            // } else if (isset($event['recurrence'])) {
+            //     $meeting['instanceType'] = Syncroton_Model_EmailMeetingRequest::TYPE_RECURRING_MASTER;
+            //     $this->recurrence_from_kolab($collection, $event, $meeting);
+            // } else {
+            //     $meeting['instanceType'] = Syncroton_Model_EmailMeetingRequest::TYPE_NORMAL;
+            // }
+            $meeting['instanceType'] = Syncroton_Model_EmailMeetingRequest::TYPE_NORMAL;
+
+            $meeting['location'] = $event['location'] ?? null;
+
+            // Organizer
+            if (!empty($event['attendees'])) {
+                foreach ($event['attendees'] as $idx => $attendee) {
+                    if ($attendee['role'] == 'ORGANIZER') {
+                        if ($email = $attendee['email']) {
+                            $meeting['organizer'] = $email;
+                        }
+                        break;
+                    }
+                }
+            }
 
-        $result['contentClass'] = 'urn:content-classes:message';
+            // Kolab Format 3.0 and xCal does support timezone per-date, but ActiveSync allows
+            // only one timezone per-event. We'll use timezone of the start date
+            $meeting['timeZone'] = kolab_sync_timezone_converter::encodeTimezoneFromDate($event['start']);
+            $meeting['globalObjId'] = self::encodeGlobalObjId(['uid' => $event['uid']]);
+
+            //TODO handle other methods
+            if ($event['_method'] == 'REQUEST') {
+                $meeting['meetingMessageType'] = Syncroton_Model_EmailMeetingRequest::MESSAGE_TYPE_REQUEST;
+            } else {
+                $meeting['meetingMessageType'] = Syncroton_Model_EmailMeetingRequest::MESSAGE_TYPE_NORMAL;
+            }
+
+            $result['meetingRequest'] = new Syncroton_Model_EmailMeetingRequest($meeting);
+        }
 
         // Categories (Tags)
         if (isset($this->tag_categories) && $this->tag_categories) {
@@ -1675,22 +1770,33 @@
     /**
      * Returns calendar event data from the iTip invitation attached to a mail message
      */
+    public function get_invitation_event_from_message($message)
+    {
+        // Parse the message and find iTip attachments
+        $libcal = libcalendaring::get_instance();
+        $libcal->mail_message_load(array('object' => $message));
+        $ical_objects = $libcal->get_mail_ical_objects();
+
+        // We support only one event in the iTip
+        foreach ($ical_objects as $mime_id => $event) {
+            if ($event['_type'] == 'event') {
+                $event['_method'] = $ical_objects->method;
+                return $event;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns calendar event data from the iTip invitation attached to a mail message
+     */
     public function get_invitation_event($messageId)
     {
         // Get the mail message object
         if ($message = $this->getObject($messageId)) {
-            // Parse the message and find iTip attachments
-            $libcal = libcalendaring::get_instance();
-            $libcal->mail_message_load(array('object' => $message));
-            $ical_objects = $libcal->get_mail_ical_objects();
-
-            // We support only one event in the iTip
-            foreach ($ical_objects as $mime_id => $event) {
-                if ($event['_type'] == 'event') {
-                    return $event;
-                }
-            }
+            return $this->get_invitation_event_from_message($message);
         }
+        return null;
     }
 
 
diff --git a/lib/kolab_sync_timezone_converter.php b/lib/kolab_sync_timezone_converter.php
--- a/lib/kolab_sync_timezone_converter.php
+++ b/lib/kolab_sync_timezone_converter.php
@@ -217,6 +217,29 @@
         return $this->_packTimezoneInfo($offsets);
     }
 
+
+    /**
+     * Returns an encoded timezone representation from $date
+     *
+     * @param DateTime $date The date with the timezone to encode
+     *
+     * @return string encoded timezone
+     */
+    public static function encodeTimezoneFromDate($date)
+    {
+        if ($date instanceof DateTime) {
+            $timezone = $date->getTimezone();
+
+            if ($timezone && ($tz_name = $timezone->getName()) != 'UTC') {
+                $tzc = self::getInstance();
+                if ($tz_name = $tzc->encodeTimezone($tz_name, $date->format('Y-m-d'))) {
+                    return $tz_name;
+                }
+            }
+        }
+        return null;
+    }
+
     /**
      * Get offsets for given timezone
      *
diff --git a/tests/globalid_converter.php b/tests/globalid_converter.php
new file mode 100644
--- /dev/null
+++ b/tests/globalid_converter.php
@@ -0,0 +1,32 @@
+<?php
+
+require_once "../lib/kolab_sync_data.php";
+require_once "../lib/kolab_sync_data_email.php";
+
+class globalid_converter extends PHPUnit\Framework\TestCase
+{
+    function test_decode()
+    {
+        // https://learn.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-asemail/e7424ddc-dd10-431e-a0b7-5c794863370e
+        $input = 'BAAAAIIA4AB0xbcQGoLgCAAAAAAAAAAAAAAAAAAAAAAAAAAAMwAAAHZDYWwtVWlkAQAAAHs4MTQxMkQzQy0yQTI0LTRFOUQtQjIwRS0xMUY3QkJFOTI3OTl9AA==';
+        $output = kolab_sync_data_email::decodeGlobalObjId($input);
+
+        $this->assertSame(51, $output['bytecount']);
+        $this->assertSame('{81412D3C-2A24-4E9D-B20E-11F7BBE92799}', $output['uid']);
+
+        $encoded = kolab_sync_data_email::encodeGlobalObjId($output);
+        $this->assertSame($encoded, $input);
+
+
+        $input = 'BAAAAIIA4AB0xbcQGoLgCAfUCRDgQMnBJoXEAQAAAAAAAAAAEAAAAAvw7UtuTulOnjnjhns3jvM=';
+        $output = kolab_sync_data_email::decodeGlobalObjId($input);
+
+        $this->assertSame(16, $output['bytecount']);
+        $this->assertSame(2004, $output['year']);
+        $this->assertSame(9, $output['month']);
+        $this->assertSame(16, $output['day']);
+        //FIXME we don't currently implement non ical uids
+        // $encoded = kolab_sync_data_email::encodeGlobalObjId($output);
+        // $this->assertSame($encoded, $input);
+    }
+}