Skip to content

Product Revenues Query

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

The Product Revenues API returns revenue metrics broken down by product, ranked by revenue. It complements revenueSummary, which returns a single school-wide aggregate: productRevenues instead 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.

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
}
}

The AdminProductRevenue object contains the following fields:

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 — subtract it yourself for net-of-refund revenue
refundedAmountFloat!Sum of refunded line item amounts. Windowed by payment.paidAt like totalRevenue
ordersCountInt!Number of distinct payments containing this product
currencyString!Currency code (e.g., TWD, USD) in ISO 4217 format
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.

The productType argument uses the AdminProductType enum (values below). Note this differs from the productType field in the response, which is a String! containing the canonical class name (Course, Event, …) rather than the enum value (COURSE, EVENT, …):

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)

The orderBy argument uses the AdminProductRevenueOrderBy enum:

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

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 timestamp)
refundedAtIntOperatorFilter by refund timestamp (Unix timestamp)
createdAtIntOperatorFilter by creation timestamp (Unix timestamp)
tradeNoStringOperatorFilter by trade/transaction number

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

Note: The since/until arguments set the primary reporting window on paidAt. The paymentFilter fields above are applied on top of that window — use them for non-time attributes (e.g. affiliateCode, paymentType) and, when needed, for additional timestamp constraints such as refundedAt or createdAt.

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
}
}

The Product Revenues API relates to:

  • Payments: Revenue is aggregated from the line items of the underlying payments
  • Courses / Events / Membership Plans / Digital Products / Order Bumps: Each row maps to one canonical product

Below is a comprehensive view of fields available in the productRevenues query:

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!)
}
}