Skip navigation

Set up content expiration

It's often useful to let content editors schedule when a content item should expire. Ever wanted to launch a time-limited campaign? After its time is up, it's gone. To let your editors do that, you first need to make a few changes in your project and prepare your app for it.

Table of contents

    How do you plan content expiration?

    To let your editors set expiration dates for articles and other types of content, you add a date element to articles, landing pages, or any other types of items so that your editors can choose specific dates and times. And then you adjust your app so that it omits the expired articles when displaying content.

    In a nutshell, implementing scheduled expiration is a 3-step process:

    1. Add an Expire at date element to the relevant content type.
    2. Specify the expiration dates for your content.
    3. Make your app do the rest using one of these approaches:
      1. Either check the dates and only display those items that are supposed to be public.
      2. Or check the dates and unpublish the expired items via API.

    Let's go through it on a simple example with landing pages.

    You can apply the same steps in your project and app regardless of whether you need to do this for landing pages, campaigns, or banners.

    1. Add an expiration date to your model

    The content structure of all landing pages in your project is defined by a Landing page content type. Let's edit it and add a new date element.

    1. From the app menu, choose .
    2. Open the relevant content type for editing.
    3. Insert a Date & time element from the right.
    4. Name it Expire at.
    5. Add helpful guidelines for your editors, such as Choose when this content should no longer be public.
    6. Click Save.

    You can use the same approach for any other content type.

    A screenshot showing the Expire at element.

    Adding a Date & time element to a content type

    Reuse elements across types with snippets

    If you need to add identical elements to more than one type, consider using content type snippets. For example, you can wrap the Expire at date element in a Publishing period snippet and use the snippet in multiple types.

    Update content models inside your app

    To use the newly added date element inside your application, you need to specify the element in the class used for landing pages. If you're using strongly-typed models, you can map the content retrieved from the Delivery API directly to a class in your code.

    • Java
    public final class LandingPage extends ContentItem { public static final String TYPE = "landing_page"; public Date getExpireAt() { return ExpireAt.getValue(); } ... }
    public final class LandingPage extends ContentItem { public static final String TYPE = "landing_page"; public Date getExpireAt() { return ExpireAt.getValue(); } ... }
    • Swift
    var ExpireAt: DateTimeElement? ... public required init?(map: Map){ let mapper = MapElement.init(map: map) ExpireAt = mapper.map(elementName: "expire_at", elementType: DateTimeElement.self) ... }
    var ExpireAt: DateTimeElement? ... public required init?(map: Map){ let mapper = MapElement.init(map: map) ExpireAt = mapper.map(elementName: "expire_at", elementType: DateTimeElement.self) ... }
    • Java
    @ElementMapping("expire_at") ZonedDateTime ExpireAt; public ZonedDateTime getExpireAt() { return ExpireAt; } public void setExpireAt(ZonedDateTime ExpireAt) { this.ExpireAt = ExpireAt; }
    @ElementMapping("expire_at") ZonedDateTime ExpireAt; public ZonedDateTime getExpireAt() { return ExpireAt; } public void setExpireAt(ZonedDateTime ExpireAt) { this.ExpireAt = ExpireAt; }
    • JavaScript
    export class LandingPage extends KontentDelivery.ContentItem { constructor(){ super({ propertyResolver: ((elementName) => { if (elementName === 'expire_at'){ return 'ExpireAt'; } ... }), }) } }
    export class LandingPage extends KontentDelivery.ContentItem { constructor(){ super({ propertyResolver: ((elementName) => { if (elementName === 'expire_at'){ return 'ExpireAt'; } ... }), }) } }
    • C#
    public const string ExpireAtCodename = "expire_at"; public DateTime? ExpireAt { get; set; }
    public const string ExpireAtCodename = "expire_at"; public DateTime? ExpireAt { get; set; }
    • PHP
    <?php // Tip: Find more about PHP SDKs at https://docs.kontent.ai/php class LandingPageModel { public $ExpireAt = null; // ... }
    <?php // Tip: Find more about PHP SDKs at https://docs.kontent.ai/php class LandingPageModel { public $ExpireAt = null; // ... }
    • TypeScript
    export class LandingPage extends ContentItem { public ExpireAt: Elements.DateTimeElement; ... }
    export class LandingPage extends ContentItem { public ExpireAt: Elements.DateTimeElement; ... }

    2. Add expiration dates to your content

    Now add the dates to each of your landing pages.

    1. From the app menu, choose .
    2. Using the filters on the left, select Landing page in the content type drop-down.
    3. Click an item to edit it.
    4. Fill in the Expire at date element.
    5. Repeat for all filtered items.
    A screenshot showing the Expire at element for scheduled expiration.

    Prepared dates in the Expire at element

    3. Display only live content

    You have two options here. Either omit expired content before displaying it or unpublish the expired content via API. Let's check these approaches in detail.

    Option A: Omit expired content

    To ensure your app only displays landing pages that should be public, you need to add a filtering logic to your app. This lets you check for pages with an expiration date in the past and also without an expiration date (empty dates and null values). If a page's expiration date is in the past, you filter it out. If there's no date, that landing page will never expire.

    Modify your application to check the Expire at dates of each item and add a middle step that filters out all items that aren't supposed to be public at the time.

    • Java
    // Tip: Find more about Java/JavaRx SDKs at https://docs.kontent.ai/javaandroid import com.github.kentico.kontent_delivery_core.*; import com.github.kentico.kontent_delivery_rx.*; import io.reactivex.Observer; import io.reactivex.disposables.Disposable; import io.reactivex.functions.Function; Date now = new Date(); // Prepares an array to hold strongly-typed models List<TypeResolver<?>> typeResolvers = new ArrayList<>(); // Registers the type resolvers typeResolvers.add(new TypeResolver<>(LandingPage.TYPE, new Function<Void, LandingPage>() { @Override public LandingPage apply(Void input) { return new LandingPage(); } })); // Prepares the DeliveryService configuration object String projectId = "8d20758c-d74c-4f59-ae04-ee928c0816b7"; IDeliveryConfig config = DeliveryConfig.newConfig(projectId) .withTypeResolvers(typeResolvers); // Initializes a DeliveryService for Java projects IDeliveryService deliveryService = new DeliveryService(config); // Gets all landing pages using a simple request List<LandingPage> landingPages = deliveryService.<LandingPage>items() .equalsFilter("system.type", "landing_page") .get() .getItems(); List<LandingPage> publishedItems = new ArrayList<>(); for (LandingPage page : landingPages) { if ( page.getExpireAt() == null || page.getExpireAt().after(now)) { publishedItems.add(page); } } // Gets all landing pages using RxJava2 deliveryService.<LandingPage>items() .equalsFilter("system.type", "landing_page") .getObservable() .subscribe(new Observer<DeliveryItemListingResponse<LandingPage>>() { @Override public void onSubscribe(Disposable d) { } @Override public void onNext(DeliveryItemListingResponse<LandingPage> response) { Date now = new Date(); // Gets the retrieved landing pages List<LandingPage> landingPages = response.getItems(); List<LandingPage> publishedItems = new ArrayList<>(); // Filters the landing pages, keeping those that should be public for (LandingPage page : landingPages) { if (page.getExpireAt() == null || page.getExpireAt().after(now)) { publishedItems.add(page); } } } @Override public void onError(Throwable e) { } @Override public void onComplete() { } });
    // Tip: Find more about Java/JavaRx SDKs at https://docs.kontent.ai/javaandroid import com.github.kentico.kontent_delivery_core.*; import com.github.kentico.kontent_delivery_rx.*; import io.reactivex.Observer; import io.reactivex.disposables.Disposable; import io.reactivex.functions.Function; Date now = new Date(); // Prepares an array to hold strongly-typed models List<TypeResolver<?>> typeResolvers = new ArrayList<>(); // Registers the type resolvers typeResolvers.add(new TypeResolver<>(LandingPage.TYPE, new Function<Void, LandingPage>() { @Override public LandingPage apply(Void input) { return new LandingPage(); } })); // Prepares the DeliveryService configuration object String projectId = "8d20758c-d74c-4f59-ae04-ee928c0816b7"; IDeliveryConfig config = DeliveryConfig.newConfig(projectId) .withTypeResolvers(typeResolvers); // Initializes a DeliveryService for Java projects IDeliveryService deliveryService = new DeliveryService(config); // Gets all landing pages using a simple request List<LandingPage> landingPages = deliveryService.<LandingPage>items() .equalsFilter("system.type", "landing_page") .get() .getItems(); List<LandingPage> publishedItems = new ArrayList<>(); for (LandingPage page : landingPages) { if ( page.getExpireAt() == null || page.getExpireAt().after(now)) { publishedItems.add(page); } } // Gets all landing pages using RxJava2 deliveryService.<LandingPage>items() .equalsFilter("system.type", "landing_page") .getObservable() .subscribe(new Observer<DeliveryItemListingResponse<LandingPage>>() { @Override public void onSubscribe(Disposable d) { } @Override public void onNext(DeliveryItemListingResponse<LandingPage> response) { Date now = new Date(); // Gets the retrieved landing pages List<LandingPage> landingPages = response.getItems(); List<LandingPage> publishedItems = new ArrayList<>(); // Filters the landing pages, keeping those that should be public for (LandingPage page : landingPages) { if (page.getExpireAt() == null || page.getExpireAt().after(now)) { publishedItems.add(page); } } } @Override public void onError(Throwable e) { } @Override public void onComplete() { } });
    • Swift
    // Tip: Find more about Swift SDK at https://docs.kontent.ai/ios import KenticoKontentDelivery let client = DeliveryClient.init(projectId: "8d20758c-d74c-4f59-ae04-ee928c0816b7") let landingPageQueryParameters = QueryBuilder.params().type("landing_page") // More about strongly-typed models https://github.com/Kentico/kontent-delivery-sdk-swift#using-strongly-typed-models client.getItems(modelType: LandingPage.self, queryParameters: landingPageQueryParameters) { (isSuccess, itemsResponse, error) in if isSuccess { if let landingPages = itemsResponse?.items { let now = Date() // Filters out the landing pages with no value in Expire at elements var publishedItems = landingPages.filter { (($0.ExpireAt?.value)! > now) } } } else { if let error = error { print(error) } } }
    // Tip: Find more about Swift SDK at https://docs.kontent.ai/ios import KenticoKontentDelivery let client = DeliveryClient.init(projectId: "8d20758c-d74c-4f59-ae04-ee928c0816b7") let landingPageQueryParameters = QueryBuilder.params().type("landing_page") // More about strongly-typed models https://github.com/Kentico/kontent-delivery-sdk-swift#using-strongly-typed-models client.getItems(modelType: LandingPage.self, queryParameters: landingPageQueryParameters) { (isSuccess, itemsResponse, error) in if isSuccess { if let landingPages = itemsResponse?.items { let now = Date() // Filters out the landing pages with no value in Expire at elements var publishedItems = landingPages.filter { (($0.ExpireAt?.value)! > now) } } } else { if let error = error { print(error) } } }
    • Java
    // Tip: Find more about Java/JavaRx SDKs at https://docs.kontent.ai/java import com.github.kentico.kontent.delivery; DeliveryClient client = new DeliveryClient("8d20758c-d74c-4f59-ae04-ee928c0816b7"); List<NameValuePair> params = DeliveryParameterBuilder.params() .filterEquals("system.type", "landing_page") .build(); // Create strongly typed models according to https://docs.kontent.ai/strongly-typed-models List<LandingPageItem> items = client.getItems(LandingPageItem.class, params); List<LandingPageItem> publishedItems = items.stream() .filter(item -> (item.getExpireAt() > today || item.getExpireAt() == null)) .collect(Collectors.toList());
    // Tip: Find more about Java/JavaRx SDKs at https://docs.kontent.ai/java import com.github.kentico.kontent.delivery; DeliveryClient client = new DeliveryClient("8d20758c-d74c-4f59-ae04-ee928c0816b7"); List<NameValuePair> params = DeliveryParameterBuilder.params() .filterEquals("system.type", "landing_page") .build(); // Create strongly typed models according to https://docs.kontent.ai/strongly-typed-models List<LandingPageItem> items = client.getItems(LandingPageItem.class, params); List<LandingPageItem> publishedItems = items.stream() .filter(item -> (item.getExpireAt() > today || item.getExpireAt() == null)) .collect(Collectors.toList());
    • JavaScript
    // Tip: Find more about JS/TS SDKs at https://docs.kontent.ai/javascript const KontentDelivery = require('@kentico/kontent-delivery'); const _ = require('underscore'); // Create strongly typed models according to https://docs.kontent.ai/strongly-typed-models class LandingPage extends KontentDelivery.ContentItem { constructor() { super(); } } const deliveryClient = new KontentDelivery.DeliveryClient({ projectId: '8d20758c-d74c-4f59-ae04-ee928c0816b7', typeResolvers: [ new KontentDelivery.TypeResolver('landing_page', (rawData) => new LandingPage) ] }); const d = new Date(); const now = d.toISOString(); deliveryClient.items() .type('landing_page') .toObservable() .subscribe(response => console.log(_.filter(response.items, function (i) { return ((i.ExpireAt > now || i.ExpireAt === undefined || i.ExpireAt === null)) })));
    // Tip: Find more about JS/TS SDKs at https://docs.kontent.ai/javascript const KontentDelivery = require('@kentico/kontent-delivery'); const _ = require('underscore'); // Create strongly typed models according to https://docs.kontent.ai/strongly-typed-models class LandingPage extends KontentDelivery.ContentItem { constructor() { super(); } } const deliveryClient = new KontentDelivery.DeliveryClient({ projectId: '8d20758c-d74c-4f59-ae04-ee928c0816b7', typeResolvers: [ new KontentDelivery.TypeResolver('landing_page', (rawData) => new LandingPage) ] }); const d = new Date(); const now = d.toISOString(); deliveryClient.items() .type('landing_page') .toObservable() .subscribe(response => console.log(_.filter(response.items, function (i) { return ((i.ExpireAt > now || i.ExpireAt === undefined || i.ExpireAt === null)) })));
    • C#
    // Tip: Find more about .NET SDKs at https://docs.kontent.ai/net using Kentico.Kontent.Delivery; // Creates an instance of the delivery client // ProTip: Use DI for this in your apps https://docs.kontent.ai/net-register-client IDeliveryClient client = DeliveryClientBuilder .WithProjectId("8d20758c-d74c-4f59-ae04-ee928c0816b7") .Build(); // Gets all landing pages // Create strongly typed models according to https://docs.kontent.ai/strongly-typed-models var response = await client.GetItemsAsync<LandingPage>( new EqualsFilter("system.type", "landing_page") ); var today = System.DateTime.Today; // Filters the landing pages, keeping those that should be public var itemsToDisplay = response.Items.Where((item) => (item.ExpireAt > today || item.ExpireAt == null)); return View(itemsToDisplay);
    // Tip: Find more about .NET SDKs at https://docs.kontent.ai/net using Kentico.Kontent.Delivery; // Creates an instance of the delivery client // ProTip: Use DI for this in your apps https://docs.kontent.ai/net-register-client IDeliveryClient client = DeliveryClientBuilder .WithProjectId("8d20758c-d74c-4f59-ae04-ee928c0816b7") .Build(); // Gets all landing pages // Create strongly typed models according to https://docs.kontent.ai/strongly-typed-models var response = await client.GetItemsAsync<LandingPage>( new EqualsFilter("system.type", "landing_page") ); var today = System.DateTime.Today; // Filters the landing pages, keeping those that should be public var itemsToDisplay = response.Items.Where((item) => (item.ExpireAt > today || item.ExpireAt == null)); return View(itemsToDisplay);
    • PHP
    <?php // Tip: Find more about PHP SDKs at https://docs.kontent.ai/php // Defined by Composer to include required libraries require __DIR__ . '/vendor/autoload.php'; use \Datetime; use Kentico\Kontent\Delivery\DeliveryClient; use Kentico\Kontent\Delivery\QueryParams; $client = new DeliveryClient('8d20758c-d74c-4f59-ae04-ee928c0816b7'); $items = $client->getItems((new QueryParams()) ->equals('system.type', 'landing_page')); $publishedItems = array_filter($items->items, function($item){ $now = new DateTime(); return ($item->ExpireAt > $now || is_null($item->ExpireAt)); });
    <?php // Tip: Find more about PHP SDKs at https://docs.kontent.ai/php // Defined by Composer to include required libraries require __DIR__ . '/vendor/autoload.php'; use \Datetime; use Kentico\Kontent\Delivery\DeliveryClient; use Kentico\Kontent\Delivery\QueryParams; $client = new DeliveryClient('8d20758c-d74c-4f59-ae04-ee928c0816b7'); $items = $client->getItems((new QueryParams()) ->equals('system.type', 'landing_page')); $publishedItems = array_filter($items->items, function($item){ $now = new DateTime(); return ($item->ExpireAt > $now || is_null($item->ExpireAt)); });
    • Ruby
    # Tip: Find more about Ruby SDKs at https://docs.kontent.ai/ruby require 'delivery-sdk-ruby' delivery_client = Kentico::Kontent::Delivery::DeliveryClient.new project_id: '8d20758c-d74c-4f59-ae04-ee928c0816b7' delivery_client.items('system.type'.eq('landing_page')) .execute do |response| now = DateTime.now.strftime '%Y-%M-%dT%H:%M:%SZ' items_to_display = response.items.select { |i| i.expire_at > now || i.expire_at.nil? } end
    # Tip: Find more about Ruby SDKs at https://docs.kontent.ai/ruby require 'delivery-sdk-ruby' delivery_client = Kentico::Kontent::Delivery::DeliveryClient.new project_id: '8d20758c-d74c-4f59-ae04-ee928c0816b7' delivery_client.items('system.type'.eq('landing_page')) .execute do |response| now = DateTime.now.strftime '%Y-%M-%dT%H:%M:%SZ' items_to_display = response.items.select { |i| i.expire_at > now || i.expire_at.nil? } end
    • TypeScript
    // Tip: Find more about JS/TS SDKs at https://docs.kontent.ai/javascript import { ContentItem, DeliveryClient, Elements, TypeResolver } from '@kentico/kontent-delivery'; import * as _ from 'underscore'; // Create strongly typed models according to https://docs.kontent.ai/strongly-typed-models export class LandingPage extends ContentItem { public title: Elements.TextElement; public summary: Elements.TextElement; public post_date: Elements.DateTimeElement; public teaser_image: Elements.AssetsElement; } const deliveryClient = new DeliveryClient({ projectId: '8d20758c-d74c-4f59-ae04-ee928c0816b7', typeResolvers: [ new TypeResolver('landing_page', (rawData) => new LandingPage) ] }); const d = new Date(); const now = d.toISOString(); deliveryClient.items<LandingPage>() .type('landing_page') .toObservable() .subscribe(response => console.log(_.filter(response.items, function (i) { return ((i.ExpireAt > now || i.ExpireAt === undefined || i.ExpireAt === null)) })));
    // Tip: Find more about JS/TS SDKs at https://docs.kontent.ai/javascript import { ContentItem, DeliveryClient, Elements, TypeResolver } from '@kentico/kontent-delivery'; import * as _ from 'underscore'; // Create strongly typed models according to https://docs.kontent.ai/strongly-typed-models export class LandingPage extends ContentItem { public title: Elements.TextElement; public summary: Elements.TextElement; public post_date: Elements.DateTimeElement; public teaser_image: Elements.AssetsElement; } const deliveryClient = new DeliveryClient({ projectId: '8d20758c-d74c-4f59-ae04-ee928c0816b7', typeResolvers: [ new TypeResolver('landing_page', (rawData) => new LandingPage) ] }); const d = new Date(); const now = d.toISOString(); deliveryClient.items<LandingPage>() .type('landing_page') .toObservable() .subscribe(response => console.log(_.filter(response.items, function (i) { return ((i.ExpireAt > now || i.ExpireAt === undefined || i.ExpireAt === null)) })));

    This keeps the landing pages published and lets you display only those that should be public.

    Filtering by date with Delivery API

    If all of your landing pages have an expiration date, you can also use the Delivery API directly to filter them out. For example, you can use a query parameter like elements.expire_at[gt]=now (where now is the current date and time in a correct ISO 8601Opens in a new window format) to get articles with expiration date in the future. See filtering content in the API reference for more details.

    Option B: Unpublish expired content

    If you'd like to unpublish the landing pages after their (expiration) time is up, you need to automatically unpublish them via Management API. Whenever you get content from Kontent in your app, you check for Expire at elements in the items you've got and see whether the element's value is in the past. If it is, you call the unpublish endpoint on the specific items.

    For example, to unpublish a landing page named You're this close to winning! with the codename you_re_this_close_to_winning_ in your project's default language, send a PUT request to the Management API as you see in the code sample.

    • cURL
    curl --request PUT \ --url https://manage.kontent.ai/v2/projects/<YOUR_PROJECT_ID>/items/codename/you_re_this_close_to_winning_ /variants/codename/default/unpublish \ --header 'Authorization: Bearer <YOUR_MANAGEMENT_API_KEY>' \ --header 'Content-type: application/json'
    curl --request PUT \ --url https://manage.kontent.ai/v2/projects/<YOUR_PROJECT_ID>/items/codename/you_re_this_close_to_winning_ /variants/codename/default/unpublish \ --header 'Authorization: Bearer <YOUR_MANAGEMENT_API_KEY>' \ --header 'Content-type: application/json'

    By adopting this approach you don't need to worry about filtering your items by current date because any expired content will no longer be public. You just get the published content you have and display it.

    What's next?

    In this tutorial, you learned how to add date elements, set expiration dates for your content, and display only the content that should be published.