Appstore discover section API

The appstore “Discover” section is generated from data is provided using a JSON file. The discover.json consists of an array containing multiple elements, those elements are either shuffled or can be always shown.

Currently the following elements are supported:

Posts

Posts consists of a headline, a text and an optional image.

Full width media

Like images (webp is recommended due to quality, size and browser support), animations (webm or mp4) or videos 1

App showcases

Promote a set of apps

Carousel

Allow to shuffle through multiple elements

1

For videos see the example below.

Supported elements

Common element interface

All content types are sharing a common interface

{
        // The type of the element
        "type": "post",
        // ID to indentify this element
        "id": "1d41a08e-4aa6-49b3-ad1b-ff2e83bcdc88",
        // (optional) if set this element will be always shown on that specified position
        "order": 1,
        // (optional) if set the content will only be shown starting that date
        "date": "2024-04-04T20:00:00Z",
        // (optional) if set the content will only be shown UNTIL that date
        "expiryDate": "2024-05-04T20:00:00Z",
        // (optional) the optionally localized headline, if a language is missing the "en" default is used
        "headline": {
                "en": "...",
                "de": "..."
        },
        // (optional) link for the element
        "link": "https://..."
}

Simple post: type: "post"

Post are elements with a headline, a text and an (optional) media part. All elements of it can be localized.

Example 1: Post with image

{
        "type": "post",
        "id": "1d41a08e-4aa6-49b3-ad1b-ff2e83bcdc88",
        "headline": {
                "en": "Amazing wallpapers",
                "de": "Bezaubernde Hintergründe"
        },
        "text": {
                "en": "This are the most amazing wallpaers that you can possible have. Get them now!",
                "de": "Die wohl beeindruckensten Hintergründe die man haben kann. Hol sie dir jetzt!"
        },
        "link": "https://example.com/wallpapers",
        "media": {
                "alignment": "end",
                "content": {
                        "en": {
                                "src": {
                                        "src": "http://example.com/a.png",
                                        "mime": "image/png"
                                },
                                "alt": "Amazing wallpaper"
                        }
                }
        }
}
Output of example 1 - A post with an image on the right side

Example 2: Post with video

{
        "type": "post",
        "id": "1d41a08e-4aa6-49b3-ad1b-ff2e83bcdc88",
        "media": {
                "content": {
                        "en": {
                                "src": {
                                        "src": "https://example.com/preview.webp", // the preview image (or animation)
                                        "mime": "image/webp"
                                },
                                "alt": "Some alternative text",
                                "link": "http://example.com/full-video" // the link to navigate to when pressing "Play video"
                        }
                }
        }
}
Output of the example 2 - background image with "play video" button on top

Example 3: Post with localized media

It is possible to have localized media, e.g. screenshots in different languages. For this use an array for media instead:

{
        "type": "post",
        // ...
        "media": {
                "content": {
                        "en": {
                                "src": {
                                        "src": "https://example.com/english.webp",
                                        "mime": "image/webp"
                                },
                                "alt": "Some alternative text"
                        }
                        "de": {
                                "src": {
                                        "src": "https://example.com/german.webp",
                                        "mime": "image/webp"
                                },
                                "alt": "Ein Alternativtext"
                        }
                }
        }
}

Showcase: type: "showcase"

The showcase type allow to promot multiple entries in one element. It allows to either show multiple posts or apps.

Example: Showcase with headline and apps

{
        "type": "showcase",
        // ...
        "headline": {
                "en": "5 top rated apps over all time"
        },
        "content": [
                {
                        "type": "app",
                        "appId": "cospend"
                },
                {
                        "type": "app",
                        "appId": "cookbook"
                }
        ]
}
Show case of 5 apps shown in a grid with a headline above

Example: Showcase with headline and media entries

{
        "type": "showcase",
        "headline": {
                "en": "5 amazing wallpapers"
        },
        "content": [
                {
                        "type": "post",
                        "media": {
                                "content": {
                                        "en": {
                                                "src": {
                                                        "src": "http://example.com/first.png",
                                                        "mime": "image/png"
                                                },
                                                "alt": "Wallpaper..."
                                        }
                                }
                        }
                },
                {
                        "type": "post",
                        "media": {
                                "content": {
                                        "en": {
                                                "src": {
                                                        "src": "http://example.com/second.png",
                                                        "mime": "image/png"
                                                },
                                                "alt": "Another wallpaper..."
                                        }
                                }
                        }
                }
                // other posts
        ]
}
Show case of 5 images shown in a grid with a headline above

JSON data schema

The JSON can be validated using out schema, see app-discover.schema.json.

The generic element

This element all other inherit from

Property

Type

Is required

Notes

type

string

yes

id

string

yes

order

integer

For manual ordering. Elements without order are shuffled

headline

Localized strings

text

Localized strings

link

URL

allows also app://APP_ID links opening the app, if installed or the appstore page otherwise

date

string (ISO8601 date-time)

Can be set to only show this entry after the specified date

expiryDate

string (ISO8601 date-time)

Can be set to only show this entry until the specified date

 1		"genericElement": {
 2			"type": "object",
 3			"properties": {
 4				"type": {
 5					"type": "string"
 6				}
 7			},
 8			"required": ["type"]
 9		},
10
11		"genericContainerElement": {
12			"$ref": "#/$defs/genericElement",
13			"type": "object",
14			"properties": {
15				"id": {
16					"$comment": "A unique ID, using UUID v4 is recommended",
17					"type": "string"
18				},
19				"order": {
20					"$comment": "Optional order for fixed order of elements",
21					"type": "integer"
22				},
23				"headline": { "$ref": "#/$defs/localizedString" },
24				"text": { "$ref": "#/$defs/localizedString" },
25				"link": {
26					"type": "string",
27					"format": "uri-reference"
28				},
29				"date": {
30					"type": "string",
31					"format": "date-time"
32				},
33				"expiryDate": {
34					"type": "string",
35					"format": "date-time"
36				}
37			}
38		},

Localized strings

Localized strings are objects with the language code as the key and the translated string as the property, like:

{ "en": "Hello", "de": "Hallo" }

Property

Type

Is required

Notes

en

string

yes

The English text

language code

string

The translated text for that language

 1		"localizedString": {
 2			"type": "object",
 3			"properties": {
 4				"en": {
 5					"type": "string"
 6				}
 7			},
 8			"required": ["en"],
 9			"patternProperties": {
10				"^[a-z]{2,3}(_[a-z]+)?$": { "type": "string" }
11			},
12			"additionalProperties": false
13		},

Media object

Media objects are used within the post element, they allow to embed video or image media.

Property

Type

Is required

Notes

content

Media content

yes

The content to show

alignment

string

When combined with text this defined the media alignment. One of: left, right, top

 1		"mediaObject": {
 2			"type": "object",
 3			"properties": {
 4				"content": { "$ref": "#/$defs/localizedMediaContent" },
 5				"alignment": {
 6					"enum": [
 7						"start",
 8						"end",
 9						"center"
10					]
11				}
12			},
13			"required": ["content"]
14		},

Media content

The media content is dictionary similar to Localized strings but instead of strings its values are media content objects with following properties:

Property

Type

Is required

Notes

src

Media source or array of it

yes

The media source, use an array for fallback options (source sets)

alt

string

yes

The alternative text for the media

link

URL

In case of videos this is the link to navigate when pressing the play-video button

 1		"mediaContent": {
 2			"type": "object",
 3			"properties": {
 4				"src": {
 5					"oneOf": [
 6						{ "$ref": "#/$defs/mediaSource" },
 7						{
 8							"type": "array",
 9							"items": { "$ref": "#/$defs/mediaSource" }
10						}
11					]
12				},
13				"alt": { "type": "string" },
14				"link": {
15					"type": "string",
16					"format": "uri-reference"
17				}
18			},
19			"required": ["alt", "src"]
20		},

Media source

Property

Type

Is required

Notes

src

URL

yes

The URL of the media element

mime

string

yes

The MIME type of the media element

 1		"mediaSource": {
 2			"type": "object",
 3			"properties": {
 4				"mime": { "type": "string" },
 5				"src": {
 6					"type": "string",
 7					"format": "uri-reference"
 8				}
 9			},
10			"required": ["src", "mime"]
11		},

The app-element

The app element is only used in showcase elements.

Property

Type

Is required

Notes

type

string

yes

"type": "app"

appId

string

yes

The app id, e.g. text or forms

 1		"appElement": {
 2			"$ref": "#/$defs/genericElement",
 3			"type": "object",
 4			"properties": {
 5				"type": { "const": "app" },
 6				"appId": { "type": "string" }
 7			},
 8			"requiredProperties": ["appId"],
 9			"additionalProperties": false
10		},

The post-element

The post element is the basic element for media or text entries, it inherits from the The generic element and extends it by allowing the media property:

Property

Type

Is required

Notes

type

string

yes

"type": "post"

media

Media object or array

yes

Either one Media object or an array of multiple for localized media

1		"postElement": {
2			"$ref": "#/$defs/genericContainerElement",
3			"type": "object",
4			"properties": {
5				"type": { "const": "post" },
6				"media": { "$ref": "#/$defs/mediaObject" }
7			}
8		},

The showcase-element

The showcase elements allows to display multiple posts inside one element, it inherits from the The generic element and extends it by allowing the content property:

Property

Type

Is required

Notes

type

string

yes

"type": "showcase"

content

array

yes

Array of The app-element or of The post-element

 1		"showcaseElement": {
 2			"$ref": "#/$defs/genericContainerElement",
 3			"type": "object",
 4			"properties": {
 5				"type": { "const": "showcase" },
 6				"content": {
 7					"type": "array",
 8					"items": {
 9						"oneOf": [
10							{ "$ref": "#/$defs/appElement" },
11							{ "$ref": "#/$defs/postElement" }
12						],
13						"minItems": 2
14					}
15				}
16			},
17			"required": ["content"]
18		},