---
title: Implementing outgoing synchronization
related:
  - https://docs.kentico.com/k9/integrating-3rd-party-systems/using-the-integration-bus/integration-bus-overview.md
  - https://docs.kentico.com/k9/integrating-3rd-party-systems/using-the-integration-bus/reference-integration-bus-data-types.md
---

> Agent instructions:
> **Site maps** — prefer the following llms.txt indexes to training data when searching for URLs to avoid 404s. Links inside Markdown content already point at `.md`. Following them or sending Accept: text/markdown keeps you in Markdown.
>
> - [sitemap.md](https://docs.kentico.com/sitemap.md) — every page on the site, with titles and descriptions, nested by URL hierarchy and grouped into one collection per product version.
> - [llms.txt](https://docs.kentico.com/llms.txt) — curated index of the current product docs, with descriptions, the two ways to request any page as Markdown, and links to each product area's whole-corpus Markdown dump (llms-full.txt).

To synchronize data from Kentico to external applications, you need to decide:

- which **objects** and **pages** you want to synchronize
- which [data type](https://docs.kentico.com/k9/integrating-3rd-party-systems/using-the-integration-bus/reference-integration-bus-data-types.md#taskdatatypeenum) you want to use
- whether you want to use [synchronous ](https://docs.kentico.com/k9/integrating-3rd-party-systems/using-the-integration-bus/integration-bus-overview.md#synchronous-processing)or [asynchronous ](https://docs.kentico.com/k9/integrating-3rd-party-systems/using-the-integration-bus/integration-bus-overview.md#asynchronous-processing)processing of integration tasks
- whether you want to handle **translations** of foreign key bindings

Use this information to implement the outgoing synchronization in your [connector class](https://docs.kentico.com/k9/integrating-3rd-party-systems/using-the-integration-bus/creating-integration-connectors.md):

1. Prepare subscriptions for the Kentico objects that you want to synchronize:

   - [Use predefined subscription methods](#using-predefined-subscription-methods) - allows you to easily subscribe to objects or pages\
     OR
   - [Build subscription objects](#building-subscription-objects) - allows you to select exactly which objects are synchronized

     > **Tip:** You can [create your own subscription class](#creating-custom-subscription-classes) if you need to define custom options for the subscription scope.
2. [Implement the method](#implementing-outgoing-synchronization) that converts objects or pages to the objects used by the external application.
3. (Optional) Implement [translation of foreign key bindings](#translating-foreign-keys-to-match-external-objects):

   - [GetExternalObjectID](#getexternalobjectid-method) method - if the synchronized objects or pages have bindings to objects inheriting from _BaseInfo_
   - [GetExternalDocumentID](#getexternaldocumentid-method) method - if the synchronized objects or pages have bindings to pages (_TreeNode_)

## Creating subscriptions

Subscriptions keep track of actions that occur in Kentico, such as creating, updating or deleting objects and pages. Use subscriptions to determine the scope of the changes that the connector synchronizes.

You need to implement subscriptions inside the **Init()** method of your [connector class](https://docs.kentico.com/k9/integrating-3rd-party-systems/using-the-integration-bus/creating-integration-connectors.md):

```csharp

using CMS.SynchronizationEngine;
using CMS.Synchronization;
using CMS.DataEngine;

public class CMSIntegrationConnector : BaseIntegrationConnector
{
    public override void Init()
    {
        // Initializes the connector name
        ConnectorName = GetType().Name;

        // Register your subscriptions here
    }
}

```

### Using predefined subscription methods

You can subscribe to pages or objects by calling the following methods:

- SubscribeToAllDocuments
- SubscribeToDocuments
- SubscribeToAllObjects
- SubscribeToObjects

See [Reference - Integration bus data types](https://docs.kentico.com/k9/integrating-3rd-party-systems/using-the-integration-bus/reference-integration-bus-data-types.md) for information about the method parameters.

**Examples**:

```csharp

// Subscribes to all types of changes made to user objects
SubscribeToObjects(TaskProcessTypeEnum.AsyncSnapshot, UserInfo.OBJECT_TYPE);

```

```csharp

// Subscribes to all types of changes made to all pages on all sites
SubscribeToAllDocuments(TaskProcessTypeEnum.AsyncSimpleSnapshot, TaskTypeEnum.All);

```

### Building subscription objects

You can select exactly which objects are synchronized using subscription objects:

1. Create a new object of a subscription class.
2. Call the **SubscribeTo** method for the object.

Subscription classes inherit from _AbstractIntegrationSubscription_, and you can use the following predefined options:

- BaseIntegrationSubscription
- ObjectIntegrationSubscription
- DocumentIntegrationSubscription

![The inheritance hierarchy of subscription classes](https://docs.kentico.com/docsassets/k9/implementing-outgoing-synchronization/image2013-9-1073429.png "The inheritance hierarchy of subscription classes")

The subscription classes provide the following **filtering options**:

**BaseIntegrationSubscription**

- **ConnectorName** - string; assigns the subscription to a connector. Use the _ConnectorName_ property to enter the name of the current connector.
- **TaskProcessType** - enumeration; specifies the synchronization mode and data type. See [TaskProcessTypeEnum](https://docs.kentico.com/k9/integrating-3rd-party-systems/using-the-integration-bus/reference-integration-bus-data-types.md#taskprocesstypeenum) for details.
- **TaskType** - enumeration; determines the type of the synchronized action (create, update, delete, etc.). See [TaskTypeEnum](https://docs.kentico.com/k9/integrating-3rd-party-systems/using-the-integration-bus/reference-integration-bus-data-types.md#tasktypeenum) for details.
- **SiteName** - string; determines the code name of the site where the objects or pages belong. You can use _AbstractIntegrationSubscription.GLOBAL\_OBJECTS_ to subscribe only to global objects.

**ObjectIntegrationSubscription**

- **ObjectType** - string; determines the type (class) of the synchronized object. You can either use the object type code name directly (for example _cms.user_) or system constants (_UserInfo.OBJECT\_TYPE_).
- **ObjectCodeName** - string; determines the code name of one specific object, for example: _administrator_

**DocumentIntegrationSubscription**

- **DocumentNodeAliasPath** - string; determines the alias path of the synchronized pages, for example: _/Products/%_
- **DocumentCultureCode** - string; determines the culture of the synchronized pages, for example: _en-US_
- **DocumentClassName** - string; determines the [page type](https://docs.kentico.com/k9/developing-websites/defining-website-content-structure/page-types.md) of the synchronized pages, for example: _CMS.MenuItem_

> **Info:** **Wildcard character** **%**
>
> You can use the percent character (%) as a wildcard representing any number of characters in the string parameter values.
>
> For example, if you specify the _DocumentCultureCode_ as "en-%", the subscription covers all English cultures: _en-US_, _en-GB_, etc.

If you do not want to limit the synchronization scope through one of the properties, set the given value to _**null**_ in the subscription object's constructor. For the _TaskType_ enumeration, set the _**All**_ value.

**Examples**:

```csharp

// Subscription that synchronizes the creation of object types starting with 'poll.poll' - polls and poll answers
ObjectIntegrationSubscription objSub = new ObjectIntegrationSubscription(ConnectorName, TaskProcessTypeEnum.AsyncSnapshot, TaskTypeEnum.CreateObject, "PersonalSite", "poll.poll%", null);
SubscribeTo(objSub);

// Subscription that synchronizes all changes made to pages on 'NewSite', located under the /Home/ path in the content tree
DocumentIntegrationSubscription pageSub = new DocumentIntegrationSubscription(ConnectorName, TaskProcessTypeEnum.AsyncSimpleSnapshot, TaskTypeEnum.All, "NewSite", "/Home/%", null, null);
SubscribeTo(pageSub);

```

```csharp

// Subscribes to all changes made to the SETTINGS of a specific custom table, where 'customtable.SampleTable' is the table's code name
ObjectIntegrationSubscription customTableSub = new ObjectIntegrationSubscription(ConnectorName, TaskProcessTypeEnum.AsyncSimpleSnapshot, TaskTypeEnum.All, null, null, "customtable.SampleTable");
SubscribeTo(customTableSub);

// Subscribes to all changes made to the DATA of the 'customtable.SampleTable' custom table
ObjectIntegrationSubscription customTableDataSub = new ObjectIntegrationSubscription(ConnectorName, TaskProcessTypeEnum.AsyncSimpleSnapshot, TaskTypeEnum.All, null, CustomTableItemProvider.GetObjectType("customtable.SampleTable"), null);
SubscribeTo(customTableDataSub);

```

### Creating custom subscription classes

If you need to extend the filtering options of a connector's subscriptions, you can create your own subscription class:

1. Create a new class inheriting from one of the existing [subscription classes](#building-subscription-objects).
   - If you wish to create a completely custom subscription class, we recommend inheriting from **AbstractIntegrationSubscription**.
   - If you only wish to add custom filtering for objects or pages, inherit from **ObjectIntegrationSubscription** or **DocumentIntegrationSubscription**.
2. Define any properties and constructors that you need for the custom logic of your subscription class.
3. Override the **IsMatch()** method:

   ```csharp

   public override bool IsMatch(ICMSObject obj, TaskTypeEnum taskType, ref TaskProcessTypeEnum taskProcessType)
   {
       /* If you are inheriting from ObjectIntegrationSubscription or DocumentIntegrationSubscription,
       * we recommend calling the IsMatch method of the parent class first:
       * bool result = base.IsMatch(obj, taskType, taskProcessType)
       * You can then use additional custom logic to modify the result. */

       /* Evaluate whether the subscription's properties match the properties of the 'obj' parameter (TreeNode page or BaseInfo object)
       * and the value of 'taskType'.
       * Return a boolean value that indicates when objects match the subscription requirements. */
   }

   ```

You can then use the custom subscription class to [build subscription objects](#building-subscription-objects).

## Implementing outgoing synchronization

To synchronize the objects covered by your [subscriptions](#creating-subscriptions) to external applications, you need to override methods inside your [connector class](https://docs.kentico.com/k9/integrating-3rd-party-systems/using-the-integration-bus/creating-integration-connectors.md):

- **ProcessInternalTaskAsync** for [asynchronous processing](https://docs.kentico.com/k9/integrating-3rd-party-systems/using-the-integration-bus/integration-bus-overview.md#asynchronous-processing) of integration tasks
- **ProcessInternalTaskSync** for [synchronous processing](https://docs.kentico.com/k9/integrating-3rd-party-systems/using-the-integration-bus/integration-bus-overview.md#synchronous-processing)

> **Info:**&#x20;
>
> ### Asynchronous processing
>
> The **ProcessInternalTaskAsync** method ensures asynchronous processing of **objects** (users, roles, forums, etc.) or **pages** (content tree nodes):
>
> ```csharp title="Objects"
>
> public override IntegrationProcessResultEnum ProcessInternalTaskAsync(GeneralizedInfo infoObj, TranslationHelper translations, TaskTypeEnum taskType, TaskDataTypeEnum dataType, string siteName, out string errorMessage)
>
> ```
>
> ```csharp title="Pages"
>
> public override IntegrationProcessResultEnum ProcessInternalTaskAsync(TreeNode node, TranslationHelper translations, TaskTypeEnum taskType, TaskDataTypeEnum dataType, string siteName, out string errorMessage)
>
> ```

> **Info:**&#x20;
>
> ### Synchronous processing
>
> The **ProcessInternalTaskSync** method ensures synchronous processing of **objects** (users, roles, forums, etc.) or **pages** (content tree nodes):
>
> ```csharp title="Objects"
>
> public override IntegrationProcessResultEnum ProcessInternalTaskSync(GeneralizedInfo infoObj, TaskTypeEnum taskType, string siteName, out string errorMessage)
>
> ```
>
> ```csharp title="Pages"
>
> public override IntegrationProcessResultEnum ProcessInternalTaskSync(TreeNode node, TaskTypeEnum taskType, string siteName, out string errorMessage)
>
> ```

In all cases, the purpose of the method is to transform the _GeneralizedInfo_ or _TreeNode_ internal object into a corresponding object in the third party system, and perform the action specified by [TaskTypeEnum](https://docs.kentico.com/k9/integrating-3rd-party-systems/using-the-integration-bus/reference-integration-bus-data-types.md#tasktypeenum). You also have to take the [TaskDataTypeEnum](https://docs.kentico.com/k9/integrating-3rd-party-systems/using-the-integration-bus/reference-integration-bus-data-types.md#taskdatatypeenum) into account. When you are done with the processing, set the error message and return an [IntegrationProcessResultEnum ](https://docs.kentico.com/k9/integrating-3rd-party-systems/using-the-integration-bus/reference-integration-bus-data-types.md#integrationprocessresultenum)value.

For example:

```csharp

public override IntegrationProcessResultEnum ProcessInternalTaskAsync(TreeNode node, TranslationHelper translations, TaskTypeEnum taskType, TaskDataTypeEnum dataType, string siteName, out string errorMessage)
{

    // Convert the TreeNode to an external page object

    // Optional: Translate foreign key values

    // Send the data to the target application

    // Method result for successful processing
    errorMessage = null;
    return IntegrationProcessResultEnum.OK;
}

```

The synchronous and asynchronous versions of the methods work in the same way. The only difference is that you cannot use the _TranslateColumnsToExternal_ method to [translate foreign key values](#translating-foreign-keys-to-match-external-objects) inside the _ProcessInternalTaskSync_ method. If you wish to translate foreign keys with synchronous processing, you need to manually [write the translation code](#translating-foreign-keys-in-synchronous-mode).

### Sending data to the target application

Once you have converted the object or page to an external equivalent, there are several ways to synchronize the data with the target system:

- Call the API of the external system (you need to add references to the required namespaces).
- Use _CMSConnectionScope_ and _GeneralConnection_ and perform a query against the external database.
- Push the data to an external endpoint in a format that the target system can process. For example, the endpoint can be represented by a web service in the external system.

You can use any other approach, but you always need to be able to determine whether the processing succeeded on the external side. Both the _ProcessInternalTaskAsync_ and _ProcessInternalTaskSync_ methods must return the [result status](https://docs.kentico.com/k9/integrating-3rd-party-systems/using-the-integration-bus/reference-integration-bus-data-types.md#integrationprocessresultenum).

## Translating foreign keys to match external objects

The values of identifier columns may not always be the same for equivalent objects in Kentico and external systems. For [subscriptions](#creating-subscriptions) using the _SimpleSnapshot_ or _Snapshot_ [data type](https://docs.kentico.com/k9/integrating-3rd-party-systems/using-the-integration-bus/reference-integration-bus-data-types.md#taskdatatypeenum) and asynchronous processing, you can ensure consistency by translating the columns that store foreign key bindings to related objects.

To perform the translation, call **TranslateColumnsToExternal** inside your connector's [ProcessInternalTaskAsync](#implementing-outgoing-synchronization) method. The TranslateColumnsToExternal method is inherited from _BaseIntegrationConnector_ and accepts the following parameters:

```csharp title="Objects"

TranslateColumnsToExternal(GeneralizedInfo infoObj, TranslationHelper translations, bool processChildren)

```

```csharp title="Pages"

TranslateColumnsToExternal(TreeNode node, TranslationHelper translations, bool processChildren)

```

The last parameter determines whether to translate foreign keys of child objects. For the _SimpleSnapshot_ data type, always pass _false_. The processChildren parameter is useful when using the _Snapshot_ data type, for example when you are processing an object that does not exist in the target system yet, and you do no have enough transformation to translate the foreign keys of child objects before you process the main object. We recommend using the following translation order:

1. Call _TranslateColumnsToExternal(infoObj, translations, **false**)_.
2. Process the main object (send the data to the target system).
3. Call _TranslateColumnsToExternal(infoObj, translations, **true**)_.
4. Iterate through the **Children** collection of _infoObj_ and process each object.

To ensure correct translation functionality, you need to override one or both of the following methods inside your [connector class](https://docs.kentico.com/k9/integrating-3rd-party-systems/using-the-integration-bus/creating-integration-connectors.md):

- [GetExternalObjectID](#getexternalobjectid-method) - implement if the synchronized objects or pages have bindings to objects inheriting from _BaseInfo_
- [GetExternalDocumentID](#getexternaldocumentid-method) - implement if the synchronized objects or pages have bindings to pages (_TreeNode_)

![Diagram of the column translation process](https://docs.kentico.com/docsassets/k9/implementing-outgoing-synchronization/image2013-9-5164119.png "Diagram of the column translation process")

### GetExternalObjectID method

Override the GetExternalObjectID method inside your connector class if you call _TranslateColumnsToExternal_ for objects or pages that have references to other objects inheriting from _BaseInfo_.

```csharp

public override int GetExternalObjectID(string objectType, string codeName, string siteName, string parentType, int parentId, int groupId)

```

Parameters:

- **objectType** - identifies the type of the synchronized internal object (class). For example, _cms.user_ can match external objects such as "person" or "member".
- **codename** - the unique identifier of the synchronized object.
- **siteName** - the code name of the site where the object belongs (only for site-related objects).
- **parentType** - the type of the object's parent (if the object has a parent).
- **parentId** - the ID of the object's parent.
- **groupId** - the identifier of the object's group (if the object belongs to a group).

Use the parameters to find the corresponding object in the external system and **return the given object's identifier** (integer value).

### GetExternalDocumentID method

Override the GetExternalDocumentID method inside your connector class if you call _TranslateColumnsToExternal_ for objects or pages that have references to other pages (_TreeNode_).

```csharp

public override int GetExternalDocumentID(Guid nodeGuid, string cultureCode, string siteName, bool returnDocumentId)

```

Parameters:

> **Info:** To learn how Kentico stores pages in the database, refer to [Page database structure](https://docs.kentico.com/k9/custom-development/working-with-pages-in-the-api/page-database-structure.md).

- **nodeGuid** - the page's [GUID](http://en.wikipedia.org/wiki/GUID) identifier.
- **cultureCode** - the culture code of the page's language (for example _en-US_).
- **siteName** - the code name of the site where the page belongs.
- **returnDocumentId** - indicates which identifier you need to provide as the method's return value. If true, return the _DocumentID_, otherwise return the _NodeID_. The _NodeID_ is an identifier of a tree node including all [language versions](https://docs.kentico.com/k9/multilingual-websites/editing-the-content-of-multilingual-websites.md), while _DocumentID_ is unique for each language version.

  |                           | Identifier | Alternative identification         |
  | ------------------------- | ---------- | ---------------------------------- |
  | Shared page data          | NodeID     | nodeGuid & siteName                |
  | Culture version of a page | DocumentID | nodeGuide & siteName & cultureCode |

Use the parameters to find the corresponding page in the external system and **return the given page's identifier** (integer value).

### Translating foreign keys in synchronous mode

If you need to translate ID column values to match external objects, we recommend using asynchronous processing. In synchronous mode, the only way to translate columns is to write custom code inside the [ProcessInternalTaskSync](#synchronous-processing) method.

The following sample code indicates how to translate the identifier of a page's parent:

```csharp

using CMS.DocumentEngine;
using CMS.Membership;

public override IntegrationProcessResultEnum ProcessInternalTaskSync(TreeNode node, TaskTypeEnum taskType, string siteName, out string errorMessage)
{
    ...

    // Gets the Kentico parent node of the synchronized page
    TreeProvider tree = new TreeProvider(MembershipContext.AuthenticatedUser);
    TreeNode parentNode = DocumentHelper.GetDocument(node.NodeParentID, tree);

    // Gets the properties of the parent node
    Guid parentGuid = parentNode.NodeGUID;
    string parentSiteName = parentNode.NodeSiteName;

    int newParentId = 0;

    // External code that finds the matching external page according to the parentNode properties, and fills the newParentID

    // Assigns the new parent ID to the synchronized page
    node.NodeParentID = newParentId;

    ...

}


```
