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.
Overview
Section titled “Overview”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.
Available Queries
Section titled “Available Queries”productRevenues
Section titled “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:
| Parameter | Type | Description |
|---|---|---|
since | Int | Start of the period as a Unix timestamp. Defaults to 30 days ago. |
until | Int | End of the period as a Unix timestamp. Defaults to now. |
productType | AdminProductType | Restrict 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. |
paymentFilter | AdminPaymentFilter | Payment-level filters applied to the underlying payments (see Payment Filtering). |
orderBy | AdminProductRevenueOrderBy | Sort key. Defaults to TOTAL_REVENUE_DESC. |
limit | Int | Maximum 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
Section titled “AdminProductRevenue Object”The AdminProductRevenue object contains the following fields:
| Field | Type | Description |
|---|---|---|
productId | ID! | Canonical product identifier (Course / Event / MembershipPlan / DigitalProduct / OrderBump) |
productType | String! | Canonical product class name (Course, Event, MembershipPlan, DigitalProduct, or OrderBump) |
productName | String! | Human-readable product name |
totalRevenue | Float! | Sum of post-discount line item amounts. Does not subtract refundedAmount — subtract it yourself for net-of-refund revenue |
refundedAmount | Float! | Sum of refunded line item amounts. Windowed by payment.paidAt like totalRevenue |
ordersCount | Int! | Number of distinct payments containing this product |
currency | String! | Currency code (e.g., TWD, USD) in ISO 4217 format |
periodStart | String! | Start of the requested period (ISO 8601) |
periodEnd | String! | End of the requested period (ISO 8601) |
Net revenue:
totalRevenueis gross of refunds. To compute net revenue for a product, subtractrefundedAmountfromtotalRevenueon the client.
Product Types
Section titled “Product Types”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, …):
| Value | Description |
|---|---|
COURSE | Course, aggregated across all of its curriculum plans |
MEMBERSHIP_PLAN | Membership plan |
DIGITAL_PRODUCT | Digital download product |
EVENT | Event, aggregated across all of its ticket types |
ORDER_BUMP | Order bump upsell SKU (its own product, not rolled into the underlying course/event) |
Sorting
Section titled “Sorting”The orderBy argument uses the AdminProductRevenueOrderBy enum:
| Value | Description |
|---|---|
TOTAL_REVENUE_DESC | Order by totalRevenue, highest first. This is the default. |
Payment Filtering
Section titled “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 Field | Type | Description |
|---|---|---|
id | StringOperator | Filter by payment ID |
amount | FloatOperator | Filter by payment amount |
paymentState | StringOperator | Filter by payment state |
paymentType | StringOperator | Filter 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 |
affiliateCode | StringOperator | Filter by affiliate tracking code |
paidAt | IntOperator | Filter by payment timestamp (Unix timestamp) |
refundedAt | IntOperator | Filter by refund timestamp (Unix timestamp) |
createdAt | IntOperator | Filter by creation timestamp (Unix timestamp) |
tradeNo | StringOperator | Filter by trade/transaction number |
See the Payments query for the full operator reference (StringOperator, FloatOperator, IntOperator).
Note: The
since/untilarguments set the primary reporting window onpaidAt. ThepaymentFilterfields 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 asrefundedAtorcreatedAt.
Filter Examples
Section titled “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 }}Relationships
Section titled “Relationships”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
Detailed Query Structure
Section titled “Detailed Query Structure”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!) }}