​Get started with BitTitan SOAP APIs

Overview

MigrationWiz implements a simple, secure SOAP-based API allowing callers to perform operations such as:

  • Configure projects and users
  • Submit migrations
  • Monitor migration progress and statistics

Using the MigrationWiz SOAP API, you enable data migration from and to a multitude of systems, and enable on-demand migrations without the need for any user interaction or for a dedicated migration infrastructure.

Need help? Contact us to request code snippets, sample code, or for general technical support.

Requirements

To use the MigrationWiz SOAP API, the following are required:

  • A user account created on https://www.bittitan.com
  • A basic understanding of SOAP-based web services
  • A development environment able to consume WSDL web service definition

All accounts must be API-enabled before they can access the MigrationWiz SOAP API. Contact us to request access (or check if your account is API-enabled).

Client Access

The MigrationWiz SOAP API implements a single web service called WebService, exposing a single method called Execute. Its implementation is available at:

  • WSDL: https://www.migrationwiz.com/API/2.0/WebService.asmx?wsdl
  • Execution: https://www.migrationwiz.com/API/2.0/WebService.asmx

To generate a client access proxy using Visual Studio 2013, follow these simple steps (similar steps can be used with other versions of Visual Studio):

  • Right-click on References.
  • Select Add Service Reference.
  • Click on Advanced.
  • Click on Add Web Reference.
  • Paste the WSDL URL into the URL text box.
  • Enter a namespace name in Web reference name.
  • Click on Add Reference.

After completing these steps, add a "using" statement for the selected namespace, and copy/paste the following code:

WebService webService = new WebService
{
    Url = "https://www.migrationwiz.com/API/2.0/WebService.asmx"
};

Messages

The MigrationWiz SOAP API uses a simple request/response model. For each request message submitted to the web service’s Execute method, a response message is returned to the caller.

The following types of requests are available:

AuthenticateRequestProvides authentication tickets.
RetrieveRequestFinds entities such as mailboxes, mailbox connectors, etc.
CreateRequestCreates entities such as mailboxes, mailbox connectors, etc.
UpdateRequestModifies entities such as mailboxes, mailbox connectors, etc.
DeleteRequestDeletes entities such as mailboxes, mailbox connectors, etc.
ScalarRetrieveRequestRequests a calculated value such as a sun, max, min, or average value over entities.

The following response types are available:

AuthenticateResponseReturns authentication tickets.
RetrieveResponseReturns found entities such as mailboxes, mailbox connectors, etc.
CreateResponseConfirms creation of entities such as mailboxes, mailbox connectors, etc.
UpdateResponseConfirms modification of entities such as mailboxes, mailbox connectors, etc.
DeleteResponseConfirms deletion of entities such as mailboxes, mailbox connectors, etc.
FailureResponseReturns information about failure to process a request.
ScalarRetrieveResponseReturns a calculated value such as a sum, max, min, or average value over entities.

Here is an example demonstrating the request/response mechanism:

// Prepare authenticate request
AuthenticateRequest authenticateRequest = new AuthenticateRequest
{
    EmailAddress = "user@domain.com",
    Password = "password"
};

// Perform authenticate request
AuthenticateResponse authenticateResponse = (AuthenticateResponse)webService.Execute(authenticateRequest);

Authentication

Callers authenticate by presenting credentials using AuthenticationRequest messages. Obtained authentication tickets must then be attached to subsequent requests for authentication purposes.

Here is an example showing a simple authentication and execution routine:

// Prepare authenticate request
AuthenticateRequest authenticateRequest = new AuthenticateRequest
{
    EmailAddress = "user@domain.com",
    Password = "password"
};

// Perform authenticate request
AuthenticateResponse authenticateResponse = (AuthenticateResponse)webService.Execute(authenticateRequest);
Ticket ticket = authenticateResponse.Ticket;
Console.WriteLine(ticket.ExpirationDate);

// Prepare create request
CreateRequest createRequest = new CreateRequest
{
    Ticket = ticket,
    Entities = new Entity[]
    {
        //...
    }
};

// Perform create request
CreateResponse createResponse = (CreateResponse)webService.Execute(createRequest);

If the AuthenticateRequest fails for any reason, the caller receives a FailureResponse message with detailed error information. Tickets usually expire, so callers should renew tickets as needed. In any case, callers should not retrieve a new ticket each time a request is sent (callers should cache tickets).

While tickets normally only represent the account whose identity matches the specified email address, privileged accounts can also impersonate other accounts by setting the ImpersonateUserId property. However, this functionality is for internal use only and currently unavailable to external callers. We recommend not setting this property.

Entities

The MigrationWiz SOAP API allows callers to configure, initiate and monitor migrations using different types of entities.

The following entity types are available:

EntityDescription
MailboxConnectorRepresents configuration information about a source and destination messaging system.
MailboxRepresents configuration information about a source and destination mailbox.
MailboxMigrationRepresents an active, completed, or failed mailbox migration.
MailboxStatsRepresents statistics about an active, completed, or failed mailbox migration.

To perform a migration, follow these steps:

  1. Create a MailboxConnector entity specifying properties of the Source and Destination messaging systems (i.e. Specify that the connector should migrate from an IMAP system to an Exchange system).
  2. Create a Mailbox entity specifying properties of the Source and Destination mailboxes (i.e. Specify that the mailbox should migrate from source@gmail.com to destination@exchange.com).
  3. Create a MailboxMigration entity to start the migration (i.e. Specify whether the migration is a full or trial migration).

To monitor a migration, follow these steps:

  1. Retrieve MailboxMigration entities (i.e. Retrieve mailbox migrations by date, mailbox connector, or ID and check the Status property).
  2. Retrieve MailboxStats entities (i.e. Retrieve mailbox stats by date, mailbox, or ID and check the stat properties).

Creating a MailboxMigration entity also means scheduling a migration. For non-trial migrations, MigrationWiz credits will be debited from your account. For testing purposes, simply create MailboxMigration entities whose Type property is set to Trial (no credits will be debited for trial migrations). Because only one trial migration is allowed per email address, you may need to log in to our web site and reset the email address’s trial migration state.

The following actions are allowed:

EntityRetrieveCreateUpdateDelete
MailboxConnectorYesYesYesYes
MailboxYesYesYesYes
MailboxMigrationYesYesYes (only to stop)No
MailboxStatsYesNoNoNo

Note the following:

  • MailboxMigration entities are system-managed entities and cannot be deleted. To ensure deletion, specify an adequate purge period when creating MailboxConnector entities. MailboxMigration entities can be updated, but only to stop in-progress migrations. For example, consider a MailboxMigration entity whose state is currently set to Processing. You could stop it by updating its status to Stopping, and wait for the system to set it to Stopped. At this point however, the MailboxMigration cannot be updated any more.
  • MailboxStats entities are system-generated read-only statistics. To ensure deletion, specify an adequate purge period when creating MailboxConnector entities. MailboxStats entities cannot be created or updated.

Retrieving Entities

Callers must retrieve entities using RetrieveRequest messages. Callers must specify the type of entity to retrieve. Callers can specify optional filtering and sorting criteria. For example, a caller may decide to retrieve all mailbox connectors created in the last 10 days, sorting them by creation date in descending order. However, more complex multi-level filtering and ordering criteria are also possible.

Here is an example showing a simple entity retrieve routine:

// Prepare retrieve request
RetrieveRequest retrieveRequest = new RetrieveRequest
{
    Ticket = ticket,
    EntityName = typeof(MailboxConnector).Name,
    Offset = 0,
    Count = int.MaxValue,
    SearchCondition = new SearchCondition
    {
        IsOrCondition = false,
        SearchFilters = new SearchFilter[]
        {
            new SearchFilter
            {
                PropertyName = "CreateDate",
                ComparisonOperator = ComparisonOperator.Greater,
                Value = DateTime.Now.AddDays(-10)
            }
        }
    },
    SearchOrders = new SearchOrder[]
    {
        new SearchOrder
        {
            PropertyName = "CreateDate",
            IsAscending = false
        }
    }
};

// Perform retrieve request
RetrieveResponse retrieveResponse = (RetrieveResponse)webService.Execute(retrieveRequest);

// Print retrieved entities
foreach (MailboxConnector mailboxConnector in retrieveResponse.Entities)
{
    Console.WriteLine(mailboxConnector.Name);
    Console.WriteLine(mailboxConnector.CreateDate);
}

If the RetrieveRequest fails for any reason, the caller receives a FailureResponse message with detailed error information. The MigrationWiz SOAP API limits the number of entities returned by a single request. By default, the limit is 100 entities. As a best practice, callers should implement paging to retrieve larger data sets. Simply specify the offset to match the count of entities retrieved so far.

The MigrationWiz SOAP API is designed to protect configuration data stored on behalf of its customers. Only records associated with the calling account can be retrieved. In addition, the API will reject any request whose search or ordering criteria references a property storing passwords. Finally, for all retrieved entities, properties storing passwords are set to a placeholder value (example: "********").

The MigrationWiz SOAP API is designed to support complex multi-level filtering criteria. Each SearchCondition object can specify a list of SearchFilter objects, all joined by a single AND or OR clause, but also (optionally) a list of child SearchCondition objects. This provision allows callers to construct complex and/or condition trees. However, in most cases, this is not necessary.

Creating Entities

Callers must create entities using CreateRequest messages. Callers must specify a list of entities to create. If not specified, IDs are automatically assigned by the system.

Here is an example showing a simple entity create routine:

// Prepare create request
CreateRequest createRequest = new CreateRequest
{
    Ticket = ticket,
    Entities = new Entity[]
    {
        new MailboxConnector
        {
            Id = Guid.NewGuid()
            // set additional properties
        }
    }
};

// Perform create request
CreateResponse createResponse = (CreateResponse)webService.Execute(createRequest);

// Print created entities
foreach (ProcessingError processingError in createResponse.ProcessingErrors)
{
    Console.WriteLine(processingError.ErrorCode);
    Console.WriteLine(processingError.Entity);
}

If the CreateRequest fails entirely, the caller receives a FailureResponse message with detailed error information. If the CreateRequest fails partially (example: some entities could not be created), the caller receives ProcessingError objects within a CreateResponse message. The MigrationWiz SOAP API limits the number of entities which can be created by a single request. By default, the limit is 10 entities. As a best practice, callers should use multiple requests to create a large number of entities.

Updating Entities

Callers must update entities using UpdateRequest messages. Callers must specify a list of entities to update. Entity IDs assigned during creation cannot be changed.

Here is an example showing a simple entity update routine:

// Prepare update request
UpdateRequest updateRequest = new UpdateRequest
{
    Ticket = ticket,
    Entities = new Entity[] { mailboxConnector }
};

// Perform update request
UpdateResponse updateResponse = (UpdateResponse)webService.Execute(updateRequest);

// Print updated entities
foreach (ProcessingError processingError in updateResponse.ProcessingErrors)
{
    Console.WriteLine(processingError.ErrorCode);
    Console.WriteLine(processingError.Entity);
}

If the UpdateRequest fails entirely, the caller receives a FailureResponse message with detailed error information. If the UpdateRequest fails partially (example: some entities could not be updated), the caller receives ProcessingError objects within an UpdateResponse message. The MigrationWiz SOAP API limits the number of entities which can be updated by a single request. By default, the limit is 10 entities. As a best practice, callers should use multiple requests to update a large number of entities.

Updates have no effect on migrations which are already in progress. For example, if you change the properties of a mailbox, but a migration is already in progress for that mailbox, the changes you made will have no effect. We recommend stopping any active migration before making changes to associated entities.

Deleting Entities

Callers must create entities using DeleteRequest messages. Callers must specify a list of entity targets to delete. Each entity target specifies the entity type and ID to delete.

Here is an example showing a simple entity delete routine:

// Prepare delete request
DeleteRequest deleteRequest = new DeleteRequest
{
    Ticket = ticket,
    EntityTargets = new EntityTarget[]
    {
        new EntityTarget
        {
            Id = mailboxConnector.Id,
            EntityName = typeof(MailboxConnector).Name
        }
    }
};

// Perform delete request
DeleteResponse deleteResponse = (DeleteResponse)webService.Execute(deleteRequest);

// Print deleted entities
foreach (ProcessingError processingError in deleteResponse.ProcessingErrors)
{
    Console.WriteLine(processingError.ErrorCode);
    Console.WriteLine(processingError.EntityTarget.EntityName);
    Console.WriteLine(processingError.EntityTarget.Id);
}

If the DeleteRequest fails entirely, the caller receives a FailureResponse message with detailed error information. If the DeleteRequest fails partially (example: some entities could not be deleted), the caller receives ProcessingError objects within a DeleteResponse message. The MigrationWiz SOAP API limits the number of entities which can be deleted by a single request. By default, the limit is 10 entities. As a best practice, callers should use multiple requests to delete a large number of entities.

Deletes may fail if associated entities are in actively in use. For example, if you delete a mailbox, but a migration is already in progress for that mailbox, the delete request may fail. We recommend stopping any active migration before deleting associated entities.

Error Handling

The MigrationWiz SOAP API reports errors using ProcessingError objects. Each ProcessingError object contains detailed information about processing failures. Depending on the failure, only some of the properties listed below may be set.

PropertyDescription
EntityEntity for which processing failed.
EntityTargetTarget for which processing failed.
ErrorCodeDetailed error code.
PropertyName of property for which processing failed.
ValueValue of a property for which processing failed.
MessageAdditional information about the failure.

If a SOAP request failed entirely (for example because you forgot to include a ticket), the web service returns a FailureResponse message containing a single ProcessingError object. Below is an example of global failure handling:

// Prepare create request
CreateRequest createRequest = new CreateRequest
{
    Ticket = null, // ticket missing
    Entities = new Entity[]
    {
        // ...
    }
};

// Perform create request
FailureResponse failureResponse = (FailureResponse)webService.Execute(createRequest);

// Print error
Console.WriteLine(failureResponse.ProcessingError.ErrorCode);

If a SOAP request failed in a partial manner (for example: because only some of the entities could not be created), the web service returns a normal response (example: CreateResponse) containing ProcessingError objects indicating which entities it failed to process. Here is an example of partial failure handling:

// Prepare create request
CreateRequest createRequest = new CreateRequest
{
    Ticket = ticket,
    Entities = new Entity[] { null } // invalid entity
};

// Perform create request
CreateResponse createResponse = (CreateResponse)webService.Execute(createRequest);

// Print error
foreach (ProcessingError processingError in createResponse.ProcessingErrors)
{
    Console.WriteLine(processingError.ErrorCode);
    Console.WriteLine(processingError.Entity);
}

The MigrationWiz SOAP API is designed to produce useful, detailed, actionable error information. For example, if a property was unexpectedly set to a negative value, a ProcessingError specifying the name of the property, the minimum expected value, and a specific error code (e.g., PropertyMustBeMore) will be returned.

Introduction

​​​​​​​​​​​​​MigrationWiz recently introduced a new version of its SOAP API that supports all new systems introduced in the past months (such as Google Drive, OneDrive, PST, Open Exchange ...).

This API v2 is mostly compatible with the previous API (~ 90%) but some key services have changed. This document lists all changes introduced by the new API.

Note that the API v1 will stay active and backward compatible until June, 1st 2015. After this date, all API calls should be made to the v2 endpoint and using a proxy generated from the v2 WSDL.

​API Endpoint: https://www.migrationwiz.com/API/2.0/WebService.asmx

API WSDL: https://www.migrationwiz.com/API/2.0/WebService.asmx?​WSDL​​​

Create a MailboxConnector (v1)

// Create the mailbox connector
MailboxConnector mailboxConnector = new MailboxConnector
{
    // Set administrative usernames and passwords (optional)
    ExportAdministrativeUserName = "admin_username",             // set source admin username (consumer key)
    ExportAdministrativePassword = "admin_password"​,             // set source admin password (consumer secret)
    ImportAdministrativeUsername = "admin_username",             // set destination admin username
    ImportAdministrativePassword = "admin_password"​,             // set destination admin password

    // Set basic properties
    Id = Guid.NewGuid(),​                                         // auto-generated if unspecified
    Name = "My Example Connector",                               // connector display name
    UserId = ticket.UserId,                                      // own user ID
    ExportType = MailboxConnectorTypes.Gmail,                    // system to migrate from (type)
    ExportSettings = new MailboxConnectorSettingsZimbra          // system to migrate from (configuration)
    {
        Url = "https://my.zimbra.server.com",
        ContactHandling = ContactHandling.SkipSuggestedContacts
    },
    ImportType = MailboxConnectorTypes.ExchangeServer,           // system to migrate to (type)
    ImportSettings = new MailboxConnectorSettingsExchangeServer  // system to migrate to (configuration)
    {
        Url = "https://www.myExchangeServer.com",
        ExchangeMailboxArchiveHandling = ExchangeMailboxArchiveHandling.Mailbox
    },
    ProjectType = ProjectTypes.Mailbox                           // project type (mailbox)
};

Create a MailboxConnector (v2)

Changes:

  1. All MailboxConnectorSettingsSomething classes have been replaced by SomethingConfiguration classes (see match table in the next section).
  2. Administrative usernames and password have been moved inside the SomethingConfiguration classes with appropriate attribute names.
// Create the mailbox connector
MailboxConnector mailboxConnector = new MailboxConnector
{
    Id = Guid.NewGuid(),                                           // auto-generated if unspecified
    Name = "My Example Connector",                                 // connector display name
    UserId = ticket.UserId,                                        // own user ID
    ExportType = MailboxConnectorTypes.Gmail,                      // system to migrate from (type)
    ExportConfiguration = new ZimbraConfiguration                  // system to migrate from (configuration)
    {
        UseAdministrativeCredentials = true,
        Url = "https://my.zimbra.server.com",
        MigrateSuggestedContacts = true,
        AdministrativeUsername = "admin_username",                 // set admin username
        AdministrativePassword = "admin_password"                  // set admin password
    },
    ImportType = MailboxConnectorTypes.ExchangeServer,             // system to migrate to (type)
    ImportConfiguration = new ExchangeConfiguration                // system to migrate to (configuration)
    {
        Url = "https://www.myExchangeServer.com",
        ExchangeItemType = ExchangeMailboxArchiveHandling.Mailbox,
        AdministrativeUsername = "admin_username",                 // set admin username
        AdministrativePassword = "admin_password"                  // set admin password
    },
    ProjectType = ProjectTypes.Mailbox                             // project type (mailbox)​​
};

Conversion table

The table below lists all types that have been removed from API v1 and their equivalent in the API v2.​

API v1​API v2
​MailboxConnectorSettingsAws​AwsArchiveConfiguration​
​MailboxConnectorSettingsExchangeOnline2 (Mailbox / Archive)​ExchangeConfiguration
​MailboxConnectorSettingsExchangeOnlineChina (Mailbox / Archive)ExchangeConfiguration
​MailboxConnectorSettingsExchangeServer (Mailbox / Archive) ​ExchangeConfiguration
​MailboxConnectorSettingsExchangeOnline2 (Public Folder)ExchangePublicFolderConfiguration
​MailboxConnectorSettingsExchangeOnlineChina (Public Folder)​ExchangePublicFolderConfiguration
​MailboxConnectorSettingsExchangeServer (Public Folder)​ExchangePublicFolderConfiguration
​MailboxConnectorSettingsGmail​GoogleMailboxConfiguration
​MailboxConnectorSettingsGoogleDriveGoogleDriveConfiguration
​MailboxConnectorSettingsGoogleVault​GoogleVaultConfiguration
​MailboxConnectorSettingsGroupWise​GroupWiseConfiguration
​MailboxConnectorSettingsImap​HostConfiguration
​MailboxConnectorSettingsLotusLotusConfiguration
​MailboxConnectorSettingsOneDriveProOnline​OneDriveProConfiguration
​MailboxConnectorSettingsPopHostConfiguration
​MailboxConnectorSettingsPstAzurePstConfiguration / PstInternalStorageConfiguration
​MailboxConnectorSettingsZimbraZimbraConfiguration

The following sample scripts show how to perform certain tasks in MigrationWiz through our SOAP APIs. It is important to note that these scripts use a SoapSampleUtils class found below, which has a method ExecuteAndCheck that wraps the Execute method exposed by the WebService class. The ExecuteAndCheck method handles all errors that arise from requests. You can write your own variation of this method to use in your own application.

Set up and run migration

/// <summary>
/// Represents intro sample code.
/// In this example, we will migrate a mailbox from hotmail into Office 365
/// </summary>
/// <remarks>
/// Snippet reference for https://www.bittitan.com/kb/115008251608 (KB004293).
/// </remarks>
public static class SetUpAndRunMigration
{
    /// <summary>
    /// Web service.
    /// </summary>
    private static WebService webService;

    /// <summary>
    /// Ticket.
    /// </summary>
    private static Ticket ticket;

    /// <summary>
    /// Main entry point.
    /// </summary>
    public static void Run()
    {
        // Initialize a web service client
        Console.WriteLine("Starting to Run");
        SetUpAndRunMigration.webService = new WebService
        {
            Url = "https://www.migrationwiz.com/API/2.0/WebService.asmx"
        };
        Console.WriteLine("Initialized web service client");

        // Get a ticket
        SetUpAndRunMigration.ticket = SetUpAndRunMigration.GetTicket("user@domain.com", "password");
        Console.WriteLine("Authenticated web service client");

        // Create a connector
        MailboxConnector mailboxConnector = SetUpAndRunMigration.CreateMailboxConnector();
        Console.WriteLine("Created mailbox connector");

        // Create a mailbox
        Mailbox mailbox = SetUpAndRunMigration.CreateMailbox(
            mailboxConnector: mailboxConnector,
            sourceEmailAddress: "source@example.com",
            sourcePassword: "sourcepassword",
            destinationEmailAddress: "destination@example.com",
            destinationPassword: "destinationpassword");
        Console.WriteLine("Created mailbox");

        // Submit the mailbox for migration (full migration using a premium license drawn from the account)
        MailboxMigration mailboxMigration = SetUpAndRunMigration.SubmitMailbox(mailbox, MailboxQueueTypes.Full);
        Console.WriteLine("Submitted mailbox for migration");

        // Wait until the migration completes
        SetUpAndRunMigration.WaitUntilCompletion(mailboxMigration);
        Console.WriteLine("Completed migration");
    }

    /// <summary>
    /// Gets a ticket used for authentication purposes.
    /// </summary>
    /// <param name="emailAddress">The email address.</param>
    /// <param name="password">The password.</param>
    /// <returns>A ticket.</returns>
    public static Ticket GetTicket(
        string emailAddress,
        string password)
    {
        // Prepare authenticate request
        AuthenticateRequest authenticateRequest = new AuthenticateRequest
        {
            EmailAddress = emailAddress,
            Password = password
        };

        // Perform authenticate request
        AuthenticateResponse authenticateResponse =
            SetUpAndRunMigration.webService.ExecuteAndCheck<AuthenticateResponse>(authenticateRequest);

        // Return ticket from the authenticate response
        return authenticateResponse.Ticket;
    }

    /// <summary>
    /// Creates a mailbox connector.
    /// In this example, the connector is configured to migrate from Hotmail to Office 365
    /// </summary>
    /// <returns>A mailbox connector.</returns>
    public static MailboxConnector CreateMailboxConnector()
    {
        // Prepare mailbox connector
        MailboxConnector mailboxConnector = new MailboxConnector
        {
            // Set basic properties
            Id = Guid.NewGuid(),                                // auto-generated if unspecified
            Name = "My Example Connector",                      // connector display name
            UserId = SetUpAndRunMigration.ticket.UserId,                 // own user ID
            ExportType = MailboxConnectorTypes.POP,             // system to migrate from (type)
            ExportConfiguration = new HostConfiguration         // system to migrate FROM (configuration)
            {
                Host = "pop3.live.com"
            },
            ImportType = MailboxConnectorTypes.ExchangeOnline2, // system to migrate to (type)
            ImportConfiguration = new ExchangeConfiguration(),  // system to migrate to (configuration)

            // Set filtering properties
            ItemStartDate = DateTime.MinValue,                  // do not restrict to a specific start date
            ItemEndDate = DateTime.MaxValue,                    // do not restrict to a specific end date
            FolderFilter = null,                                // do not exclude any folder
            Flags = MailboxFlags.None,                          // do not use advanced options

            // Set error properties
            ItemRetryTimeout = 60,                  // on error, first wait 60 seconds, then 120 seconds, etc.
            MaximumItemFailures = 100,              // fail the migration if more than 100 items failed to migrate
            MaximumItemRetry = 3,                   // retry each item up to 3 times before marking it as failed

            // Set purging properties
            PurgeNotification = 30,                             // show a warning 30 days before the purge date
            PurgePeriod = 90,                                   // purge the connector 90 days

            // Set max transfer properties
            MaximumDataTransferRate = long.MaxValue,            // no data transfer rate limit
            MaximumDataTransferRateDuration = long.MaxValue,    // no data transfer rate duration limit

            // Set processing properties
            MaximumSimultaneousMigrations = 1000,               // maximum number of concurrent mailboxes to migrate
            ProcessingRequirement = ProcessingRequirements.Any, // do not specify advanced processing requirements
            ZoneRequirement = ZoneRequirements.NorthAmerica     // do not specify advanced processing requirements
        };

        // Prepare create request
        CreateRequest createRequest = new CreateRequest
        {
            Ticket = SetUpAndRunMigration.ticket,
            Entities = new Entity[] { mailboxConnector }
        };

        // Perform create request
        CreateResponse createResponse =
            SetUpAndRunMigration.webService.ExecuteAndCheck<CreateResponse>(createRequest);
        mailboxConnector = createResponse.Entities.OfType<MailboxConnector>().First();

        // Return mailbox connector
        return mailboxConnector;
    }

    /// <summary>
    /// Creates a mailbox.
    /// </summary>
    /// <param name="mailboxConnector">The mailbox connector.</param>
    /// <param name="sourceEmailAddress">The source email address.</param>
    /// <param name="sourcePassword">The source password.</param>
    /// <param name="destinationEmailAddress">The destination email address.</param>
    /// <param name="destinationPassword">The destination password.</param>
    /// <returns>A mailbox.</returns>
    public static Mailbox CreateMailbox(
        MailboxConnector mailboxConnector,
        string sourceEmailAddress,
        string sourcePassword,
        string destinationEmailAddress,
        string destinationPassword)
    {
        // Prepare mailbox
        Mailbox mailbox = new Mailbox
        {
            Id = Guid.NewGuid(),                            // auto-generated if unspecified
            ConnectorId = mailboxConnector.Id,              // associate the mailbox with the connector
            ExportEmailAddress = sourceEmailAddress,        // mailbox to migrate from (email address)
            ExportUserName = sourceEmailAddress,            // mailbox to migrate from (user name)
            ExportPassword = sourcePassword,                // mailbox to migrate from (password)
            ImportEmailAddress = destinationEmailAddress,   // mailbox to migrate to (email address)
            ImportUserName = destinationEmailAddress,       // mailbox to migrate to (user name)
            ImportPassword = destinationPassword,           // mailbox to migrate to (password)
            FolderFilter = null,                            // no folder filter
            Flags = MailboxFlags.None                       // no flogs
        };

        // Prepare create request
        CreateRequest createRequest = new CreateRequest
        {
            Ticket = SetUpAndRunMigration.ticket,
            Entities = new Entity[] { mailbox }
        };

        // Perform create request
        CreateResponse createResponse =
            SetUpAndRunMigration.webService.ExecuteAndCheck<CreateResponse>(createRequest);
        mailbox = createResponse.Entities.OfType<Mailbox>().First();

        // Return mailbox
        return mailbox;
    }

    /// <summary>
    /// Submits a mailbox for migration.
    /// </summary>
    /// <param name="mailbox">The mailbox.</param>
    /// <param name="mailboxQueueType">The mailbox queue type</param>
    /// <returns></returns>
    public static MailboxMigration SubmitMailbox(
        Mailbox mailbox,
        MailboxQueueTypes mailboxQueueType)
    {
        // Set basic properties
        MailboxMigration mailboxMigration = new MailboxMigration
        {
            Id = Guid.NewGuid(),                    // auto-generated if unspecified
            UserId = SetUpAndRunMigration.ticket.UserId,     // own user ID
            ConnectorId = mailbox.ConnectorId,      // associate the migration submission with the connector
            MailboxId = mailbox.Id,                 // associate the migration submission with the mailbox
            Type = mailboxQueueType,                // type of migration: trial, full, etc.
            MaximumDataTransfer = long.MaxValue,    // no data transfer limit
            MaximumItemsPerFolder = int.MaxValue,   // no data transfer limit
            Status = MailboxQueueStatus.Submitted,  // must be submitted
            Priority = 1                            // must be 1
        };


        // Preare create reqeust
        CreateRequest createRequest = new CreateRequest
        {
            Ticket = SetUpAndRunMigration.ticket,
            Entities = new Entity[] { mailboxMigration }
        };

        // Perform create request
        CreateResponse createResponse =
            SetUpAndRunMigration.webService.ExecuteAndCheck<CreateResponse>(createRequest);
        mailboxMigration = createResponse.Entities.OfType<MailboxMigration>().First();

        // Return mailbox migration
        return mailboxMigration;
    }

    /// <summary>
    /// Waits until completion.
    /// </summary>
    /// <param name="mailboxMigration">The mailbox migration.</param>
    public static void WaitUntilCompletion(MailboxMigration mailboxMigration)
    {
        // Prepare retrieve request
        RetrieveRequest retrieveRequest = new RetrieveRequest
        {
            Ticket = SetUpAndRunMigration.ticket,
            EntityName = typeof(MailboxMigration).Name,
            SearchCondition = new SearchCondition
            {
                SearchFilters = new SearchFilter[] {
                    new SearchFilter
                    {
                        PropertyName = "Id",
                        ComparisonOperator = ComparisonOperator.Equal,
                        Value = mailboxMigration.Id,
                    }
                }
            },
            Count = 1
        };

        // Keep retrieving the migration state until complete
        while (true)
        {
            // Perform retrieve reqesut
            RetrieveResponse retrieveResponse =
                SetUpAndRunMigration.webService.ExecuteAndCheck<RetrieveResponse>(retrieveRequest);
            mailboxMigration = retrieveResponse.Entities.OfType<MailboxMigration>().First();

            // Print status
            switch (mailboxMigration.Status)
            {
                case MailboxQueueStatus.Completed:
                case MailboxQueueStatus.Stopped:
                case MailboxQueueStatus.Failed:
                case MailboxQueueStatus.MaximumTransferReached:
                    Console.WriteLine("Done - " + mailboxMigration.Status);
                    return;

                default:
                    Console.Write("Waiting - " + mailboxMigration.Status);
                    break;
            }

            // Do not call too frequently or your account may be locked
            const int sleepTime = 60000; // 1 minute
            Thread.Sleep(sleepTime);
        }
    }
}

Manage coupons

/// <summary>
/// Represents coupon sample code.
/// </summary>
/// <remarks>
/// Snippet reference for https://www.bittitan.com/kb/115008258748 (KB004970).
/// </remarks>
public static class CouponSample
{
    /// <summary>
    /// Web service.
    /// </summary>
    private static WebService webService;

    /// <summary>
    /// Ticket.
    /// </summary>
    private static Ticket ticket;

    /// <summary>
    /// Main entry point.
    /// </summary>
    public static void Run()
    {
        // Initialize a web service client
        CouponSample.webService = new WebService
        {
            Url = "https://www.migrationwiz.com/API/2.0/WebService.asmx"
        };

        // Get a ticket. Replace the username/password with valid entries.
        CouponSample.ticket = CouponSample.GetTicket("user@domain.com", "password");
        Console.WriteLine("Authenticated web service client");

        // Create coupons
        Coupon[] coupons = CouponSample.CreateCoupons();
        Console.WriteLine("Created coupons");

        // List coupons
        CouponSample.ListCoupons();
        Console.WriteLine("Listed coupons");

        // Delete coupons
        CouponSample.DeleteCoupons(coupons);
        Console.WriteLine("Deleted coupons");

        // List coupons
        CouponSample.ListCoupons();
        Console.WriteLine("Listed coupons");
    }

    /// <summary>
    /// Gets a ticket used for authentication purposes.
    /// </summary>
    /// <param name="emailAddress">Email address.</param>
    /// <param name="password">Password.</param>
    /// <returns>Ticket.</returns>
    public static Ticket GetTicket(
        string emailAddress,
        string password)
    {
        // Prepare authenticate request
        AuthenticateRequest authenticateRequest = new AuthenticateRequest
        {
            EmailAddress = emailAddress,
            Password = password
        };

        // Perform authenticate request
        AuthenticateResponse authenticateResponse =
            CouponSample.webService.ExecuteAndCheck<AuthenticateResponse>(authenticateRequest);

        // Return ticket
        return authenticateResponse.Ticket;
    }

    /// <summary>
    /// Creates coupons.
    /// </summary>
    /// <returns>Coupons.</returns>
    public static Coupon[] CreateCoupons()
    {
        // Define coupons to create Only 10 coupons can be created per create request If many coupons must be
        // created, use multiple batched create requests
        const int couponCount = 1;
        Coupon[] coupons = new Coupon[couponCount];
        for (int i = 0; i < couponCount; i++)
        {
            coupons[i] = new Coupon
            {
                UserId = CouponSample.ticket.UserId,                    // required
                ProductSkuId = MigrationProxy.ProductSkuValues.Mailbox, // mailbox license
                Quantity = 1,                                           // 1 coupon = 1 license
                ExpireDate = new DateTime(2013, 01, 01),                // must be used before 01/01/13
                Validity = 30,                                          // must be used within 30 days of redemption
                Label = "My Coupon Project",                            // note for tracking purposes
            };
        }

        // Prepare create request
        CreateRequest createRequest = new CreateRequest
        {
            Ticket = CouponSample.ticket,
            Entities = coupons
        };

        // Perform create request
        CreateResponse createResponse =
            CouponSample.webService.ExecuteAndCheck<CreateResponse>(createRequest);
        coupons = createResponse.Entities.OfType<Coupon>().ToArray();

        // Return coupons having codes ready to be used
        return coupons;
    }

    /// <summary>
    /// Delete coupons.
    /// </summary>
    /// <param name="coupons">Coupons.</param>
    public static void DeleteCoupons(Coupon[] coupons)
    {
        // Define coupons to delete Only 10 coupons can be deleted per delete request If many coupons must be
        // deleted, use multiple batched delete requests
        EntityTarget[] deleteTargets = new EntityTarget[coupons.Length];
        for (int i = 0; i < coupons.Length; i++)
        {
            EntityTarget deleteTarget = new EntityTarget();
            deleteTarget.Id = coupons[i].Id;
            deleteTarget.EntityName = typeof(Coupon).Name;
            deleteTargets[i] = deleteTarget;
        }

        // Delete coupons An error will be returned for coupons that have been redeemed Otherwise licenses will be
        // restored to the account and the coupon code will be deleted
        DeleteRequest deleteRequest = new DeleteRequest
        {
            Ticket = CouponSample.ticket,
            EntityTargets = deleteTargets
        };

        // Perform delete request
        CouponSample.webService.ExecuteAndCheck<DeleteResponse>(deleteRequest);
    }

    /// <summary>
    /// Lists coupons.
    /// </summary>
    public static void ListCoupons()
    {
        // Prepare a retrieve request In this example, only retrieve coupons created later than 1 year ago Complex
        // conditions (include multi-level and/or conditions) can also be used, as well as sorting and aggregates
        RetrieveRequest retrieveRequest = new RetrieveRequest
        {
            Ticket = CouponSample.ticket,
            EntityName = typeof(Coupon).Name,
            SearchCondition = new SearchCondition
            {
                SearchFilters = new SearchFilter[]
                {
                    new SearchFilter
                    {
                        PropertyName = "CreateDate",
                        ComparisonOperator = ComparisonOperator.Greater,
                        Value = DateTime.UtcNow.AddYears(-1)
                    }
                }
            },
            Count = 100 // cannot retrieve more than 100 at a time
        };

        // Keep retrieving coupons with paging
        for (int i = 0; i < int.MaxValue; i++)
        {
            retrieveRequest.Offset = i * retrieveRequest.Count; // paging offset: 0, then 100, then 200, etc.
            RetrieveResponse retrieveResponse =
                CouponSample.webService.ExecuteAndCheck<RetrieveResponse>(retrieveRequest);
            foreach (Coupon coupon in retrieveResponse.Entities)
            {
                // Write a few properties
                Console.WriteLine("Code: " + coupon.Code);
                Console.WriteLine("Used: " + (Guid.Empty == coupon.ActivationUserId));
                Console.WriteLine("Expires: " + coupon.ExpireDate.ToLocalTime());
                Console.WriteLine();
            }
            if (retrieveResponse.Entities.Length < retrieveRequest.Count)
                break; // done paging
        }
    }
}

Soap utilities

/// <summary>
/// Defines soap sample utilities.
/// </summary>
public static class SoapSampleUtils
{
    /// <summary>
    /// Executes a request and checks the result.
    /// </summary>
    /// <param name="request">Request.</param>
    /// <returns>Typed response.</returns>
    public static T ExecuteAndCheck<T>(
        this WebService webService,
        Request request)
        where T : Response
    {
        // Execute the request
        Response response = webService.Execute(request);

        // Check the response
        return SoapSampleUtils.CheckResponse<T>(response);
    }

    /// <summary>
    /// Checks that a response is of the specified type and also fully succeeded.
    /// </summary>
    /// <param name="response">Response.</param>
    /// <returns>Typed response.</returns>
    public static T CheckResponse<T>(Response response)
        where T : Response
    {
        // This routine provides simple error checking Additional properties
        // (ex: ErrorCode) are available for troubleshooting

        // Check if the entire request failed
        FailureResponse failureResponse = response as FailureResponse;
        if (null != failureResponse)
        {
            string message = failureResponse.ProcessingError.Message;
            Console.WriteLine(message);
            throw new ApplicationException(message);
        }

        // Check if the response is of a type we did not expect
        T typedResponse = response as T;
        if (null == typedResponse)
        {
            string message = "Bad response.";
            Console.WriteLine(message);
            throw new ApplicationException(message);
        }

        // Check if a create request had any partial failure
        CreateResponse createResponse = response as CreateResponse;
        if ((null != createResponse) &&
            (null != createResponse.ProcessingErrors) &&
            (0 < createResponse.ProcessingErrors.Length))
        {
            foreach (ProcessingError processingError in createResponse.ProcessingErrors)
                Console.WriteLine("Create failure: " + processingError.Message);
            string message = "Create failed.";
            throw new ApplicationException(message);
        }

        // Check if an update request had any partial failure
        UpdateResponse updateResponse = response as UpdateResponse;
        if ((null != updateResponse) &&
            (null != updateResponse.ProcessingErrors) &&
            (0 < updateResponse.ProcessingErrors.Length))
        {
            foreach (ProcessingError processingError in updateResponse.ProcessingErrors)
                Console.WriteLine("Update failure: " + processingError.Message);
            string message = "Update failed.";
            throw new ApplicationException(message);
        }

        // Check if a delete request had any partial failure
        DeleteResponse deleteResponse = response as DeleteResponse;
        if ((null != deleteResponse) &&
            (null != deleteResponse.ProcessingErrors) &&
            (0 < deleteResponse.ProcessingErrors.Length))
        {
            foreach (ProcessingError processingError in deleteResponse.ProcessingErrors)
                Console.WriteLine("Delete failure: " + processingError.Message);
            string message = "Delete failed.";
            throw new ApplicationException(message);
        }

        // Returned the typed response
        return typedResponse;
    }
}