Importing linked content

Your content will often contain references to other pieces of imported content. A content item can use assets or point to other content items using Linked items elements or Rich text elements. This tutorial will walk through how to import these references to Kentico Cloud. For the basics of using the Content Management API, first see Importing to Kentico Cloud.

Premium feature 

The Content Management API requires a Professional plan or higher. See Pricing for more details.

Table of contents

    To avoid having to import objects in a specific order, you can use external IDs to reference non-existent (not imported yet) content. This also solves problems with circular dependencies.

    Here is how it works:

    1. Define external IDs for all content items and assets you want to import in advance.
    2. When referencing another content item or asset, use its external ID.
    3. Import your content using the upsert methods with external ID. The system will resolve all references.

    This way, you can import your content in any order and run the import process repeatedly to keep your project up to date. In the example below, you will import two content items that reference each other in their Linked items elements.

    Example scenario

    Say you want to import two related content items: A Donate with us and On Roasts article. Each content item references the other in the Related articles Linked items element. The final result will have the following structure:

    1. Defining external IDs

    External IDs are string-based identifiers of items and assets defined by you. You can define new IDs or reuse IDs from the original storage system you are importing content from.

    It's up to you to ensure no two objects have the same external ID. See Referencing by external ID for more details. For large projects, consider using GUIDs.

    To keep things simple here, let's go with {~123~} and {~456~} for your two articles.

    2. Using external IDs to reference items

    When defining the Linked items elements, use external IDs to reference the other content item:

    • JSON
    "related_articles": [ { "external_id": "456" } ]
    "related_articles": [ { "external_id": "456" } ]

    Rich text links 

    See the Content Management API reference to learn how to link to content items from Rich text elements.

    3. Importing content

    To create the On Roasts content item, send a PUT request to {~/items/external-id/<YOUR_ITEM_EXTERNAL_ID>~}.

    In the body of the request, specify the item's name and content type.

    See our API Reference for more details on Upserting content items.

    • C#
    using KenticoCloud.ContentManagement; ContentManagementOptions options = new ContentManagementOptions { ApiKey = "<YOUR_API_KEY>", ProjectId = "14372844-0a5d-434a-8423-605b8a631623" }; ContentManagementClient client = new ContentManagementClient(options); string externalId = "123"; ContentItemUpsertModel item = new ContentItemUpsertModel() { Name = "On Roasts", Type = ContentTypeIdentifier.ByCodename("article"), SitemapLocations = new[] { SitemapNodeIdentifier.ByCodename("articles") } }; ContentItemModel contentItemResponse = await client.UpsertContentItemByExternalIdAsync(externalId, item);
    using KenticoCloud.ContentManagement; ContentManagementOptions options = new ContentManagementOptions { ApiKey = "<YOUR_API_KEY>", ProjectId = "14372844-0a5d-434a-8423-605b8a631623" }; ContentManagementClient client = new ContentManagementClient(options); string externalId = "123"; ContentItemUpsertModel item = new ContentItemUpsertModel() { Name = "On Roasts", Type = ContentTypeIdentifier.ByCodename("article"), SitemapLocations = new[] { SitemapNodeIdentifier.ByCodename("articles") } }; ContentItemModel contentItemResponse = await client.UpsertContentItemByExternalIdAsync(externalId, item);
    • cURL
    curl --request PUT \ --url https://manage.kenticocloud.com/v1/projects/14372844-0a5d-434a-8423-605b8a631623/items/external-id/123 \ --header "Authorization: Bearer <YOUR_API_KEY>" \ --header "Content-type: application/json" \ --data " { "name":"On Roasts", "type":{ "codename":"article" }, "sitemap_locations":[ { "codename":"articles" } ] }"
    curl --request PUT \ --url https://manage.kenticocloud.com/v1/projects/14372844-0a5d-434a-8423-605b8a631623/items/external-id/123 \ --header "Authorization: Bearer <YOUR_API_KEY>" \ --header "Content-type: application/json" \ --data " { "name":"On Roasts", "type":{ "codename":"article" }, "sitemap_locations":[ { "codename":"articles" } ] }"

    Import content of the item by upserting its language variant.

    Send a PUT request to the endpoint specifying the language variant you want to insert or update. In the body of the request, specify the values of individual content elements.

    • C#
    using KenticoCloud.ContentManagement; ContentManagementOptions options = new ContentManagementOptions { ApiKey = "<YOUR_API_KEY>", ProjectId = "14372844-0a5d-434a-8423-605b8a631623" }; ContentManagementClient client = new ContentManagementClient(options); ArticleModel stronglyTypedElements = new ArticleModel { Title = "On Roasts", RelatedArticles = new [] { ContentItemIdentifier.ByExternalId("456") } }; ContentItemIdentifier itemIdentifier = ContentItemIdentifier.ByExternalId("123"); LanguageIdentifier languageIdentifier = LanguageIdentifier.ByCodename("en-US"); ContentItemVariantIdentifier identifier = new ContentItemVariantIdentifier(itemIdentifier, languageIdentifier); ContentItemVariantModel<ArticleModel> responseVariant = await client.UpsertContentItemVariantAsync<ArticleModel>(identifier, stronglyTypedElements);
    using KenticoCloud.ContentManagement; ContentManagementOptions options = new ContentManagementOptions { ApiKey = "<YOUR_API_KEY>", ProjectId = "14372844-0a5d-434a-8423-605b8a631623" }; ContentManagementClient client = new ContentManagementClient(options); ArticleModel stronglyTypedElements = new ArticleModel { Title = "On Roasts", RelatedArticles = new [] { ContentItemIdentifier.ByExternalId("456") } }; ContentItemIdentifier itemIdentifier = ContentItemIdentifier.ByExternalId("123"); LanguageIdentifier languageIdentifier = LanguageIdentifier.ByCodename("en-US"); ContentItemVariantIdentifier identifier = new ContentItemVariantIdentifier(itemIdentifier, languageIdentifier); ContentItemVariantModel<ArticleModel> responseVariant = await client.UpsertContentItemVariantAsync<ArticleModel>(identifier, stronglyTypedElements);
    • cURL
    curl --request PUT \ --url https://manage.kenticocloud.com/v1/projects/14372844-0a5d-434a-8423-605b8a631623/items/external-id/123/variants/codename/en-US \ --header "authorization: Bearer <YOUR_API_KEY>" \ --header "content-type: application/json" \ --data " { "elements":{ "title":"On Roasts", "related_articles":[ { "external_id":"456" } ] } }"
    curl --request PUT \ --url https://manage.kenticocloud.com/v1/projects/14372844-0a5d-434a-8423-605b8a631623/items/external-id/123/variants/codename/en-US \ --header "authorization: Bearer <YOUR_API_KEY>" \ --header "content-type: application/json" \ --data " { "elements":{ "title":"On Roasts", "related_articles":[ { "external_id":"456" } ] } }"

    Other elements were omitted for brevity. Notice that you are referencing the Donate with us item even though you haven't imported it yet.

    The reference is not visible inside the Kentico Cloud user interface but it is there. It will resolve itself once you import the second content item.

    Second content item

    Repeat the same process with the Donate with us article. Start by creating the content item:

    • C#
    using KenticoCloud.ContentManagement; ContentManagementOptions options = new ContentManagementOptions { ApiKey = "<YOUR_API_KEY>", ProjectId = "14372844-0a5d-434a-8423-605b8a631623" }; ContentManagementClient client = new ContentManagementClient(options); string externalId = "456"; ContentItemUpsertModel item = new ContentItemUpsertModel() { Name = "Donate with us", Type = ContentTypeIdentifier.ByCodename("article"), SitemapLocations = new[] { SitemapNodeIdentifier.ByCodename("articles") } }; ContentItemModel contentItemResponse = await client.UpsertContentItemByExternalIdAsync(externalId, item);
    using KenticoCloud.ContentManagement; ContentManagementOptions options = new ContentManagementOptions { ApiKey = "<YOUR_API_KEY>", ProjectId = "14372844-0a5d-434a-8423-605b8a631623" }; ContentManagementClient client = new ContentManagementClient(options); string externalId = "456"; ContentItemUpsertModel item = new ContentItemUpsertModel() { Name = "Donate with us", Type = ContentTypeIdentifier.ByCodename("article"), SitemapLocations = new[] { SitemapNodeIdentifier.ByCodename("articles") } }; ContentItemModel contentItemResponse = await client.UpsertContentItemByExternalIdAsync(externalId, item);
    • cURL
    curl --request PUT \ --url https://manage.kenticocloud.com/v1/projects/14372844-0a5d-434a-8423-605b8a631623/items/external-id/456 \ --header "Authorization: Bearer <YOUR_API_KEY>" \ --header "Content-type: application/json" \ --data " { "name":"Donate with us", "type":{ "codename":"article" }, "sitemap_locations":[ { "codename":"articles" } ] }"
    curl --request PUT \ --url https://manage.kenticocloud.com/v1/projects/14372844-0a5d-434a-8423-605b8a631623/items/external-id/456 \ --header "Authorization: Bearer <YOUR_API_KEY>" \ --header "Content-type: application/json" \ --data " { "name":"Donate with us", "type":{ "codename":"article" }, "sitemap_locations":[ { "codename":"articles" } ] }"

    This resolves the reference in the On Roasts item.

    Lastly, import content of the Donate with us item by upserting its language variant:

    • C#
    using KenticoCloud.ContentManagement; ContentManagementOptions options = new ContentManagementOptions { ApiKey = "<YOUR_API_KEY>", ProjectId = "14372844-0a5d-434a-8423-605b8a631623" }; ContentManagementClient client = new ContentManagementClient(options); ArticleModel stronglyTypedElements = new ArticleModel { Title = "Donate with us", RelatedArticles = new [] { ContentItemIdentifier.ByExternalId("123") } }; ContentItemIdentifier itemIdentifier = ContentItemIdentifier.ByExternalId("456"); LanguageIdentifier languageIdentifier = LanguageIdentifier.ByCodename("en-US"); ContentItemVariantIdentifier identifier = new ContentItemVariantIdentifier(itemIdentifier, languageIdentifier); ContentItemVariantModel<ArticleModel> responseVariant = await client.UpsertContentItemVariantAsync<ArticleModel>(identifier, stronglyTypedElements);
    using KenticoCloud.ContentManagement; ContentManagementOptions options = new ContentManagementOptions { ApiKey = "<YOUR_API_KEY>", ProjectId = "14372844-0a5d-434a-8423-605b8a631623" }; ContentManagementClient client = new ContentManagementClient(options); ArticleModel stronglyTypedElements = new ArticleModel { Title = "Donate with us", RelatedArticles = new [] { ContentItemIdentifier.ByExternalId("123") } }; ContentItemIdentifier itemIdentifier = ContentItemIdentifier.ByExternalId("456"); LanguageIdentifier languageIdentifier = LanguageIdentifier.ByCodename("en-US"); ContentItemVariantIdentifier identifier = new ContentItemVariantIdentifier(itemIdentifier, languageIdentifier); ContentItemVariantModel<ArticleModel> responseVariant = await client.UpsertContentItemVariantAsync<ArticleModel>(identifier, stronglyTypedElements);
    • cURL
    curl --request PUT \ --url https://manage.kenticocloud.com/v1/projects/14372844-0a5d-434a-8423-605b8a631623/items/external-id/456/variants/codename/en-US \ --header "authorization: Bearer <YOUR_API_KEY>" \ --header "content-type: application/json" \ --data " { "elements":{ "title":"Donate with us", "related_articles":[ { "external_id":"123" } ] } }"
    curl --request PUT \ --url https://manage.kenticocloud.com/v1/projects/14372844-0a5d-434a-8423-605b8a631623/items/external-id/456/variants/codename/en-US \ --header "authorization: Bearer <YOUR_API_KEY>" \ --header "content-type: application/json" \ --data " { "elements":{ "title":"Donate with us", "related_articles":[ { "external_id":"123" } ] } }"

    Both references are now resolved. To verify, you can view the imported content items inside Kentico Cloud:

    In the app menu, choose Content & Assets to view the imported content items.

    Validating content

    After your import process is finished you can validate the content of your project using the {~/validate~} endpoint. The system checks your project for possible issues, such as references to missing content items or empty required elements. It returns a detailed report.

    • cURL
    curl --request POST \ --url https://manage.kenticocloud.com/v1/projects/14372844-0a5d-434a-8423-605b8a631623/validate \ --header "Authorization: Bearer <YOUR_API_KEY>" \ --header "Content-Length: 0" \ --header "Content-Type: application/json" \
    curl --request POST \ --url https://manage.kenticocloud.com/v1/projects/14372844-0a5d-434a-8423-605b8a631623/validate \ --header "Authorization: Bearer <YOUR_API_KEY>" \ --header "Content-Length: 0" \ --header "Content-Type: application/json" \

    Imagine you never added the second content item. The report would alert you to the fact that you are referencing a non-existent object:

    • JSON
    { "project":{ "id":"a7dfe559-b503-44bf-8164-a495fd14c70e", "name":"Importing linked content tutorial" }, "variant_issues":[ { "item":{ "id":"fc93acd9-3d85-50d9-ab80-c7226c553686", "name":"On Roasts", "codename":"on_roasts" }, "language":{ "id":"00000000-0000-0000-0000-000000000000", "name":"English (United States)", "codename":"en-US" }, "issues":[ { "element":{ "id":"5cc64d60-2045-fa85-10b7-cc6989c39a83", "name":"Related articles", "codename":"related_articles" }, "messages":[ "Element "Related articles" references a non-existent content item e8337cb1-faa3-524a-9bc1-3363c1e283a1." ] } ] } ] }
    { "project":{ "id":"a7dfe559-b503-44bf-8164-a495fd14c70e", "name":"Importing linked content tutorial" }, "variant_issues":[ { "item":{ "id":"fc93acd9-3d85-50d9-ab80-c7226c553686", "name":"On Roasts", "codename":"on_roasts" }, "language":{ "id":"00000000-0000-0000-0000-000000000000", "name":"English (United States)", "codename":"en-US" }, "issues":[ { "element":{ "id":"5cc64d60-2045-fa85-10b7-cc6989c39a83", "name":"Related articles", "codename":"related_articles" }, "messages":[ "Element "Related articles" references a non-existent content item e8337cb1-faa3-524a-9bc1-3363c1e283a1." ] } ] } ] }

    What's next?

    In this tutorial, you have imported two mutually referencing content items using their external IDs.