← API & MCP settings

Marlo API & MCP

Read your workspace’s Shopify customers and reporting data from your own integrations or an MCP client.

Authentication

A workspace owner or admin can generate a key in API & MCP settings. Copy the key when it is created; Marlo stores only its hash. Send it in the Authorization: Bearer YOUR_API_KEY header. The key identifies your workspace automatically.

Keep keys in a server environment variable or your client’s secret store. Never put them in browser code, a URL, or a public repository. Keys can read all selected apps in their workspace and also authorize Marlo’s ingestion endpoints. Create a separately named key for each integration. Generating another key keeps existing keys working; revoking a key immediately invalidates only that key. To rotate one integration, create a replacement, update the integration, then revoke its old key.

Workspace summary and metrics

Link to this section

GET https://app.withmarlo.app/api/v1/summary

GET https://app.withmarlo.app/api/v1/metrics/{metric}

The summary includes MRR, ARR, ARPU per paying customer, retention, concentration, usage earnings and received usage activity. Use a specific metric for a focused response: mrr, arr, usage, net-revenue-retention, or customer-concentration.

Use a workspace API key in the Authorization: Bearer header (authentication details). The first example returns the last 30 days across all selected apps, with each currency reported separately.

curl 'https://app.withmarlo.app/api/v1/summary' \
  -H "Authorization: Bearer $MARLO_API_KEY"

curl 'https://app.withmarlo.app/api/v1/summary?from=2026-09-01&to=2026-09-16&currency=USD' \
  -H "Authorization: Bearer $MARLO_API_KEY"

curl 'https://app.withmarlo.app/api/v1/metrics/net-revenue-retention?from=2026-09-01&to=2026-09-16' \
  -H "Authorization: Bearer $MARLO_API_KEY"
{
  "range": { "from": "2026-09-01", "to": "2026-09-16" },
  "asOf": "2026-09-16",
  "generatedAt": "2026-09-16T12:00:00.000Z",
  "appId": null,
  "customerScope": "included",
  "currencies": ["USD"],
  "mixedCurrencies": false,
  "byCurrency": {
    "USD": {
      "mrrCents": 120000, "subscriptionMrrCents": 100000, "usageMrrCents": 20000,
      "arrCents": 1440000, "payingCustomers": 40, "arpuCents": 3000,
      "netRevenueRetentionRate": 1.05,
      "largestCustomerShare": 0.2, "topFiveCustomerShare": 0.6, "topTenCustomerShare": 0.8,
      "usage": {
        "usageMrrCents": 20000,
        "billedUsage": { "grossCents": 18000, "feesCents": 1800, "netCents": 16200, "transactionCount": 4, "complete": true },
        "observedUsageCharges": { "grossCents": 20000, "count": 5 }
      }
    }
  },
  "metricBases": { "netRevenueRetention": "subscription_mrr", "customerConcentration": "total_mrr" },
  "usageActivity": {
    "source": "received_usage_events", "eventCount": 2, "totalCount": 200,
    "byName": [{ "name": "search", "eventCount": 2, "totalCount": 200 }],
    "lastOccurredAt": "2026-09-15T12:00:00.000Z", "loaded": true, "complete": null
  },
  "source": "marlo_synced_partner_data", "staleDataWarning": null
}

This abbreviated example omits other dashboard fields and coverage. Every summary or metric response includes range, the closing date in asOf, the response timestamp in generatedAt, source, staleDataWarning, and source coverage. Coverage records transaction and charge history start dates, transaction truncation, and lifecycle history completeness and dates. Inspect it before treating missing historical activity as zero. generatedAt does not mean Shopify was synced at that instant.

List customers

GET https://app.withmarlo.app/api/v1/customers

curl 'https://app.withmarlo.app/api/v1/customers?limit=20' \
  -H "Authorization: Bearer $MARLO_API_KEY"

Customers are ordered by ID. Pagination reflects the current synced data, so changes between requests can change which customers match.

{
  "customers": [{
    "id": "CUSTOMER_ID",
    "appId": "APP_ID",
    "name": "Example shop",
    "domain": "example.myshopify.com",
    "status": "active",
    "paying": true,
    "reportExcluded": false
  }],
  "nextCursor": null,
  "range": { "from": "2026-08-18", "to": "2026-09-16" },
  "customerScope": "included",
  "source": "customers"
}

The example shows a subset of fields. Customer objects also include country, plan, tags, health, firstSeen, lastActive, lifetime revenue and store classification where available. Monetary fields ending in Cents are integer minor units; ltvByCurrencyCents separates currencies when available. amrCents is the latest monthly subscription estimate and is zero for non-paying customers; use the customer detail endpoint below for calculated subscription and usage MRR by currency. Do not sum different currencies.

Get a customer

GET https://app.withmarlo.app/api/v1/customers/{customerId}

curl 'https://app.withmarlo.app/api/v1/customers/CUSTOMER_ID' \
  -H "Authorization: Bearer $MARLO_API_KEY"

Use the ID from the list response, or a unique example.myshopify.com domain. The response contains customer, analytics, timeline, range, and source. limit controls timeline entries (1–50, default 20), newest first; it does not limit analytics. A domain shared by multiple apps returns 409 with matching customer IDs; retry with an ID.

Customer detail can include excluded customers, but only from the workspace’s selected apps. paying uses the same current billing calculation as Marlo, independently of the install status. reportExcluded tells you whether a customer is excluded from aggregate reporting. These endpoints are read-only; customer updates are not supported.

Customer MRR, earnings and usage

Every customer detail request includes this analytics structure automatically. The example abbreviates the customer object. MCP’s get_customer returns the same analytics.

{
  "customer": { "id": "CUSTOMER_ID", "paying": true, "reportExcluded": false },
  "analytics": {
    "asOf": "2026-09-16T12:00:00.000Z",
    "paying": { "paying": true, "reason": "Paying because an active positive-value monthly subscription with recurring payment evidence is in effect on 2026-09-16. Trials and zero-value plans do not count." },
    "currencies": ["USD"],
    "current": {
      "asOf": "2026-09-16",
      "byCurrency": {
        "USD": { "mrrCents": 12000, "subscriptionMrrCents": 10000, "usageMrrCents": 2000 }
      }
    },
    "last30Days": {
      "range": { "from": "2026-08-18", "to": "2026-09-16" },
      "byCurrency": {
        "USD": {
          "grossCents": 12200, "feeCents": 1220, "netCents": 10980, "totalDeductionsCents": 1220,
          "subscriptionCents": 10000, "usageCents": 2000,
          "oneTimeCents": 500, "refundCents": 300, "transactionCount": 4,
          "observedUsageCharges": { "grossCents": 600, "count": 1, "lastOccurredAt": "2026-09-15T12:00:00.000Z" }
        }
      },
      "usageActivity": {
        "eventCount": 2, "totalCount": 200,
        "byName": [{ "name": "search", "eventCount": 2, "totalCount": 200 }],
        "lastOccurredAt": "2026-09-15T12:00:00.000Z"
      }
    },
    "coverage": {
      "transactions": { "complete": true, "coverageFrom": "2026-06-19", "coverageThrough": "2026-09-16", "truncated": false },
      "lifecycle": { "complete": true, "coverageFrom": "2020-01-01", "coverageThrough": "2026-09-16" },
      "observedUsageCharges": { "complete": true, "coverageFrom": "2026-06-19", "coverageThrough": "2026-09-16" },
      "usageActivity": { "source": "received_usage_events", "loaded": true, "complete": null }
    }
  },
  "timeline": [],
  "range": { "from": "2026-08-18", "to": "2026-09-16" },
  "source": "customer_detail"
}

coverage identifies partial or stale source data. A complete: true financial or charge range requires coverage of all 30 dates and completed Shopify history through the current reporting date. truncated indicates the loaded transaction history reached its row limit, though the requested range may still be covered. Usage activity can only show received events: its completeness remains null because Marlo cannot verify that your integration sent everything. Zero received usage does not prove the customer was inactive.

Errors and freshness

Errors return JSON with error and message. 400 means invalid parameters, 401 means a missing, invalid or revoked key, 403 means the workspace needs an active subscription, 404 means an unknown metric or a customer unavailable in this workspace, 409 means an ambiguous domain, and 503 means a temporary service failure. Retry 503 responses with exponential backoff.

Responses use the latest Shopify data synced into Marlo. When present, staleDataWarning explains freshness limits; this API does not trigger a live Shopify sync. All API responses disable caching.

Connect an MCP client

Add a remote server with Streamable HTTP transport and this URL:

https://app.withmarlo.app/api/mcp

Configure the HTTP header Authorization: Bearer YOUR_API_KEY in your client’s secure connection settings. Your client must support bearer headers for remote MCP servers; OAuth sign-in is not currently supported. No repository checkout, local server or team slug is required.

Available tools: list_customers, get_customer, get_summary, query_metrics, get_report, compare_periods, and list_sync_runs. get_summary returns the same response as the summary REST endpoint. Customer list and reporting tools accept customerScope; reporting tools also accept currency, from, to, and appId. get_report supports net-revenue-retention and customer-concentration. Customer tools have the same limits and data access as the REST API.