What goes in a course revenue dashboard?
A course revenue dashboard answers the question every creator platform's own reporting dodges: what did you actually keep? Gross sales, minus the platform's cut, minus payment processing, minus refunds and chargebacks, reconciled against the money that landed in your bank account. Everything else on the dashboard — product mix, offer performance, payment-plan revenue — hangs off that spine.
Which cards belong on this dashboard?
- Gross revenue vs. net revenue after all fees, by month
- Effective fee rate: platform plus payment fees as a share of gross
- Revenue by product or course, top 15
- New purchases vs. payment-plan installments vs. renewals
- Refund rate by product and by weeks since purchase
- Revenue by offer, coupon, or traffic source
- Average order value and its trend
- Payout reconciliation: net revenue recognized vs. payouts received
What data does this dashboard need?
- An order or transaction table at the charge grain, with gross amount, currency, and timestamp
- A fee breakdown per transaction: platform fee, payment processing fee, tax collected
- A product table with type (course, membership, download, coaching) and price
- Refunds and chargebacks as their own rows or columns, dated when they occurred
- Payout records with settlement date and the transaction IDs each payout covers
How do you build it?
- Land the source data in a database — via a managed connector, scheduled API pulls, or a CSV loaded with
mb upload csv— keeping raw IDs, timestamps, currencies, and fee lines intact. - Build models at the grain this dashboard needs, with gross, fee, tax, and net amounts as explicit columns rather than per-card arithmetic.
- Create one saved question per card and certify the shared models so every dashboard downstream inherits the same definitions.
- Add dashboard filters for Date range, product or course, offer or coupon, traffic source, currency.
- Show data freshness on the dashboard — these sources lag, and every viewer should see by how much.
Example card SQL
WITH monthly AS (
SELECT
date_trunc('month', o.ordered_at)::date AS month,
SUM(o.gross_amount_usd) AS gross,
SUM(o.platform_fee_usd) AS platform_fees,
SUM(o.payment_fee_usd) AS payment_fees,
SUM(o.refund_amount_usd) AS refunds,
COUNT(*) AS orders,
COUNT(*) FILTER (WHERE o.refund_amount_usd > 0) AS refunded
FROM creator_orders o
WHERE o.ordered_at >= date_trunc('month', CURRENT_DATE)
- INTERVAL '12 months'
GROUP BY 1
), net AS (
-- Refunds are subtracted in the month they happened, not the
-- month of the original sale, so this is a cash-style view.
SELECT
m.*,
gross - platform_fees - payment_fees - refunds AS net_revenue
FROM monthly m
)
SELECT
month,
ROUND(gross, 2) AS gross_revenue,
ROUND(net_revenue, 2) AS net_revenue,
ROUND(
100.0 * (platform_fees + payment_fees) / NULLIF(gross, 0), 1
) AS effective_fee_rate_pct,
ROUND(100.0 * refunded / NULLIF(orders, 0), 1) AS refund_rate_pct,
ROUND(gross / NULLIF(orders, 0), 2) AS average_order_value,
ROUND(
100.0 * (
net_revenue / NULLIF(LAG(net_revenue) OVER (ORDER BY month), 0) - 1
), 1
) AS net_mom_growth_pct
FROM net
ORDER BY month;