Exchange Server on premises REST API

Installation

overview

Postman

Postman is a collaboration platform for API development
With Postman you can send requests and view responses easily.

Prerequisites

  • Exchange Server 2016 or later (Exchange Server 2016 must be upgraded to CU3 or later)
  • Allow access to the REST API /api virtual directory and to the /autodiscover/autodiscover.json virtual directory file

IIS - Internet Information Services

To access the REST API, basic authentication must be enabled on the /api virtual directory in IIS.

rest api authentication

Powershell examples

Some examples on how to consume the REST API using powershell.

Mail

Get mails from the authenticated user

$mails = Invoke-RestMethod -Uri "https://mail.domain.ch/api/v2.0/me/messages" -Credential (Get-Credential) 

get email recipients

$mails.value.ToRecipients.EmailAddress 

get mail received date time

$mails.value.ReceivedDateTime | get-date 

get mail sender and subject only

$mails = Invoke-RestMethod -Uri "https://mail.domain.ch/api/v2.0/me/MailFolders/sentitems/messages/?`$select=Sender,Subject" -Credential (Get-Credential) 

send email

$mail = '{
  "Message": {
    "Subject": "first mail sent over REST API",
    "Body": {
      "ContentType": "Text",
      "Content": "This is the content"
    },
    "ToRecipients": [
      {
        "EmailAddress": {
          "Address": "john@domain.ch"
        }
      }
    ]
  },
  "SaveToSentItems": "true"
}'
 
invoke-RestMethod -Uri "https://mail.domain.ch/api/v2.0/me/sendmail"
-Method Post -Body $mail -ContentType "application/json" -Credential (get-credential) 

Appointments

Get appointments from the authenticated user

$events = Invoke-RestMethod -Uri "https://mail.domain.ch/api/v2.0/me/events" -Credential (Get-Credential) 

get event subject

$events.value.Subject 

get event created date times

$events.value.CreatedDateTime | get-date 

get event subject only

$events = Invoke-RestMethod -Uri "https://mail.domain.ch/api/v2.0/me/events?$select=Subject" -Credential (Get-Credential) 

C# Application

overview

Json.Net

Information about serialization and deserialization

Creating a c# Windows Application

For sending HTTP request and receiving HTTP response we are using the class HttpClient.

baseAddress
Gets or Sets the base address of URI of the Internet Resource used when sending requests. 
DefaultRequestHeaders
Sets the headers which should be sent with each request. 

ApiHelper Class

using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace rest_api
{
    public abstract class ApiHelper
    
    public static HttpClient ApiClient { get; set; }
    private static string Username { get; set; }
    private static string Password { get; set; }
    private static ApiVersion { get; set; }
    
    public static void InitializeClient()
    {
        ApiClient = new HttpClient();
        
        /*----- set credentials -----*/
        string username = "jane";
        string password = "youshallnotpass";
        string apiVersion = "v2.0";
        
        var auth = Encoding.ASCII.GetBytes($"{username}:{password});
        
        /*----- set base endpoint url -----*/
        string baseUrl = $"https://mail.domain.ch//api//{apiVersion}//me//";
        ApiClient.baseAddress = new Uri(baseUrl);
        
        /*----- set default request headers -----*/
        ApiClient.DefaultRequestHeaders.Accept.Clear();
        ApiClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        ApiClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(auth));
    
        /// <summary>
        /// Send a PATCH request to the specified Uri as an asynchronous operation.
        /// </summary>
        /// <param name="requestUri">The Uri the request is sent to</param>
        /// <param name="content">The HTTP request content sent to the server.</param>
        /// <returns>The Task object representing the asynchronous operation</returns>
        public static Task<HttpResponseMessage> PatchAsync( string requestUri, HttpContent content)
        {
            return ApiHelper.PatchAsync(CreateUri(requestUri), content);
        }

        /// <summary>
        /// Send a PATCH request to the specified Uri as an asynchronous operation.
        /// </summary>
        /// <param name="requestUri">The Uri the request is sent to</param>
        /// <param name="content">The HTTP request content sent to the server.</param>
        /// <returns>The Task object representing the asynchronous operation</returns>
        public static Task<HttpResponseMessage> PatchAsync(Uri requestUri, HttpContent content) 
        {
            return ApiHelper.PatchAsync(requestUri, content, CancellationToken.None);
        }

        /// <summary>
        /// Send a PATCH request with a cancellation token as an asynchronous operation.
        /// </summary>
        /// <param name="requestUri">The Uri the request is sent to</param>
        /// <param name="content">The HTTP request content sent to the server</param>
        /// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
        /// <returns> The task object representing the asynchronous operation</returns>
        public static Task<HttpResponseMessage> PatchAsync( string requestUri, HttpContent content, CancellationToken cancellationToken)
        {
            return ApiHelper.PatchAsync(CreateUri(requestUri), content, cancellationToken);
        }

        /// <summary>
        /// Send a PATCH request with a cancellation token as an asynchronous operation.
        /// </summary>
        /// <param name="requestUri">The Uri the request is sent to</param>
        /// <param name="content">The HTTP request content sent to the server</param>
        /// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
        /// <returns> The task object representing the asynchronous operation</returns>
        public static Task<HttpResponseMessage> PatchAsync(Uri requestUri, HttpContent content, CancellationToken cancellationToken)
        {
            return ApiClient.SendAsync(new HttpRequestMessage(new HttpMethod("PATCH"), requestUri)
            {
                Content = content
            }, cancellationToken);
        }

        /// <summary>
        /// format uri from string
        /// </summary>
        /// <param name="uri"></param>
        /// <returns></returns>
        private static Uri CreateUri(string uri)
        {
            return string.IsNullOrEmpty(uri) ? null : new Uri(uri, UriKind.RelativeOrAbsolute);
        }
    }
} 

Appointment Container Class

using Newtonsoft.Json;
using System.Collections.Generic;

namespace rest_api.Classes.Appointment
{
    class AppointmentContainer
    {
        [JsonProperty("@odata.context")]
        public string odata_context { get; set; }

        [JsonProperty("value")]
        public List<Appointment> Appointments { get; set;  }

        public void ResetAppointmentList()
        {
            Appointments = new List<Appointment>();
        }
    }
}
 

Appointment Class

using System;
using Newtonsoft.Json;


namespace rest_api.Classes.Appointment
{
    class Appointment
    {
        
        [JsonProperty("@odata.id")]
        public string OdataId { get; set; }

        [JsonProperty("@odata.etag")]
        public string OdataEtag { get; set; }


        [JsonProperty("Id")]
        public string Id { get; set; }

        [JsonProperty("CreatedDateTime")]
        public DateTime CreatedDateTime { get; set; }

        [JsonProperty("LastModifiedDateTime")]
        public DateTime LastModifiedDateTime { get; set; }

        [JsonProperty("ChangeKey")]
        public string ChangeKey { get; set; }

        [JsonProperty("ReminderMinutesBeforeStart")]
        public int ReminderMinutesBeforeStart { get; set; }

        [JsonProperty("IsReminderOn")]
        public bool? IsReminderOn { get; set; }

        [JsonProperty("HasAttachments")]
        public bool? HasAttachments { get; set; }

        [JsonProperty("Subject")]
        public string Subject { get; set; }

        [JsonProperty("BodyPreview")]
        public string BodyPreview { get; set; }

        [JsonProperty("Importance")]
        public string Importance { get; set; }

        [JsonProperty("Sensitivity")]
        public string Sensitivity { get; set; }

        [JsonProperty("IsAllDay")]
        public bool? IsAllDay { get; set; }

        [JsonProperty("IsCancelled")]
        public bool? IsCancelled { get; set; }

        [JsonProperty("IsOrganizer")]
        public bool? IsOrganizer { get; set; }

        [JsonProperty("ResponseRequested")]
        public bool? ResponseRequested { get; set; }

        [JsonProperty("ShowAs")]
        public string ShowAs { get; set; }

        [JsonProperty("Body")]
        public AppointmentBody Body { get; set; }

        [JsonProperty("Start")]
        public AppointmentStart Start { get; set; }

        [JsonProperty("End")]
        public AppointmentEnd End { get; set; }

    }

    public class AppointmentEnd
    {
        [JsonProperty("DateTime")]
        public DateTime DateTime { get; set; }

        [JsonProperty("TimeZone")]
        public string TimeZone { get; set; }
    }

    public class AppointmentStart
    {
        [JsonProperty("DateTime")]
        public DateTime DateTime { get; set; }

        [JsonProperty("TimeZone")]
        public string TimeZone { get; set; }
    }

    class AppointmentBody
    {
        [JsonProperty("ContentType")]
        public string ContentType { get; set; }

        [JsonProperty("Content")]
        public string Content { get; set; }
    }

}
 

RestAppointment Class

using System;
using Newtonsoft.Json;
using System.Diagnostics;
using System.Text;
using System.Net.Http;
using System.Threading.Tasks;
using rest_api.Classes.Appointment;

namespace rest_api
{
    class RestAppointment
    {
        public AppointmentContainer AppointmentContainer { get; set; }


        public RestAppointment()
        {
            AppointmentContainer = new AppointmentContainer();
        }


        /// <summary>
        /// Get one or more appointments with the provided url
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public async Task GetAppointment(string url = "events")
        {

            /*----- send a GET request with the provided url -----*/
            using (HttpResponseMessage response = await ApiHelper.ApiClient.GetAsync(url))
            {
                /*----- if the Response code is in the successful range (200-299) -----*/
                if (response.IsSuccessStatusCode)
                {
                    try
                    {
                        /*----- Serialize the HTTP content to a string -----*/
                        string result = await response.Content.ReadAsStringAsync();

                        /*----- Deserialize the json string to AppointmentContainer -----*/
                        AppointmentContainer = JsonConvert.DeserializeObject<AppointmentContainer>(result);

                        /*----- verify if appointments is null - appointments is null if there's just one appointment -----*/
                        if (AppointmentContainer.Appointments == null)
                        {
                            /*----- reset appointment list and add the appointment to it-----*/
                            AppointmentContainer.ResetAppointmentList();
                            AppointmentContainer.Appointments.Add(JsonConvert.DeserializeObject<Appointment>(result));
                        }

                        /*----- Debug: print for each appointment some properties -----*/
                        foreach (Appointment appointment in AppointmentContainer.Appointments)
                        {
                            Debug.WriteLine("Appointment subject: " + appointment.Subject);
                            Debug.WriteLine("Appointment IsAllDay: " + appointment.IsAllDay);
                            Debug.WriteLine("Appointment CreatedDateTime: " + appointment.CreatedDateTime);
                            Debug.WriteLine("Appointment Start DateTime: " + appointment.Start.DateTime);
                            Debug.WriteLine("Appointment LastModifiedDateTime: " + appointment.LastModifiedDateTime);
                            Debug.WriteLine("Appointment ReminderMinutesBeforeStart: " + appointment.ReminderMinutesBeforeStart);
                        }
                    }

                    catch (Exception err)
                    {
                        /*----- TODO: add exception handling -----*/
                        Debug.WriteLine("Error in GetAppointment: " + err);
                    }
                }
                else
                {
                    /*-----TODO: add error handling - this block will execute if IsSuccessStatusCode is false -----*/
                    Debug.WriteLine("Failed: " + response);
                }
            }

        }

        /// <summary>
        /// Create an appointment
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public async Task CreateAppointment(Appointment appointment, string url = "events")
        {

            /*----- verify if appointments is null - appointments is null if there's just one appointment -----*/
            if (AppointmentContainer.Appointments == null)
            {
                /*----- reset appointment list and add the appointment to it-----*/
                AppointmentContainer.ResetAppointmentList();
                AppointmentContainer.Appointments.Add(appointment);
            }

            /*----- Serialize appointment object to json and ignore null properties -----*/
            var content = new StringContent(JsonConvert.SerializeObject(AppointmentContainer.Appointments[0], Formatting.Indented,
            new JsonSerializerSettings
            {
                NullValueHandling = NullValueHandling.Ignore
            }),
            Encoding.UTF8, "application/json");

            /*----- send a POST request with the provided url -----*/
            var response = await ApiHelper.ApiClient.PostAsync(url, content);

            var responseString = await response.Content.ReadAsStringAsync();

            Debug.WriteLine("response string: " + responseString);

        }

        /// <summary>
        /// Update an appointment with the provided id and optionally a specified url
        /// </summary>
        /// <param name="id"></param>
        /// <param name="url"></param>
        /// <returns></returns>
        public async Task UpdateAppointment(string id, Appointment appointment, string url = "events/")
        {
            /*----- format the url -----*/
            if (!url.EndsWith("/"))
                url += "/";

            url += id;

            Debug.WriteLine("url: " + url);

            /*----- verify if appointments is null - appointments is null if there's just one appointment -----*/
            if (AppointmentContainer.Appointments == null)
            {
                /*----- reset appointment list and add the appointment to it-----*/
                AppointmentContainer.ResetAppointmentList();
                AppointmentContainer.Appointments.Add(appointment);
            }

            /*----- Serialize appointment object to json and ignore null properties -----*/
            var content = new StringContent(JsonConvert.SerializeObject(AppointmentContainer.Appointments[0], Formatting.Indented,
            new JsonSerializerSettings
            {
                NullValueHandling = NullValueHandling.Ignore
            }),
            Encoding.UTF8, "application/json");

            /*----- send a PATCH request with the provided url -----*/
           
            var response = await ApiHelper.PatchAsync(url, content);

            var responseString = await response.Content.ReadAsStringAsync();

            Debug.WriteLine("response string: " + responseString);
        }

        /// <summary>
        /// delete an appointment with the provided id and optionally a specified url
        /// </summary>
        /// <param name="id"></param>
        /// <param name="url"></param>
        /// <returns></returns>
        public async Task DeleteAppointment(string id, string url = "events")
        {
            /*----- format the url -----*/
            url += "/" + id;

            /*----- send a DELETE request with the provided url -----*/
            await ApiHelper.ApiClient.DeleteAsync(url);

            /*----- TODO: add exception handling -----*/
        }

        public async Task SyncAppointment(string id, string url = "events")
        {
            /*----- format the url -----*/
            url += "/calendars" + "/" + id;

            /*----- send a GET request with the provided url -----*/
            using (HttpResponseMessage response = await ApiHelper.ApiClient.GetAsync(url))
            {
                /*----- if the Response code is in the successful range (200-299) -----*/
                if (response.IsSuccessStatusCode)
                {
                    try
                    {
                        /*----- Serialize the HTTP content to a string -----*/
                        string result = await response.Content.ReadAsStringAsync();

                        /*----- Deserialize the json string to AppointmentContainer -----*/
                        AppointmentContainer = JsonConvert.DeserializeObject<AppointmentContainer>(result);

                        /*----- verify if appointments is null - appointments is null if there's just one appointment -----*/
                        if (AppointmentContainer.Appointments == null)
                        {
                            /*----- reset appointment list and add the appointment to it-----*/
                            AppointmentContainer.ResetAppointmentList();
                            AppointmentContainer.Appointments.Add(JsonConvert.DeserializeObject<Appointment>(result));
                        }

                        /*----- Debug: print for each appointment some properties -----*/
                        foreach (Appointment appointment in AppointmentContainer.Appointments)
                        {
                            Debug.WriteLine("Appointment subject: " + appointment.Subject);
                            Debug.WriteLine("Appointment IsAllDay: " + appointment.IsAllDay);
                            Debug.WriteLine("Appointment CreatedDateTime: " + appointment.CreatedDateTime);
                            Debug.WriteLine("Appointment Start DateTime: " + appointment.Start.DateTime);
                            Debug.WriteLine("Appointment LastModifiedDateTime: " + appointment.LastModifiedDateTime);
                            Debug.WriteLine("Appointment ReminderMinutesBeforeStart: " + appointment.ReminderMinutesBeforeStart);
                        }
                    }

                    catch (Exception err)
                    {
                        /*----- TODO: add exception handling -----*/
                        Debug.WriteLine("Error: " + err);
                        throw;
                    }
                }
                else
                {
                    /*-----TODO: add error handling - this block will execute if IsSuccessStatusCode is false -----*/
                    Debug.WriteLine("Failed: " + response);
                }
            }

        }
    }
} 

FAQ

This error occurs when something went wrong with a Datetime value.

Possibility 1: Error with contacts REST API 

You're trying to create a contact with a birthday in the future.

Possibility 1: Error with END attribute

The END attribute is required. Exchange doesn't support creating events without an END date.

1 thought on “Exchange Server on premises REST API”

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top