App Versions
curl --request GET \
--url https://api.launchmystore.io/apps/developer/{appId}/versions \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"version": "<string>",
"releaseNotes": "<string>",
"extensions": {},
"functions": {},
"wasmPaths": {}
}
'import requests
url = "https://api.launchmystore.io/apps/developer/{appId}/versions"
payload = {
"version": "<string>",
"releaseNotes": "<string>",
"extensions": {},
"functions": {},
"wasmPaths": {}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.get(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'GET',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
version: '<string>',
releaseNotes: '<string>',
extensions: {},
functions: {},
wasmPaths: {}
})
};
fetch('https://api.launchmystore.io/apps/developer/{appId}/versions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.launchmystore.io/apps/developer/{appId}/versions",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_POSTFIELDS => json_encode([
'version' => '<string>',
'releaseNotes' => '<string>',
'extensions' => [
],
'functions' => [
],
'wasmPaths' => [
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.launchmystore.io/apps/developer/{appId}/versions"
payload := strings.NewReader("{\n \"version\": \"<string>\",\n \"releaseNotes\": \"<string>\",\n \"extensions\": {},\n \"functions\": {},\n \"wasmPaths\": {}\n}")
req, _ := http.NewRequest("GET", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.launchmystore.io/apps/developer/{appId}/versions")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"version\": \"<string>\",\n \"releaseNotes\": \"<string>\",\n \"extensions\": {},\n \"functions\": {},\n \"wasmPaths\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.launchmystore.io/apps/developer/{appId}/versions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"version\": \"<string>\",\n \"releaseNotes\": \"<string>\",\n \"extensions\": {},\n \"functions\": {},\n \"wasmPaths\": {}\n}"
response = http.request(request)
puts response.read_bodyApps
App Versions
Manage app versions — create, publish, deprecate, and view install counts
GET
/
apps
/
developer
/
{appId}
/
versions
App Versions
curl --request GET \
--url https://api.launchmystore.io/apps/developer/{appId}/versions \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"version": "<string>",
"releaseNotes": "<string>",
"extensions": {},
"functions": {},
"wasmPaths": {}
}
'import requests
url = "https://api.launchmystore.io/apps/developer/{appId}/versions"
payload = {
"version": "<string>",
"releaseNotes": "<string>",
"extensions": {},
"functions": {},
"wasmPaths": {}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.get(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'GET',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
version: '<string>',
releaseNotes: '<string>',
extensions: {},
functions: {},
wasmPaths: {}
})
};
fetch('https://api.launchmystore.io/apps/developer/{appId}/versions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.launchmystore.io/apps/developer/{appId}/versions",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_POSTFIELDS => json_encode([
'version' => '<string>',
'releaseNotes' => '<string>',
'extensions' => [
],
'functions' => [
],
'wasmPaths' => [
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.launchmystore.io/apps/developer/{appId}/versions"
payload := strings.NewReader("{\n \"version\": \"<string>\",\n \"releaseNotes\": \"<string>\",\n \"extensions\": {},\n \"functions\": {},\n \"wasmPaths\": {}\n}")
req, _ := http.NewRequest("GET", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.launchmystore.io/apps/developer/{appId}/versions")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"version\": \"<string>\",\n \"releaseNotes\": \"<string>\",\n \"extensions\": {},\n \"functions\": {},\n \"wasmPaths\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.launchmystore.io/apps/developer/{appId}/versions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"version\": \"<string>\",\n \"releaseNotes\": \"<string>\",\n \"extensions\": {},\n \"functions\": {},\n \"wasmPaths\": {}\n}"
response = http.request(request)
puts response.read_bodyApp Versions
LaunchMyStore apps are versioned independently of the underlyingApp record. Each version captures a snapshot of the app’s
extensions, functions, and per-function wasmPaths, plus a free-form
releaseNotes body. Versions move through a three-state lifecycle:
draft ─► published ─► deprecated
draft— created but not visible to merchants. Cannot be installed.published— the currently active version. Auto-installs to merchants withautoUpdate = trueand nopinnedVersion.deprecated— superseded by a newer published version, or explicitly deprecated. Still installable via rollback, but new installs go to the latest published version.
published version per app at any time —
publishing a new version automatically demotes the previously
published one to deprecated.
All endpoints in this group require merchant JWT auth and verify that
the caller’s storeId matches App.developerId. Calls from
non-owners return 404 App not found.
Endpoints
| Method | Path | Purpose |
|---|---|---|
POST | /apps/developer/:appId/versions | Create a new draft version. |
GET | /apps/developer/:appId/versions | List all versions for the app. |
GET | /apps/developer/:appId/versions/stats | Per-version install counts. |
POST | /apps/developer/:appId/versions/:version/publish | Promote draft → published. |
POST | /apps/developer/:appId/versions/:version/deprecate | Promote → deprecated. |
Create Version
POST /apps/developer/:appId/versions
curl -X POST "https://api.launchmystore.io/apps/developer/lms_app_xxx/versions" \
-H "Authorization: Bearer DEVELOPER_JWT" \
-H "Content-Type: application/json" \
-d '{
"version": "1.2.0",
"releaseNotes": "Added shipping zone customization. Fixed cart-transform null guard.",
"extensions": { "blocks": [...], "snippets": [...] },
"functions": { "cart-transform-bundle": { "type": "cart_transform" } },
"wasmPaths": { "cart-transform-bundle": "r2://apps/lms_app_xxx/1.2.0/cart-transform.wasm" }
}'
string
required
Semver-valid version string (e.g.
1.0.0, 2.3.1-beta.4). Validated
with semver.valid(). Must be strictly greater than the current
published version. Invalid versions return
400 Invalid version format. Use semver (e.g. 1.0.0). Versions less
than or equal to the latest published return
400 Version must be greater than X.Y.Z.string
Free-form changelog body. Displayed to merchants when they review the
update and to staff in the review queue.
object
Snapshot of the app’s
extensions config for this version. Defaults
to App.extensions (the live config) when omitted.object
Snapshot of function definitions for this version.
object
Map of
functionHandle → wasmPath for this version’s compiled
artifacts. The artifacts themselves must already be uploaded via
POST /apps/developer/:appId/functions.{
"status": 201,
"state": "success",
"data": {
"versionId": "9f8e7d6c-...",
"appId": "lms_app_xxx",
"version": "1.2.0",
"releaseNotes": "Added shipping zone customization...",
"extensions": { "...": "..." },
"functions": { "...": "..." },
"wasmPaths": { "...": "..." },
"status": "draft",
"publishedAt": null,
"deprecatedAt": null,
"createdBy": "<developerId>",
"createdAt": "2026-05-16T12:00:00.000Z",
"updatedAt": "2026-05-16T12:00:00.000Z"
}
}
created entry is also appended to the version changelog.
List Versions
GET /apps/developer/:appId/versions
Returns every version (any status), newest-first by createdAt.
{
"status": 200,
"state": "success",
"data": [
{ "version": "1.2.0", "status": "published", "publishedAt": "2026-05-16T12:34:00.000Z" },
{ "version": "1.1.0", "status": "deprecated", "publishedAt": "2026-04-01T...", "deprecatedAt": "2026-05-16T12:34:00.000Z" },
{ "version": "1.0.0", "status": "deprecated", "publishedAt": "2026-03-01T...", "deprecatedAt": "2026-04-01T..." }
]
}
Version Stats
GET /apps/developer/:appId/versions/stats
Per-version install counts (counts installations by their
installed version).
{
"status": 200,
"state": "success",
"data": {
"totalInstallations": 1284,
"versions": [
{ "version": "1.2.0", "status": "published", "publishedAt": "2026-05-16T...", "deprecatedAt": null, "installCount": 1102 },
{ "version": "1.1.0", "status": "deprecated", "publishedAt": "2026-04-01T...", "deprecatedAt": "2026-05-16T...", "installCount": 171 },
{ "version": "1.0.0", "status": "deprecated", "publishedAt": "2026-03-01T...", "deprecatedAt": "2026-04-01T...", "installCount": 11 }
]
}
}
installCount includes installs that are pinned to that version
(merchant-initiated rollback) plus stragglers with autoUpdate = false.
Publish Version
POST /apps/developer/:appId/versions/:version/publish
Promotes a draft to published. Has three side effects:
- The previously-published version (if any) is set to
deprecatedwithdeprecatedAt = now(). App.versionis synced to the new version.- All installations with
autoUpdate = trueANDpinnedVersion IS NULLhaveinstalledVersionupdated to the new version.
curl -X POST "https://api.launchmystore.io/apps/developer/lms_app_xxx/versions/1.2.0/publish" \
-H "Authorization: Bearer DEVELOPER_JWT"
AppVersion row with status: "published" and
publishedAt populated.
Error codes:
| HTTP | Message | When |
|---|---|---|
400 | Version is already published | Calling publish on a version already in published status. |
404 | App not found | Caller doesn’t own this app, or appId doesn’t exist. |
404 | Version X.Y.Z not found | No AppVersion row matches (appId, version). |
Deprecate Version
POST /apps/developer/:appId/versions/:version/deprecate
Manually deprecate a version — useful for emergency pulls when a bug is
discovered in a previously-published version. Does not auto-promote
another version; installations already running the deprecated version
keep running until they are rolled back or the next published version
hits them via autoUpdate.
curl -X POST "https://api.launchmystore.io/apps/developer/lms_app_xxx/versions/1.1.0/deprecate" \
-H "Authorization: Bearer DEVELOPER_JWT"
status = 'deprecated', deprecatedAt = now(), appends a
deprecated changelog entry.
Changelog
Every state transition is recorded in the version changelog:action value | When fired |
|---|---|
created | POST .../versions |
published | POST .../versions/:version/publish |
deprecated | POST .../versions/:version/deprecate, or auto-demotion on publish |
rolled_back | POST /apps/store/installations/:id/rollback (per-install) |
The publish flow’s auto-update fan-out skips any installation with a
non-null
pinnedVersion — pinning is the merchant’s signal that they
do not want automatic updates for this app.