Skip to main content
GET
/
apps
/
store
/
installed
App Installations
curl --request GET \
  --url https://api.launchmystore.io/apps/store/installed \
  --header 'Authorization: Bearer <token>'

Documentation Index

Fetch the complete documentation index at: https://docs.launchmystore.io/llms.txt

Use this file to discover all available pages before exploring further.

App Installations

An AppInstallation row represents one app installed on one merchant store. It carries the OAuth token pair, granted scopes, billing state, and per-merchant configuration. This page documents the merchant-facing endpoints — for OAuth grant flow, see Authorize; for per-install rollback, see Rollback. All endpoints in this group require merchant JWT auth (UserRole.MERCHANT or UserRole.STAFF_ADMIN) and scope by the caller’s storeId.

Endpoints

MethodPathPurpose
GET/apps/store/installedList all installations for the caller’s store.
POST/apps/store/install/:appIdInstall an app (merchant-initiated, no OAuth flow).
POST/apps/store/uninstall/:appIdUninstall an app (cascade cleanup + webhook).
PATCH/apps/store/:installationId/configUpdate the install’s config blob.
GET/apps/installations/:installationId/settingsRead merchant-configured app settings.
PUT/apps/installations/:installationId/settingsReplace merchant-configured app settings.

Data Model: AppInstallation

interface AppInstallation {
  installationId: string;             // UUID, primary key
  appId: string;                      // FK → Apps.appId
  storeId: string;                    // FK → Accounts.id (merchant)

  // Status
  status: 'active' | 'disabled' | 'pending-uninstall';

  // OAuth tokens (cleared on revoke/uninstall)
  accessToken: string | null;
  refreshToken: string | null;
  tokenExpiresAt: Date | null;        // 24h after issue
  refreshTokenExpiresAt: Date | null; // 30d after issue
  grantedScopes: string[];            // e.g. ["read_products", "write_metafields"]

  // Per-install configuration
  config: Record<string, any>;        // dev-defined, set on install
  enabledEmbeds: Record<string, any>; // merchant toggles for app embeds
  enabledBlocks: Record<string, any>; // merchant toggles for theme blocks
  scriptData: Record<string, any>;
  settings: Record<string, any>;      // merchant-edited admin settings

  // Version pinning (see Rollback)
  installedVersion: string | null;    // semver
  autoUpdate: boolean;                // default true
  pinnedVersion: string | null;       // non-null = autoUpdate suspended

  // Billing (Stripe)
  billingStatus: 'free' | 'trial' | 'active' | 'cancelled' | 'past_due';
  billingStartDate: Date | null;
  trialEndsAt: Date | null;
  stripeSubscriptionId: string | null;
  stripeCustomerId: string | null;

  createdAt: Date;
  updatedAt: Date;
}
Indexed UNIQUE (appId, storeId) — a single app can have at most one installation per merchant.

List Installed Apps

GET /apps/store/installed

Returns every installation for the caller’s store, excluding rows in pending-uninstall. Each row is joined with the parent App record so the merchant admin can render name/icon/description without a second fetch.
curl -X GET "https://api.launchmystore.io/apps/store/installed" \
  -H "Authorization: Bearer MERCHANT_JWT"
Response:
{
  "status": 200,
  "state": "success",
  "data": [
    {
      "installationId": "inst_abc123",
      "appId": "lms_app_foundry_reviews",
      "status": "active",
      "installedVersion": "1.2.0",
      "pinnedVersion": null,
      "autoUpdate": true,
      "grantedScopes": ["read_products", "write_metafields", "read_orders"],
      "billingStatus": "active",
      "trialEndsAt": null,
      "createdAt": "2026-03-12T09:00:00.000Z",
      "updatedAt": "2026-05-16T12:34:00.000Z",
      "app": {
        "appId": "lms_app_foundry_reviews",
        "name": "Foundry Reviews",
        "iconUrl": "https://cdn.launchmystore.io/apps/foundry-reviews/icon.png",
        "developer": "Foundry Apps"
      }
    }
  ]
}

Install App

POST /apps/store/install/:appId

Direct install path used by the merchant admin “Install” button. Does not go through the OAuth code → token exchange — the merchant is already authenticated as themselves, and the platform issues an installation token bound to their storeId directly.
curl -X POST "https://api.launchmystore.io/apps/store/install/lms_app_foundry_reviews" \
  -H "Authorization: Bearer MERCHANT_JWT" \
  -H "Content-Type: application/json" \
  -d '{ "config": { "...": "..." } }'
Path parameter appId is the UUID returned by findByPk, not the app handle. Looking up an app by handle via the marketplace endpoints returns both appId and handle — pass the appId here. The marketplace ?search= filter does ILIKE matching on name, not handle, so searching for foundry-reviews will not find an app named “Foundry Reviews”. Search by the first word of the name instead.
On success, the installation row is created with status = 'active' and the app’s storefront extensions (blocks/snippets/assets) are copied to disk under public/extensions/{domainSlug}/{appHandle}/ so the storefront theme can render them. Error codes:
HTTPMessageWhen
404App not foundappId doesn’t exist.
400App is not publishedApp in draft / pending-review / rejected.
409App already installedAn active installation already exists for (appId, storeId).
409Per-shop function limit exceeded for <type>: max <N>Function caps prevent a new active install.

Uninstall App

POST /apps/store/uninstall/:appId

Atomically cleans up everything related to this installation:
  1. Cancels the Stripe subscription (stripeSubscriptionId), if any.
  2. Deletes the AppInstallation row.
  3. Deletes related AppWebhook, AppBillingTransaction, GdprRequest.appAcknowledgments, function logs, etc.
  4. Removes extension files from public/extensions/{domainSlug}/{appHandle}/.
  5. Dispatches the app/uninstalled webhook to the app’s webhookCallbackUrl.
All DB-side cleanup runs in a single Sequelize transaction — a partial failure rolls back to the pre-uninstall state. Stripe cancellation runs first (remote idempotent call) so a successful Stripe cancel followed by a DB rollback can be safely re-driven on retry.
curl -X POST "https://api.launchmystore.io/apps/store/uninstall/lms_app_foundry_reviews" \
  -H "Authorization: Bearer MERCHANT_JWT"
Response:
{
  "status": 200,
  "state": "success",
  "message": "App uninstalled successfully",
  "data": {
    "appId": "lms_app_foundry_reviews",
    "uninstalledAt": "2026-05-16T12:34:56.000Z"
  }
}
HTTPMessageWhen
404Installation not foundNo active install for (appId, storeId).
500(generic)DB transaction rolled back; safe to retry.

Update Installation Config

PATCH /apps/store/:installationId/config

Used by the merchant admin for per-install settings that the app developer defined as merchant-editable (e.g. block enable/disable toggles, embed defaults). Distinct from the settings blob — config is more raw and typically driven by the app’s installation manifest.
curl -X PATCH "https://api.launchmystore.io/apps/store/inst_abc123/config" \
  -H "Authorization: Bearer MERCHANT_JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "config": { "review_layout": "grid", "show_photos": true }
  }'

Settings

GET /apps/installations/:installationId/settings

Returns the merchant-editable settings blob.
{
  "status": 200,
  "state": "success",
  "data": {
    "settings": {
      "review_layout": "grid",
      "auto_publish": false,
      "moderation_email": "reviews@acme.example"
    }
  }
}

PUT /apps/installations/:installationId/settings

Replaces the settings blob entirely. There is no PATCH-merge variant — the caller is expected to send the full settings object every time.
curl -X PUT "https://api.launchmystore.io/apps/installations/inst_abc123/settings" \
  -H "Authorization: Bearer MERCHANT_JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "settings": {
      "review_layout": "list",
      "auto_publish": true,
      "moderation_email": "reviews@acme.example"
    }
  }'
HTTPMessageWhen
404Installation not found(installationId, storeId) not matched.
400(validation)settings not an object.

Status Reference

statusVisible to merchant adminTokens validExtensions on storefront
activeYesYes (until expiry)Yes
disabledYes (greyed out)No (validateToken returns null)No
pending-uninstallHidden from getInstalledAppsNoRemoved
disabled is set by staff admin when an app has been flagged but not fully uninstalled (e.g. payment past-due grace period, abuse review). The OAuth validateToken path short-circuits on any non-active status, so a disabled install’s API calls 401 immediately even if the access token hasn’t expired.

Uninstall reasons

The platform records an uninstall reason only when the merchant provides one via the admin UI confirmation dialog. There is currently no REST surface to read uninstall reasons after the fact — the row is deleted, not soft-deleted. Developers should subscribe to the app/uninstalled webhook to capture the reason in their own systems at the moment of uninstall.
Per-installation analytics (events, function execution counts, billing transactions) are queryable through the developer dashboard endpoints under /apps/developer/:appId/.... See Versions for the per-version install breakdown and Webhook Delivery Logs for the per-installation webhook history.