Store REST API

A REST API for publishing and deleting app releases has been built into the store from day one to help release automation.

API Stability Contract

The API level will change if the following occurs:

  • a required HTTP request header is added

  • a required request parameter is added

  • a JSON field of a response object is removed

  • a JSON field of a response object is changed to appear optionally

  • a JSON field of a response object is changed to a different datatype

  • an explicitly documented HTTP response header is removed

  • an explicitly documented HTTP response header is changed to a different datatype

  • the meaning of an API call changes

The API level will not change if:

  • a new HTTP response header is added

  • an optional new HTTP request header is added

  • a new response parameter is added

  • the order of the JSON attributes is changed

  • if app validation after uploading an app release is changed in any way

You have to design your app with these things in mind:

  • Don’t depend on the order of object attributes. In JSON it does not matter where the object attribute is since you access the value by name, not by index

  • Don’t limit your app to the currently available attributes. New ones might be added. If you don’t handle them, ignore them

  • Use a library to compare versions, ideally one that uses semantic versioning

Authentication

Several routes require authentication. The following authentication methods are supported:

  • Basic: Http header where CREDENTIALS is base64encode('user:password'):

    Authorization: Basic CREDENTIALS
    
  • Token: Http header where TOKEN is a token which can be looked up in your account settings or acquired through the API:

    Authorization: Token TOKEN
    

Note

If you created your account using GitHub you will always need to use token authentication since we do not have access to your password. The token can be looked up in your account settings

Specification

The following API routes are present:

Get API Token

This route will return the API token for the authenticated user. If no token exists, one will be generated.

  • Url: POST /api/v1/token

  • Authentication: Basic, Session

  • Example CURL request:

    curl -X POST https://apps.nextcloud.com/api/v1/token -u "user:password"
    
  • Returns: application/json

{"token":"4b92477ff8d5fe889be75db4c7d9a09116276920"}

Regenerate API Token

This route will generate and return a new API token for the authenticated user regardless of whether a token already exists.

  • Url: POST /api/v1/token/new

  • Authentication: Basic, Token

  • Example CURL request:

    curl -X POST https://apps.nextcloud.com/api/v1/token/new -u "user:password"
    
  • Returns: application/json

{"token":"ca3fb97920705d2c2ecdb0900f8ed5cf5744704d"}

Get All Categories

This route will return all categories and their translations.

  • Url: GET /api/v1/categories.json

  • Authentication: None

  • Caching: ETag

  • Example CURL request:

    curl https://apps.nextcloud.com/api/v1/categories.json -H 'If-None-Match: "4-2016-06-11 10:37:24+00:00"'
    
  • Returns: application/json

[
    {
        "id": "games",
        "translations": {
            "en": {
                "name": "Games",
                "description": ""
            },
            "de": {
                "name": "Spiele",
                "description": ""
            },
            "fr": {
                "name": "Jeux",
                "description": ""
            }
        }
    },
    {
        "id": "multimedia",
        "translations": {
            "en": {
                "name": "Multimedia",
                "description": ""
            },
            "de": {
                "name": "Multimedia",
                "description": ""
            },
            "fr": {
                "name": "Multimedia",
                "description": ""
            }
        }
    },
    {
        "id": "pim",
        "translations": {
            "en": {
                "name": "PIM",
                "description": ""
            },
            "de": {
                "name": "PIM",
                "description": ""
            },
            "fr": {
                "name": "PIM",
                "description": ""
            }
        }
    },
    {
        "id": "tools",
        "translations": {
            "en": {
                "name": "Tools",
                "description": ""
            },
            "de": {
                "name": "Werkzeuge",
                "description": ""
            },
            "fr": {
                "name": "Outil",
                "description": ""
            }
        }
    }
]
translations

Translated fields are stored inside a translations object. They can have any size, depending on if there is a translation. If a required language is not found, you should fall back to English.

Get All Nextcloud Releases

This will return all the Nextcloud releases that the store knows about. To check if a release can actually be downloaded check the hasRelease flag.

Note

Unsupported Nextcloud releases will be removed from the response

Note

To find the latest version that has a release you will need to use a semantic version library to sort the list. The result is unsorted.

  • Url: GET /api/v1/platforms.json

  • Authentication: None

  • Caching: ETag

  • Example CURL request:

    curl https://apps.nextcloud.com/api/v1/platforms.json -H 'If-None-Match: "4-2016-06-11 10:37:24+00:00"'
    
  • Returns: application/json

[
    {
        "hasRelease": false,
        "version": "99.0.0",
        "isSupported": true
    },
    {
        "hasRelease": true,
        "version": "9.0.0",
        "isSupported": false
    }
]
hasRelease

boolean flag that indicates if the Nextcloud release is officially out yet

isSupported

boolean flag that indicates if the Nextcloud is officially supported

Get All Apps and Releases Compatible with a Nextcloud Version

This route will return all releases to display inside Nextcloud’s apps admin area filtered by the releases which are marked as compatible with the platforms version.

  • Url: GET /api/v1/platform/{platform-version}/apps.json

  • Url parameters:

    • platform-version: semantic version, digits only: Returns all the apps and their releases that work on this version. If an app has no working releases, the app will be excluded

  • Authentication: None

  • Caching: ETag

  • Example CURL request:

    curl https://apps.nextcloud.com/api/v1/platform/9.0.0/apps.json -H 'If-None-Match: "1-1-2016-06-17 23:08:58.042321+00:00"'
    
  • Returns: application/json

[
    {
        "id": "news",
        "categories": [
            "multimedia"
        ],
        "authors": [
            {
                "name": "Bernhard Posselt",
                "mail": "",
                "homepage": ""
            },
            {
                "name": "Alessandro Cosentino",
                "mail": "",
                "homepage": ""
            },
            {
                "name": "Jan-Christoph Borchardt",
                "mail": "",
                "homepage": ""
            }
        ],
        "userDocs": "https://github.com/owncloud/news/wiki#user-documentation",
        "adminDocs": "https://github.com/owncloud/news#readme",
        "developerDocs": "https://github.com/owncloud/news/wiki#developer-documentation",
        "issueTracker": "https://github.com/owncloud/news/issues",
        "website": "https://github.com/owncloud/news",
        "discussion": "https://help.nextcloud.com/c/apps/news",
        "created": "2016-06-25T16:08:56.794719Z",
        "lastModified": "2016-06-25T16:49:25.326855Z",
        "ratingOverall": 0.5,
        "ratingNumOverall": 20,
        "ratingRecent": 1.0,
        "ratingNumRecent": 10,
        "releases": [
            {
                "version": "9.0.4-alpha.1",
                "phpExtensions": [
                    {
                        "id": "libxml",
                        "versionSpec": ">=2.7.8",
                        "rawVersionSpec": ">=2.7.8"
                    },
                    {
                        "id": "curl",
                        "versionSpec": "*",
                        "rawVersionSpec": "*"
                    },
                    {
                        "id": "SimpleXML",
                        "versionSpec": "*",
                        "rawVersionSpec": "*"
                    },
                    {
                        "id": "iconv",
                        "versionSpec": "*",
                        "rawVersionSpec": "*"
                    }
                ],
                "databases": [
                    {
                        "id": "pgsql",
                        "versionSpec": ">=9.4.0",
                        "rawVersionSpec": ">=9.4"
                    },
                    {
                        "id": "sqlite",
                        "versionSpec": "*",
                        "rawVersionSpec": "*"
                    },
                    {
                        "id": "mysql",
                        "versionSpec": ">=5.5.0",
                        "rawVersionSpec": ">=5.5"
                    }
                ],
                "shellCommands": [
                    "grep"
                ],
                "phpVersionSpec": ">=5.6.0",
                "platformVersionSpec": ">=9.0.0 <9.2.0",
                "rawPhpVersionSpec": ">=5.6",
                "rawPlatformVersionSpec": ">=10 <=10",
                "minIntSize": 64,
                "isNightly": false,
                "download": "https://github.com/owncloud/news/releases/download/8.8.0/news.tar.gz",
                "created": "2016-06-25T16:08:56.796646Z",
                "licenses": [
                    "agpl"
                ],
                "lastModified": "2016-06-25T16:49:25.319425Z",
                "signature": "909377e1a695bbaa415c10ae087ae1cc48e88066d20a5a7a8beed149e9fad3d5",
                "translations": {
                    "en": {
                        "changelog": "* **Bugfix**: Pad API last modified timestamp to milliseconds in updated items API to return only new items. API users however need to re-sync their complete contents, #24\n* **Bugfix**: Do not pad milliseconds for non millisecond timestamps in API"
                    }
                }
            }
        ],
        "screenshots": [
            {
                "url": "https://example.com/news.jpg",
                "smallThumbnail": ""
            }
        ],
        "translations": {
            "en": {
                "name": "News",
                "summary": "An RSS/Atom feed reader",
                "description": "# This is markdown\nnext line"
            }
        },
        "isFeatured": false,
        "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEojCCA4qgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwezELMAkGA1UEBhMCREUx\r\nGzAZBgNVBAgMEkJhZGVuLVd1ZXJ0dGVtYmVyZzEXMBUGA1UECgwOTmV4dGNsb3Vk\r\nIEdtYkgxNjA0BgNVBAMMLU5leHRjbG91ZCBDb2RlIFNpZ25pbmcgSW50ZXJtZWRp\r\nYXRlIEF1dGhvcml0eTAeFw0xNjA2MTIyMTA1MDZaFw00MTA2MDYyMTA1MDZaMGYx\r\nCzAJBgNVBAYTAkRFMRswGQYDVQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxEjAQBgNV\r\nBAcMCVN0dXR0Z2FydDEXMBUGA1UECgwOTmV4dGNsb3VkIEdtYkgxDTALBgNVBAMM\r\nBGNvcmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUxcrn2DC892IX\r\n8+dJjZVh9YeHF65n2ha886oeAizOuHBdWBfzqt+GoUYTOjqZF93HZMcwy0P+xyCf\r\nQqak5Ke9dybN06RXUuGP45k9UYBp03qzlUzCDalrkj+Jd30LqcSC1sjRTsfuhc+u\r\nvH1IBuBnf7SMUJUcoEffbmmpAPlEcLHxlUGlGnz0q1e8UFzjbEFj3JucMO4ys35F\r\nqZS4dhvCngQhRW3DaMlQLXEUL9k3kFV+BzlkPzVZEtSmk4HJujFCnZj1vMcjQBg\/\r\nBqq1HCmUB6tulnGcxUzt\/Z\/oSIgnuGyENeke077W3EyryINL7EIyD4Xp7sxLizTM\r\nFCFCjjH1AgMBAAGjggFDMIIBPzAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIG\r\nQDAzBglghkgBhvhCAQ0EJhYkT3BlblNTTCBHZW5lcmF0ZWQgU2VydmVyIENlcnRp\r\nZmljYXRlMB0GA1UdDgQWBBQwc1H9AL8pRlW2e5SLCfPPqtqc0DCBpQYDVR0jBIGd\r\nMIGagBRt6m6qqTcsPIktFz79Ru7DnnjtdKF+pHwwejELMAkGA1UEBhMCREUxGzAZ\r\nBgNVBAgMEkJhZGVuLVd1ZXJ0dGVtYmVyZzESMBAGA1UEBwwJU3R1dHRnYXJ0MRcw\r\nFQYDVQQKDA5OZXh0Y2xvdWQgR21iSDEhMB8GA1UEAwwYTmV4dGNsb3VkIFJvb3Qg\r\nQXV0aG9yaXR5ggIQADAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUH\r\nAwEwDQYJKoZIhvcNAQELBQADggEBADZ6+HV\/+0NEH3nahTBFxO6nKyR\/VWigACH0\r\nnaV0ecTcoQwDjKDNNFr+4S1WlHdwITlnNabC7v9rZ\/6QvbkrOTuO9fOR6azp1EwW\r\n2pixWqj0Sb9\/dSIVRpSq+jpBE6JAiX44dSR7zoBxRB8DgVO2Afy0s80xEpr5JAzb\r\nNYuPS7M5UHdAv2dr16fDcDIvn+vk92KpNh1NTeZFjBbRVQ9DXrgkRGW34TK8uSLI\r\nYG6jnfJ6eJgTaO431ywWPXNg1mUMaT\/+QBOgB299QVCKQU+lcZWptQt+RdsJUm46\r\nNY\/nARy4Oi4uOe88SuWITj9KhrFmEvrUlgM8FvoXA1ldrR7KiEg=\r\n-----END CERTIFICATE-----",
        "signatureDigest": "sha512"
    }
]
translations

Translated fields are stored inside a translations object. They can have any size, depending on if there is a translation. If a required language is not found, you should fall back to English.

isNightly

True if the release is a nightly version. New nightly releases are not required to have a higher version than the previous one to be considered greater. Instead look at the lastModified attribute to detect updates if both nightly versions are equal. Example: 1.0.0 is equal to 1.0.0, however if the second one has a nightly flag, then the second one is greater. If both versions have nightly flags and are equal, the lastModified is used to determine the precedence.

screenshots

Guaranteed to be HTTPS

smallThumbnail

Small thumbnail which can be used as preview image. Guaranteed to be HTTPS. Not required, so if not present or an empty string, use the screenshot url instead.

download

Download archive location, guaranteed to be HTTPS

versionSpec

Required versions (minimum and maximum versions) are transformed to semantic version specs. If a field is a *, this means that there is no version requirement. The following permutations can occur:

  • All versions: *

  • Maximum version only: <8.1.2

  • Minimum version only: >=9.3.2

  • Maximum and minimum version: >=9.3.2 <8.1.2

rawVersionSpec

Non semantic versions as they occur in the info.xml. The following permutations can occur:

  • All versions: *

  • Maximum version only: <=8.1.2

  • Minimum version only: >=9.3.2

  • Maximum and minimum version: >=9.3.2 <=8.1.2

ratingRecent

Rating from 0.0 to 1.0 (0.0 being the worst, 1.0 being the best) in the past 90 days

ratingNumRecent

Number of ratings for an app in the past 90 days, as in: how many votes were casted. 0 Means no ratings yet.

ratingOverall

Rating from 0.0 to 1.0 (0.0 being the worst, 1.0 being the best) of all time

ratingNumOverall

Number of ratings for an app overall, as in: how many votes were casted. 0 Means no ratings yet.

signature

A signature using SHA512 and the app’s certificate

signatureDigest

The hashing algorithm that is used to verify the signature

description

A full blown description containing Markdown

summary

A brief explanation what the app tries to do

isFeatured

Simple boolean flag which will be presented to the user as “hey take a look at this app”. Does not imply that it has been reviewed or we recommend it officially

categories

The string value is the category’s id attribute, see Get All Categories

changelog

The translated release changelog in Markdown. Can be empty for all languages

version

A semantic version without build metadata (e.g. 1.3.0, 1.2.1-alpha.1)

Get All Apps and Releases

This route will return all releases to display inside Nextcloud’s apps admin area.

  • Url: GET /api/v1/apps.json

  • Url parameters: None

  • Authentication: None

  • Caching: ETag

  • Example CURL request:

    curl https://apps.nextcloud.com/api/v1/apps.json -H 'If-None-Match: "1-1-2016-06-17 23:08:58.042321+00:00"'
    
  • Returns: application/json

[
    {
        "id": "news",
        "categories": [
            "multimedia"
        ],
        "authors": [
            {
                "name": "Bernhard Posselt",
                "mail": "",
                "homepage": ""
            },
            {
                "name": "Alessandro Cosentino",
                "mail": "",
                "homepage": ""
            },
            {
                "name": "Jan-Christoph Borchardt",
                "mail": "",
                "homepage": ""
            }
        ],
        "userDocs": "https://github.com/owncloud/news/wiki#user-documentation",
        "adminDocs": "https://github.com/owncloud/news#readme",
        "developerDocs": "https://github.com/owncloud/news/wiki#developer-documentation",
        "issueTracker": "https://github.com/owncloud/news/issues",
        "website": "https://github.com/owncloud/news",
        "discussion": "https://help.nextcloud.com/c/apps/news",
        "created": "2016-06-25T16:08:56.794719Z",
        "lastModified": "2016-06-25T16:49:25.326855Z",
        "ratingOverall": 0.5,
        "ratingNumOverall": 20,
        "ratingRecent": 1.0,
        "ratingNumRecent": 10,
        "releases": [
            {
                "version": "9.0.4-alpha.1",
                "phpExtensions": [
                    {
                        "id": "libxml",
                        "versionSpec": ">=2.7.8",
                        "rawVersionSpec": ">=2.7.8"
                    },
                    {
                        "id": "curl",
                        "versionSpec": "*",
                        "rawVersionSpec": "*"
                    },
                    {
                        "id": "SimpleXML",
                        "versionSpec": "*",
                        "rawVersionSpec": "*"
                    },
                    {
                        "id": "iconv",
                        "versionSpec": "*",
                        "rawVersionSpec": "*"
                    }
                ],
                "databases": [
                    {
                        "id": "pgsql",
                        "versionSpec": ">=9.4.0",
                        "rawVersionSpec": ">=9.4"
                    },
                    {
                        "id": "sqlite",
                        "versionSpec": "*",
                        "rawVersionSpec": "*"
                    },
                    {
                        "id": "mysql",
                        "versionSpec": ">=5.5.0",
                        "rawVersionSpec": ">=5.5"
                    }
                ],
                "shellCommands": [
                    "grep"
                ],
                "phpVersionSpec": ">=5.6.0",
                "platformVersionSpec": ">=9.0.0 <9.2.0",
                "rawPhpVersionSpec": ">=5.6",
                "rawPlatformVersionSpec": ">=10 <=10",
                "minIntSize": 64,
                "isNightly": false,
                "download": "https://github.com/owncloud/news/releases/download/8.8.0/news.tar.gz",
                "created": "2016-06-25T16:08:56.796646Z",
                "licenses": [
                    "agpl"
                ],
                "lastModified": "2016-06-25T16:49:25.319425Z",
                "signature": "909377e1a695bbaa415c10ae087ae1cc48e88066d20a5a7a8beed149e9fad3d5",
                "translations": {
                    "en": {
                        "changelog": "* **Bugfix**: Pad API last modified timestamp to milliseconds in updated items API to return only new items. API users however need to re-sync their complete contents, #24\n* **Bugfix**: Do not pad milliseconds for non millisecond timestamps in API"
                    }
                }
            }
        ],
        "screenshots": [
            {
                "url": "https://example.com/news.jpg",
                "smallThumbnail": ""
            }
        ],
        "translations": {
            "en": {
                "name": "News",
                "summary": "An RSS/Atom feed reader",
                "description": "# This is markdown\nnext line"
            }
        },
        "isFeatured": false,
        "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEojCCA4qgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwezELMAkGA1UEBhMCREUx\r\nGzAZBgNVBAgMEkJhZGVuLVd1ZXJ0dGVtYmVyZzEXMBUGA1UECgwOTmV4dGNsb3Vk\r\nIEdtYkgxNjA0BgNVBAMMLU5leHRjbG91ZCBDb2RlIFNpZ25pbmcgSW50ZXJtZWRp\r\nYXRlIEF1dGhvcml0eTAeFw0xNjA2MTIyMTA1MDZaFw00MTA2MDYyMTA1MDZaMGYx\r\nCzAJBgNVBAYTAkRFMRswGQYDVQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxEjAQBgNV\r\nBAcMCVN0dXR0Z2FydDEXMBUGA1UECgwOTmV4dGNsb3VkIEdtYkgxDTALBgNVBAMM\r\nBGNvcmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUxcrn2DC892IX\r\n8+dJjZVh9YeHF65n2ha886oeAizOuHBdWBfzqt+GoUYTOjqZF93HZMcwy0P+xyCf\r\nQqak5Ke9dybN06RXUuGP45k9UYBp03qzlUzCDalrkj+Jd30LqcSC1sjRTsfuhc+u\r\nvH1IBuBnf7SMUJUcoEffbmmpAPlEcLHxlUGlGnz0q1e8UFzjbEFj3JucMO4ys35F\r\nqZS4dhvCngQhRW3DaMlQLXEUL9k3kFV+BzlkPzVZEtSmk4HJujFCnZj1vMcjQBg\/\r\nBqq1HCmUB6tulnGcxUzt\/Z\/oSIgnuGyENeke077W3EyryINL7EIyD4Xp7sxLizTM\r\nFCFCjjH1AgMBAAGjggFDMIIBPzAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIG\r\nQDAzBglghkgBhvhCAQ0EJhYkT3BlblNTTCBHZW5lcmF0ZWQgU2VydmVyIENlcnRp\r\nZmljYXRlMB0GA1UdDgQWBBQwc1H9AL8pRlW2e5SLCfPPqtqc0DCBpQYDVR0jBIGd\r\nMIGagBRt6m6qqTcsPIktFz79Ru7DnnjtdKF+pHwwejELMAkGA1UEBhMCREUxGzAZ\r\nBgNVBAgMEkJhZGVuLVd1ZXJ0dGVtYmVyZzESMBAGA1UEBwwJU3R1dHRnYXJ0MRcw\r\nFQYDVQQKDA5OZXh0Y2xvdWQgR21iSDEhMB8GA1UEAwwYTmV4dGNsb3VkIFJvb3Qg\r\nQXV0aG9yaXR5ggIQADAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUH\r\nAwEwDQYJKoZIhvcNAQELBQADggEBADZ6+HV\/+0NEH3nahTBFxO6nKyR\/VWigACH0\r\nnaV0ecTcoQwDjKDNNFr+4S1WlHdwITlnNabC7v9rZ\/6QvbkrOTuO9fOR6azp1EwW\r\n2pixWqj0Sb9\/dSIVRpSq+jpBE6JAiX44dSR7zoBxRB8DgVO2Afy0s80xEpr5JAzb\r\nNYuPS7M5UHdAv2dr16fDcDIvn+vk92KpNh1NTeZFjBbRVQ9DXrgkRGW34TK8uSLI\r\nYG6jnfJ6eJgTaO431ywWPXNg1mUMaT\/+QBOgB299QVCKQU+lcZWptQt+RdsJUm46\r\nNY\/nARy4Oi4uOe88SuWITj9KhrFmEvrUlgM8FvoXA1ldrR7KiEg=\r\n-----END CERTIFICATE-----",
        "signatureDigest": "sha512"
    }
]
translations

Translated fields are stored inside a translations object. They can have any size, depending on if there is a translation. If a required language is not found, you should fall back to English.

isNightly

True if the release is a nightly version. New nightly releases are not required to have a higher version than the previous one to be considered greater. Instead look at the lastModified attribute to detect updates if both nightly versions are equal. Example: 1.0.0 is equal to 1.0.0, however if the second one has a nightly flag, then the second one is greater. If both versions have nightly flags and are equal, the lastModified is used to determine the precedence.

screenshots

Guaranteed to be HTTPS

smallThumbnail

Small thumbnail which can be used as preview image. Guaranteed to be HTTPS. Not required, so if not present or an empty string, use the screenshot url instead.

download

Download archive location, guaranteed to be HTTPS

versionSpec

Required versions (minimum and maximum versions) are transformed to semantic version specs. If a field is a *, this means that there is no version requirement. The following permutations can occur:

  • All versions: *

  • Maximum version only: <8.1.2

  • Minimum version only: >=9.3.2

  • Maximum and minimum version: >=9.3.2 <8.1.2

rawVersionSpec

Non semantic versions as they occur in the info.xml. The following permutations can occur:

  • All versions: *

  • Maximum version only: <=8.1.2

  • Minimum version only: >=9.3.2

  • Maximum and minimum version: >=9.3.2 <=8.1.2

ratingRecent

Rating from 0.0 to 1.0 (0.0 being the worst, 1.0 being the best) in the past 90 days

ratingNumRecent

Number of ratings for an app in the past 90 days, as in: how many votes were casted. 0 Means no ratings yet.

ratingOverall

Rating from 0.0 to 1.0 (0.0 being the worst, 1.0 being the best) of all time

ratingNumOverall

Number of ratings for an app overall, as in: how many votes were casted. 0 Means no ratings yet.

signature

A signature using SHA512 and the app’s certificate

signatureDigest

The hashing algorithm that is used to verify the signature

description

A full blown description containing Markdown

summary

A brief explanation what the app tries to do

isFeatured

Simple boolean flag which will be presented to the user as “hey take a look at this app”. Does not imply that it has been reviewed or we recommend it officially

categories

The string value is the category’s id attribute, see Get All Categories

changelog

The translated release changelog in Markdown. Can be empty for all languages

version

A semantic version without build metadata (e.g. 1.3.0, 1.2.1-alpha.1)

Register a New App

Before you can upload release you first need to register its app id. To do that use:

  • Url: POST /api/v1/apps

  • Authentication Basic, Token

  • Content-Type: application/json

  • Request body:

    • certificate: Your public certificate whose CN is equal to the app id, should be stored in ~/.nextcloud/certificates/APP_ID.cert where APP_ID is your app’s id

    • signature: A SHA512 signature over the app id using the app’s certificate, can be created using:

      echo -n "APP_ID" | openssl dgst -sha512 -sign ~/.nextcloud/certificates/APP_ID.key | openssl base64
      
    {
        "certificate": "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEojCCA4qgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwezELMAkGA1UEBhMCREUx\r\nGzAZBgNVBAgMEkJhZGVuLVd1ZXJ0dGVtYmVyZzEXMBUGA1UECgwOTmV4dGNsb3Vk\r\nIEdtYkgxNjA0BgNVBAMMLU5leHRjbG91ZCBDb2RlIFNpZ25pbmcgSW50ZXJtZWRp\r\nYXRlIEF1dGhvcml0eTAeFw0xNjA2MTIyMTA1MDZaFw00MTA2MDYyMTA1MDZaMGYx\r\nCzAJBgNVBAYTAkRFMRswGQYDVQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxEjAQBgNV\r\nBAcMCVN0dXR0Z2FydDEXMBUGA1UECgwOTmV4dGNsb3VkIEdtYkgxDTALBgNVBAMM\r\nBGNvcmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUxcrn2DC892IX\r\n8+dJjZVh9YeHF65n2ha886oeAizOuHBdWBfzqt+GoUYTOjqZF93HZMcwy0P+xyCf\r\nQqak5Ke9dybN06RXUuGP45k9UYBp03qzlUzCDalrkj+Jd30LqcSC1sjRTsfuhc+u\r\nvH1IBuBnf7SMUJUcoEffbmmpAPlEcLHxlUGlGnz0q1e8UFzjbEFj3JucMO4ys35F\r\nqZS4dhvCngQhRW3DaMlQLXEUL9k3kFV+BzlkPzVZEtSmk4HJujFCnZj1vMcjQBg\/\r\nBqq1HCmUB6tulnGcxUzt\/Z\/oSIgnuGyENeke077W3EyryINL7EIyD4Xp7sxLizTM\r\nFCFCjjH1AgMBAAGjggFDMIIBPzAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIG\r\nQDAzBglghkgBhvhCAQ0EJhYkT3BlblNTTCBHZW5lcmF0ZWQgU2VydmVyIENlcnRp\r\nZmljYXRlMB0GA1UdDgQWBBQwc1H9AL8pRlW2e5SLCfPPqtqc0DCBpQYDVR0jBIGd\r\nMIGagBRt6m6qqTcsPIktFz79Ru7DnnjtdKF+pHwwejELMAkGA1UEBhMCREUxGzAZ\r\nBgNVBAgMEkJhZGVuLVd1ZXJ0dGVtYmVyZzESMBAGA1UEBwwJU3R1dHRnYXJ0MRcw\r\nFQYDVQQKDA5OZXh0Y2xvdWQgR21iSDEhMB8GA1UEAwwYTmV4dGNsb3VkIFJvb3Qg\r\nQXV0aG9yaXR5ggIQADAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUH\r\nAwEwDQYJKoZIhvcNAQELBQADggEBADZ6+HV\/+0NEH3nahTBFxO6nKyR\/VWigACH0\r\nnaV0ecTcoQwDjKDNNFr+4S1WlHdwITlnNabC7v9rZ\/6QvbkrOTuO9fOR6azp1EwW\r\n2pixWqj0Sb9\/dSIVRpSq+jpBE6JAiX44dSR7zoBxRB8DgVO2Afy0s80xEpr5JAzb\r\nNYuPS7M5UHdAv2dr16fDcDIvn+vk92KpNh1NTeZFjBbRVQ9DXrgkRGW34TK8uSLI\r\nYG6jnfJ6eJgTaO431ywWPXNg1mUMaT\/+QBOgB299QVCKQU+lcZWptQt+RdsJUm46\r\nNY\/nARy4Oi4uOe88SuWITj9KhrFmEvrUlgM8FvoXA1ldrR7KiEg=\r\n-----END CERTIFICATE-----",
        "signature": "65e613318107bceb131af5cf8b71e773b79e1a9476506f502c8e2017b52aba15"
    }
    
  • Example CURL request:

    curl -X POST -u "user:password" https://apps.nextcloud.com/api/v1/apps -H "Content-Type: application/json" -d '{"certificate": "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEojCCA4qgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwezELMAkGA1UEBhMCREUx\r\nGzAZBgNVBAgMEkJhZGVuLVd1ZXJ0dGVtYmVyZzEXMBUGA1UECgwOTmV4dGNsb3Vk\r\nIEdtYkgxNjA0BgNVBAMMLU5leHRjbG91ZCBDb2RlIFNpZ25pbmcgSW50ZXJtZWRp\r\nYXRlIEF1dGhvcml0eTAeFw0xNjA2MTIyMTA1MDZaFw00MTA2MDYyMTA1MDZaMGYx\r\nCzAJBgNVBAYTAkRFMRswGQYDVQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxEjAQBgNV\r\nBAcMCVN0dXR0Z2FydDEXMBUGA1UECgwOTmV4dGNsb3VkIEdtYkgxDTALBgNVBAMM\r\nBGNvcmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUxcrn2DC892IX\r\n8+dJjZVh9YeHF65n2ha886oeAizOuHBdWBfzqt+GoUYTOjqZF93HZMcwy0P+xyCf\r\nQqak5Ke9dybN06RXUuGP45k9UYBp03qzlUzCDalrkj+Jd30LqcSC1sjRTsfuhc+u\r\nvH1IBuBnf7SMUJUcoEffbmmpAPlEcLHxlUGlGnz0q1e8UFzjbEFj3JucMO4ys35F\r\nqZS4dhvCngQhRW3DaMlQLXEUL9k3kFV+BzlkPzVZEtSmk4HJujFCnZj1vMcjQBg\/\r\nBqq1HCmUB6tulnGcxUzt\/Z\/oSIgnuGyENeke077W3EyryINL7EIyD4Xp7sxLizTM\r\nFCFCjjH1AgMBAAGjggFDMIIBPzAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIG\r\nQDAzBglghkgBhvhCAQ0EJhYkT3BlblNTTCBHZW5lcmF0ZWQgU2VydmVyIENlcnRp\r\nZmljYXRlMB0GA1UdDgQWBBQwc1H9AL8pRlW2e5SLCfPPqtqc0DCBpQYDVR0jBIGd\r\nMIGagBRt6m6qqTcsPIktFz79Ru7DnnjtdKF+pHwwejELMAkGA1UEBhMCREUxGzAZ\r\nBgNVBAgMEkJhZGVuLVd1ZXJ0dGVtYmVyZzESMBAGA1UEBwwJU3R1dHRnYXJ0MRcw\r\nFQYDVQQKDA5OZXh0Y2xvdWQgR21iSDEhMB8GA1UEAwwYTmV4dGNsb3VkIFJvb3Qg\r\nQXV0aG9yaXR5ggIQADAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUH\r\nAwEwDQYJKoZIhvcNAQELBQADggEBADZ6+HV\/+0NEH3nahTBFxO6nKyR\/VWigACH0\r\nnaV0ecTcoQwDjKDNNFr+4S1WlHdwITlnNabC7v9rZ\/6QvbkrOTuO9fOR6azp1EwW\r\n2pixWqj0Sb9\/dSIVRpSq+jpBE6JAiX44dSR7zoBxRB8DgVO2Afy0s80xEpr5JAzb\r\nNYuPS7M5UHdAv2dr16fDcDIvn+vk92KpNh1NTeZFjBbRVQ9DXrgkRGW34TK8uSLI\r\nYG6jnfJ6eJgTaO431ywWPXNg1mUMaT\/+QBOgB299QVCKQU+lcZWptQt+RdsJUm46\r\nNY\/nARy4Oi4uOe88SuWITj9KhrFmEvrUlgM8FvoXA1ldrR7KiEg=\r\n-----END CERTIFICATE-----","signature": "65e613318107bceb131af5cf8b71e773b79e1a9476506f502c8e2017b52aba15"}'
    
  • Returns:

    • HTTP 201: If the app was not previously present and was registered successfully

    • HTTP 204: If the app has been updated (either owner or certificate change)

    • HTTP 400: If the app id contains invalid characters, the signature could not be validated or if the posted app certificate has been revoked

    • HTTP 401: If the user is not authenticated

    • HTTP 403: If the user is not authorized to update the app signature (only owners are allowed to do so)

You can also use this route to register a new certificate for an app if you are the app owner. However keep in mind that this will delete all previous app releases, since their signatures are now invalid and not installable anymore.

Find out more how to generate and request the certificate signature by following the App Developer Guide.

Note

DO NOT post your private key which is stored in the .key file. The private certificate needs to be stored securely. If you are unsure whether a file is a private certificate or the public one: your private certificate’s content starts with —–BEGIN PRIVATE KEY—–, whereas your public certificate’s content starts with —–BEGIN CERTIFICATE—–

Note

Keep in mind that we verify that the posted certificate and the signature are valid: the certificate needs to be signed by us and your app id signature must stem from the same certificate and match the expected result.

Publish a New App Release

The following request will create a new app release or update an existing release:

  • Url: POST /api/v1/apps/releases

  • Authentication Basic, Token

  • Content-Type: application/json

  • Request body:

    • download: An Https (Http is not allowed!) link to the archive packaged (maximum size: 20 Megabytes) as tar.gz, info.xml must be smaller than 512Kb

    • signature: A SHA512 signature over the archive using the app’s certificate, can be created using:

      openssl dgst -sha512 -sign ~/.nextcloud/certificates/APP_ID.key /path/to/app.tar.gz | openssl base64
      
    • nightly (Optional): If true this release will be stored as a nightly. All previous nightly releases will be deleted.

    {
        "download": "https://example.com/release.tar.gz",
        "signature": "65e613318107bceb131af5cf8b71e773b79e1a9476506f502c8e2017b52aba15",
        "nightly": false
    }
    
  • Example CURL request:

    curl -X POST -u "user:password" https://apps.nextcloud.com/api/v1/apps/releases -H "Content-Type: application/json" -d '{"download":"https://example.com/release.tar.gz", "signature": "65e613318107bceb131af5cf8b71e773b79e1a9476506f502c8e2017b52aba15"}'
    
  • Returns:

    • HTTP 200: If the app release was updated successfully

    • HTTP 201: If the app release was created successfully

    • HTTP 400: If the app release contains invalid data, is too large, is not registered yet, the signature could not be validated, the current app certificate has been revoked or could not be downloaded from the provided link

    • HTTP 401: If the user is not authenticated

    • HTTP 403: If the user is not authorized to create or update the app release

If there is no app with the given app id yet it will fail: you need to register your app id first. Then the info.xml file which lies in the compressed archive’s folder app-id/appinfo/info.xml is being parsed and validated. Afterwards the provided signature will be validated using the app’s certificate and the downloaded archive’s SHA512 checksum. The validated result is then saved in the database. Both owners and co-maintainers are allowed to upload new releases.

If the app release version is the latest version, everything is updated. If it’s not the latest release, only release relevant details are updated. This excludes the following info.xml elements:

  • name

  • summary

  • description

  • category

  • author

  • documentation

  • bugs

  • website

  • screenshot

For more information about validation and which info.xml fields are parsed, see App Metadata

Delete an App Release

Only app owners or co-maintainers are allowed to delete an app release. The owner is the user that pushes the first release of an app to the store.

  • Url: DELETE /api/v1/apps/{app-id}/releases/{app-version}

  • Url parameters:

  • app-id: app id, lower case ASCII characters and underscores are allowed

  • app-version: app version, semantic version, digits only

  • Authentication: Basic, Token

  • Authorization: App owners and co-maintainers

  • Example CURL request:

    curl -X DELETE https://apps.nextcloud.com/api/v1/apps/news/releases/9.0.0 -u "user:password"
    
  • Returns:

    • HTTP 204: If the app release was deleted successfully

    • HTTP 401: If the user is not authenticated

    • HTTP 403: If the user is not authorized to delete the app release

    • HTTP 404: If the app release could not be found

Delete a Nightly App Release

Only app owners or co-maintainers are allowed to delete a nightly app release. The owner is the user that pushes the first release of an app to the store.

  • Url: DELETE /api/v1/apps/{app-id}/releases/nightly/{app-version}

  • Url parameters:

  • app-id: app id, lower case ASCII characters and underscores are allowed

  • app-version: app version, semantic version, digits only

  • Authentication: Basic, Token

  • Authorization: App owners and co-maintainers

  • Example CURL request:

    curl -X DELETE https://apps.nextcloud.com/api/v1/apps/news/releases/nightly/9.0.0 -u "user:password"
    
  • Returns:

    • HTTP 204: If the app release was deleted successfully

    • HTTP 401: If the user is not authenticated

    • HTTP 403: If the user is not authorized to delete the app release

    • HTTP 404: If the app release could not be found

Delete an App

Only app owners are allowed to delete an app. The owner is the user that pushes the first release of an app to the store.

Deleting an app will also delete all releases which are associated with it.

  • Url: DELETE /api/v1/apps/{app-id}

  • Url parameters:

  • app-id: app id, lower case ASCII characters and underscores are allowed

  • Authentication: Basic, Token

  • Authorization: App owners

  • Example CURL request:

    curl -X DELETE https://apps.nextcloud.com/api/v1/apps/news -u "user:password"
    
  • Returns:

  • HTTP 204: If the app was deleted successfully

  • HTTP 401: If the user is not authenticated

  • HTTP 403: If the user is not authorized to delete the app

  • HTTP 404: If the app could not be found

Get All App Ratings

This route will return all rating comments.

  • Url: GET /api/v1/ratings.json

  • Authentication: None

  • Caching: ETag

  • Example CURL request:

    curl https://apps.nextcloud.com/api/v1/ratings.json -H 'If-None-Match: ""1-2016-09-03 17:11:38.772856+00:00""'
    
  • Returns: application/json

[
    {
        "rating": 1.0,
        "ratedAt": "2016-09-03T17:11:38.772856Z",
        "translations": {
            "en": {
                "comment": "I like it"
            }
        },
        "user": {
            "id": 1,
            "firstName": "Tom",
            "lastName": "Jones"
        },
        "app": "keeweb"
    }
]
translations

can contain 0 or any number of translated comments. If no comment is available for the currently chosen language, only the rating should be considered. Contains Markdown.

rating

range from 0.0 (worst) to 1.0 (best)

Get the app discover data

TBD

For the specification of the data format see Appstore discover section API.