> ## 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 Analytics

> Developer analytics: aggregate installs and revenue, per-app installations, webhook delivery health, and function execution logs.

App analytics surface the operational health of your **published apps** —
distinct from the store-level analytics at `/api/v1/analytics/*` (which report
a merchant's sales / orders / customers, not the app developer's own metrics).

These endpoints are read by the developer dashboard in the admin and are
scoped to the calling developer account. They live under the developer
namespace `/apps/developer/...` — there is **no** `/apps/developer/:appId/analytics/*`
sub-namespace; the capabilities below are split across the real routes.

**Auth:** developer JWT (`MERCHANT` or `PARTNER` role). The `storeId` /
developer account is resolved from the token; per-app routes additionally
verify that the caller owns the `:appId`.

<Note>
  The merchant-facing `/api/v1/analytics/*` endpoints documented in
  [Analytics overview](/api-reference/analytics/overview) report a different
  metric set (sales, orders, conversion) and require the `read_analytics` OAuth
  scope. The developer routes below are JWT-only and have no OAuth scope.
</Note>

***

## Aggregate analytics

`GET /apps/developer/analytics`

Returns a flat aggregate across **all** of the developer's apps — per-app
install counts and ratings, plus total developer-net revenue, installs, and
paid-transaction count. There is no `:appId` param and no day-bucketed
time series.

```bash theme={null}
curl -X GET "https://api.launchmystore.io/apps/developer/analytics" \
  -H "Authorization: Bearer <DEVELOPER_JWT>"
```

<ResponseField name="data.apps" type="array">
  <Expandable title="apps[]">
    <ResponseField name="appId" type="string">App UUID.</ResponseField>
    <ResponseField name="name" type="string">App name.</ResponseField>
    <ResponseField name="installCount" type="integer">Lifetime install count.</ResponseField>
    <ResponseField name="rating" type="number">Average review rating.</ResponseField>
    <ResponseField name="reviewCount" type="integer">Number of reviews.</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="data.totalRevenue" type="number">Sum of developer-net amount across all paid transactions (rounded to 2 decimals).</ResponseField>
<ResponseField name="data.totalInstalls" type="integer">Sum of `installCount` across all apps.</ResponseField>
<ResponseField name="data.totalTransactions" type="integer">Count of paid billing transactions.</ResponseField>

```json theme={null}
{
  "status": 200,
  "state": "success",
  "message": null,
  "data": {
    "apps": [
      { "appId": "fb7c1c8b-8e1d-4f4e-a05c-1b8c5a3b9f02", "name": "Tiered Discounts", "installCount": 142, "rating": 4.7, "reviewCount": 18 }
    ],
    "totalRevenue": 1172.75,
    "totalInstalls": 142,
    "totalTransactions": 96
  },
  "count": null,
  "pagination": null
}
```

***

## Per-app installations

`GET /apps/developer/:appId/installations`

Returns the raw installation rows for one app (with the installing store
attached), plus an active-subscriber count and total-install count. This is a
list, not a bucketed series.

<ParamField path="appId" type="string" required>App UUID. The caller must own it.</ParamField>

```bash theme={null}
curl -X GET "https://api.launchmystore.io/apps/developer/<APP_ID>/installations" \
  -H "Authorization: Bearer <DEVELOPER_JWT>"
```

<ResponseField name="data.installations" type="array">
  Installation rows, newest first. Each includes `installationId`, `storeId`,
  `status`, `billingStatus`, `installedVersion`, `autoUpdate`,
  `pinnedVersion`, `billingStartDate`, `trialEndsAt`, `createdAt`,
  `updatedAt`, and a nested `store` (`id`, `business`, `storeURL`, `email`).
</ResponseField>

<ResponseField name="data.activeSubscribers" type="integer">Installations that are active with an active or trialing billing status.</ResponseField>
<ResponseField name="data.totalInstalls" type="integer">Installations not pending uninstall.</ResponseField>

***

## Webhook delivery health

`GET /apps/developer/:appId/webhook-deliveries/stats`

Aggregate delivery counts across all of the app's registered webhooks.
Delivery uses 3-retry exponential backoff (1m / 5m / 15m); a delivery is
counted as `failed` only after the final retry returns non-2xx.

<ParamField path="appId" type="string" required>App UUID. The caller must own it.</ParamField>

```bash theme={null}
curl -X GET "https://api.launchmystore.io/apps/developer/<APP_ID>/webhook-deliveries/stats" \
  -H "Authorization: Bearer <DEVELOPER_JWT>"
```

The aggregate counts are returned under `data` (wrapped as
`{ status, state, data }`):

<ResponseField name="data.total" type="integer">Total delivery attempts logged.</ResponseField>
<ResponseField name="data.success" type="integer">Deliveries that succeeded.</ResponseField>
<ResponseField name="data.failed" type="integer">Deliveries that failed after the final retry.</ResponseField>
<ResponseField name="data.pending" type="integer">Deliveries not yet attempted.</ResponseField>
<ResponseField name="data.retrying" type="integer">Deliveries scheduled for a retry.</ResponseField>

```json theme={null}
{
  "status": 200,
  "state": "success",
  "data": {
    "total": 8421,
    "success": 8394,
    "failed": 27,
    "pending": 0,
    "retrying": 0
  }
}
```

For the individual delivery records (with request/response bodies and retry
history), use `GET /apps/developer/:appId/webhook-deliveries` (paged via
`page`, `limit`, and optional `status` / `topic` filters).

***

## Function execution logs

`GET /apps/developer/:appId/functions/:handle/logs`

Paged execution-log records for one function. Pass `all` as the `:handle` to
pull a unified feed across every function in the app.

<ParamField path="appId" type="string" required>App UUID. The caller must own it.</ParamField>
<ParamField path="handle" type="string" required>Function handle, or `all` for every function in the app.</ParamField>

<ParamField query="page" type="integer" default="1" />

<ParamField query="limit" type="integer" default="50" />

<ParamField query="status" type="string">Filter to one execution status.</ParamField>

```bash theme={null}
curl -X GET "https://api.launchmystore.io/apps/developer/<APP_ID>/functions/all/logs?limit=50" \
  -H "Authorization: Bearer <DEVELOPER_JWT>"
```

A companion route, `GET /apps/developer/:appId/functions/:handle/runs`
(`limit`, `status`), returns the per-run records the dashboard renders in its
runs panel.

## Error codes

| Code  | Description                                                   |
| ----- | ------------------------------------------------------------- |
| `401` | Missing or invalid developer JWT.                             |
| `403` | The calling developer does not own this app (per-app routes). |
