Loopwise Docs
Admin APIQueries

Product Revenues Query

Per-product revenue analytics for the Loopwise Admin API

Note: The Loopwise Admin API is currently under development and not yet available for public use. This documentation is provided for preview purposes only.

Overview

The Product Revenues API returns revenue metrics broken down by product, ranked by revenue. Where revenueSummary returns a single school-wide aggregate, productRevenues attributes revenue to each individual course, membership plan, digital product, event, or order bump.

Revenue is computed from paid line items (payment states paid, refunding, refunded) and windowed by payment.paidAt. Line items roll up to their canonical product:

  • CurriculumPlan line items roll up to their parent Course.
  • Ticket line items roll up to their parent Event.
  • OrderBump is treated as its own product category.

This query requires the analytics:read scope.

Available Queries

productRevenues

Returns a ranked list of products with their revenue metrics for the given period. Unlike most list queries, productRevenues is not paginated — it returns a plain array capped by limit.

Parameters:

ParameterTypeDescription
sinceIntStart of the period as a Unix timestamp. Defaults to 30 days ago.
untilIntEnd of the period as a Unix timestamp. Defaults to now.
productTypeAdminProductTypeRestrict results to a single product category (see Product Types).
productIds[ID!]Restrict results to specific canonical product ids. Requires productType — omitting it returns an error.
paymentFilterAdminPaymentFilterPayment-level filters applied to the underlying payments (see Payment Filtering).
orderByAdminProductRevenueOrderBySort key. Defaults to TOTAL_REVENUE_DESC.
limitIntMaximum number of products to return. Default 50, clamped to a maximum of 200.

Returns:

An array of AdminProductRevenue objects, ordered by orderBy.

Example:

query {
  productRevenues(
    since: 1704067200,
    until: 1735689600,
    productType: COURSE,
    limit: 10
  ) {
    productId
    productType
    productName
    totalRevenue
    refundedAmount
    ordersCount
    currency
    periodStart
    periodEnd
  }
}

AdminProductRevenue Object

FieldTypeDescription
productIdID!Canonical product identifier (Course / Event / MembershipPlan / DigitalProduct / OrderBump).
productTypeString!Canonical product class name (Course, Event, MembershipPlan, DigitalProduct, or OrderBump).
productNameString!Human-readable product name.
totalRevenueFloat!Sum of post-discount line item amounts. Does not subtract refundedAmount — matches revenueSummary.totalRevenue semantics. Subtract refundedAmount yourself for net-of-refund revenue.
refundedAmountFloat!Sum of refunded line item amounts. Windowed by payment.paidAt like totalRevenue — this diverges from revenueSummary, which windows refunds by refundedAt.
ordersCountInt!Number of distinct payments containing this product.
currencyString!Currency code in ISO 4217 format (e.g., TWD, USD).
periodStartString!Start of the requested period (ISO 8601).
periodEndString!End of the requested period (ISO 8601).

Net revenue: totalRevenue is gross of refunds. To compute net revenue for a product, subtract refundedAmount from totalRevenue on the client.

Product Types

The productType argument and the productType field use the AdminProductType enum:

ValueDescription
COURSECourse, aggregated across all of its curriculum plans.
MEMBERSHIP_PLANMembership plan.
DIGITAL_PRODUCTDigital download product.
EVENTEvent, aggregated across all of its ticket types.
ORDER_BUMPOrder bump upsell SKU (its own product, not rolled into the underlying course/event).

Sorting

The orderBy argument uses the AdminProductRevenueOrderBy enum:

ValueDescription
TOTAL_REVENUE_DESCOrder by totalRevenue, highest first. This is the default.

Payment Filtering

The paymentFilter argument reuses the same AdminPaymentFilter input type as the payments query, applied to the payments underlying each product's line items. This lets you compute revenue for a slice of payments — for example, only those attributed to a given affiliate, or only a specific payment method.

Filter FieldTypeDescription
idStringOperatorFilter by payment ID.
amountFloatOperatorFilter by payment amount.
paymentStateStringOperatorFilter by payment state.
paymentTypeStringOperatorFilter by payment method (e.g., credit, atm, cvs, web_atm, barcode, line_pay). Only eq, neq, in, and nin are supported; values must be valid payment types.
affiliateCodeStringOperatorFilter by affiliate tracking code.
paidAtIntOperatorFilter by payment timestamp (Unix).
refundedAtIntOperatorFilter by refund timestamp (Unix).
createdAtIntOperatorFilter by creation timestamp (Unix).
tradeNoStringOperatorFilter by trade/transaction number.

See the Payments query for the full operator reference (StringOperator, FloatOperator, IntOperator).

Note: The since/until arguments already constrain payments by paidAt. Use paymentFilter for the non-time attributes above.

Filter Examples

Revenue per course attributed to a specific affiliate:

query {
  productRevenues(
    productType: COURSE,
    paymentFilter: {
      affiliateCode: { eq: "summer-promo" }
    }
  ) {
    productId
    productName
    totalRevenue
    ordersCount
  }
}

Top-selling products paid by credit card or LINE Pay:

query {
  productRevenues(
    paymentFilter: {
      paymentType: { in: ["credit", "line_pay"] }
    },
    limit: 20
  ) {
    productId
    productType
    productName
    totalRevenue
    refundedAmount
  }
}

Revenue for specific courses by id (requires productType):

query {
  productRevenues(
    productType: COURSE,
    productIds: ["123", "456"]
  ) {
    productId
    productName
    totalRevenue
    refundedAmount
    ordersCount
  }
}
  • revenueSummary: Returns school-wide aggregated revenue metrics for a period. Use productRevenues when you need the per-product breakdown.

Detailed Query Structure

query {
  productRevenues(
    since: 1704067200,                      # Period start, Unix timestamp (Int) — default 30 days ago
    until: 1735689600,                      # Period end, Unix timestamp (Int) — default now
    productType: COURSE,                    # Product category (AdminProductType)
    productIds: ["123"],                    # Canonical ids; requires productType ([ID!])
    paymentFilter: {                        # Payment-level filters (AdminPaymentFilter)
      affiliateCode: { eq: "summer-promo" }
      paymentType: { in: ["credit", "line_pay"] }
    },
    orderBy: TOTAL_REVENUE_DESC,            # Sort key (AdminProductRevenueOrderBy)
    limit: 50                               # Max products, clamped to 200 (Int)
  ) {
    productId                               # Canonical product id (ID!)
    productType                             # Canonical class name (String!)
    productName                             # Product name (String!)
    totalRevenue                            # Gross-of-refund revenue (Float!)
    refundedAmount                          # Refunded amount (Float!)
    ordersCount                             # Distinct payment count (Int!)
    currency                                # ISO 4217 currency code (String!)
    periodStart                             # Period start, ISO 8601 (String!)
    periodEnd                               # Period end, ISO 8601 (String!)
  }
}

On this page