Queries · q013 · Chief Financial Officer · week 1, night 1
“How much processing-fee revenue did Tidewick earn each month in 2025, and how is it trending?”
Fictional people at a fictional company, each played by a language-model agent. Who they are and what they are told
The manager, at the start of week 1
Subject: Finding my bearings – first set of questions for the week
Hi Kofi,
Good to be working with you. I'm still building my mental map of the business, so I'd rather ask a few broad questions well than a lot of narrow ones badly. Given the slot budget, here's what I'd prioritize this week, roughly in order:
For this week, let's focus on #1 and #2 — those are the two I most need before I can speak credibly about 2025 and the 2026 budget. If there's room, #3 would be next; #4 and #5 can wait for a follow-up week if slots are tight.
Let me know if any of these need me to narrow scope further before you brief the data engineers.
Thanks, Tobias
The analyst · week 1, night 1, request 1 of 3
Tobias ranked #1 (2025 revenue mix) and #2 (merchant concentration) as this week's priorities, so I'm spending all 3 slots there tonight.
Scoping note on #1: I looked for a table that records merchants paying Tidewick its own monthly platform-plan fee. The plans/subscriptions/invoices tables are tenant-scoped and model each merchant's own subscription business toward their end customers (invoice.order_id/subscription_id point at the merchant's own orders/users) — they are not Tidewick billing the merchant. The only piece of Tidewick's own revenue that is actually observable in this warehouse is the processing fee Tidewick keeps on settled payments, captured explicitly in payouts.fee_amount per the handbook. So tonight I'm only requesting the processing-fee side of #1, trended monthly, and I'll flag to Tobias in the memo that platform-plan fee revenue isn't in this warehouse (likely lives in Tidewick's own internal billing/finance system) before promising a full split.
For #2, I expect a small number of merchants to dominate — my hypothesis is the top ~10% of merchants account for well over half of gross volume. I'm requesting one snapshot (who the big merchants are and their share in 2025) and one two-point trend (2024 vs 2025) to directly test whether that concentration is growing or shrinking, which is the risk question Tobias actually asked.
All three reports use payouts (gross/fee/net already split there) converted to EUR via exchange_rates, joined on the payout's currency and settlement date.
The engineer · 1 dry run on the empty schema, then the submission
WITH months AS ( SELECT DISTINCT t0.month_num AS month_num, t0.month_label AS month_label FROM calendar_days t0 WHERE t0.year_num = 2025 ), converted_payouts AS ( SELECT t0.id AS payout_id, t2.year_num AS year_num, t2.month_num AS month_num, t0.gross_amount / t1.rate AS gross_eur, t0.fee_amount / t1.rate AS fee_eur, t0.net_amount / t1.rate AS net_eur FROM payouts t0 JOIN exchange_rates t1 ON t1.base_code = 'EUR' AND t1.quote_code = t0.currency_code AND t1.as_of = CAST(t0.paid_at AS date) JOIN calendar_days t2 ON t2.calendar_date = CAST(t0.paid_at AS date) WHERE t0.paid_at IS NOT NULL AND t0.status IN ('paid', 'completed') ), current_year AS ( SELECT t0.month_num AS month_num, COUNT(*) AS payout_count, SUM(t0.gross_eur) AS gross_eur_total, SUM(t0.fee_eur) AS fee_eur_total, SUM(t0.net_eur) AS net_eur_total FROM converted_payouts t0 WHERE t0.year_num = 2025 GROUP BY t0.month_num ), prior_year AS ( SELECT t0.month_num AS month_num, SUM(t0.fee_eur) AS fee_eur_total_prior FROM converted_payouts t0 WHERE t0.year_num = 2024 GROUP BY t0.month_num ) SELECT t0.month_label AS month, COALESCE(t1.payout_count, 0) AS payout_count, ROUND(COALESCE(t1.gross_eur_total, 0), 2) AS gross_volume_eur, ROUND(COALESCE(t1.fee_eur_total, 0), 2) AS fee_revenue_eur, ROUND(COALESCE(t1.net_eur_total, 0), 2) AS net_paid_out_eur, ROUND(COALESCE(t1.fee_eur_total, 0) / NULLIF(t1.gross_eur_total, 0) * 100, 1) AS fee_rate_pct, ROUND(COALESCE(t2.fee_eur_total_prior, 0), 2) AS fee_revenue_prior_year_eur FROM months t0 LEFT JOIN current_year t1 ON t1.month_num = t0.month_num LEFT JOIN prior_year t2 ON t2.month_num = t0.month_num ORDER BY t0.month_num
“Submitted. One flag for you: the payouts.status column isn't an enumerated field in the table catalog (no allowed-values list given), so I filtered on status IN ('paid','completed') as the settled/completed states per the request's intent. If the actual literal values differ (e.g. 'succeeded'), let me know and I'll correct it in a follow-up request.”
As Kofi Mensah saw it, from the small dataset the agents worked on
month | payout_count | gross_volume_eur | fee_revenue_eur | net_paid_out_eur | fee_rate_pct | fee_revenue_prior_year_eur --------+--------------+------------------+-----------------+------------------+--------------+--------------------------- 2025-01 | 503 | 199172.31 | 4954.45 | 151256.63 | 2.5 | 2988.79 2025-02 | 426 | 117777.21 | 2907.62 | 95906.6 | 2.5 | 3263.45 2025-03 | 475 | 135070.48 | 3567.13 | 115334.32 | 2.6 | 2972.1 2025-04 | 449 | 188851.76 | 5006.79 | 169982.71 | 2.7 | 3242.26 2025-05 | 482 | 127570.11 | 3482.02 | 98002.35 | 2.7 | 3490.98 2025-06 | 463 | 113673.02 | 3123.65 | 91578.07 | 2.7 | 2773.41 2025-07 | 484 | 132602.63 | 3604.89 | 108283.9 | 2.7 | 2844.58 2025-08 | 474 | 141126.23 | 4276.29 | 118864.4 | 3.0 | 2727.22 2025-09 | 458 | 122449.4 | 3165.85 | 100229.52 | 2.6 | 2991.07 2025-10 | 507 | 154129.95 | 4108.16 | 130131.01 | 2.7 | 3558.57 2025-11 | 621 | 236843.96 | 6380.01 | 199899.74 | 2.7 | 4548.44 2025-12 | 775 | 322182.01 | 8609.0 | 250319.95 | 2.7 | 7526.96 (12 rows)
The memo and the queries that followed; links found by title, so inferred
Later queries that build on this report:
Changed only where the benchmark needs the same result on every system
WITH months AS ( SELECT DISTINCT t0.month_num AS month_num, t0.month_label AS month_label FROM calendar_days t0 WHERE t0.year_num = 2025),converted_payouts AS ( SELECT t0.id AS payout_id, t2.year_num AS year_num, t2.month_num AS month_num, t0.gross_amount / t1.rate AS gross_eur, t0.fee_amount / t1.rate AS fee_eur, t0.net_amount / t1.rate AS net_eur CAST(t0.gross_amount / t1.rate AS NUMERIC(18,6)) AS gross_eur, CAST(t0.fee_amount / t1.rate AS NUMERIC(18,6)) AS fee_eur, CAST(t0.net_amount / t1.rate AS NUMERIC(18,6)) AS net_eur FROM payouts t0 JOIN exchange_rates t1 ON t1.base_code = 'EUR' AND t1.quote_code = t0.currency_code AND t1.as_of = CAST(t0.paid_at AS date) JOIN calendar_days t2 ON t2.calendar_date = CAST(t0.paid_at AS date) WHERE t0.paid_at IS NOT NULL AND t0.status IN ('paid', 'completed')),current_year AS ( SELECT t0.month_num AS month_num, COUNT(*) AS payout_count, SUM(t0.gross_eur) AS gross_eur_total, SUM(t0.fee_eur) AS fee_eur_total, SUM(t0.net_eur) AS net_eur_total FROM converted_payouts t0 WHERE t0.year_num = 2025 GROUP BY t0.month_num),prior_year AS ( SELECT t0.month_num AS month_num, SUM(t0.fee_eur) AS fee_eur_total_prior FROM converted_payouts t0 WHERE t0.year_num = 2024 GROUP BY t0.month_num)SELECT t0.month_label AS month, COALESCE(t1.payout_count, 0) AS payout_count, ROUND(COALESCE(t1.gross_eur_total, 0), 2) AS gross_volume_eur, ROUND(COALESCE(t1.fee_eur_total, 0), 2) AS fee_revenue_eur, ROUND(COALESCE(t1.net_eur_total, 0), 2) AS net_paid_out_eur, ROUND(COALESCE(t1.fee_eur_total, 0) / NULLIF(t1.gross_eur_total, 0) * 100, 1) AS fee_rate_pct, ROUND(COALESCE(t2.fee_eur_total_prior, 0), 2) AS fee_revenue_prior_year_eur COALESCE(t1.gross_eur_total, 0) AS gross_volume_eur, COALESCE(t1.fee_eur_total, 0) AS fee_revenue_eur, COALESCE(t1.net_eur_total, 0) AS net_paid_out_eur, COALESCE(t1.fee_eur_total, 0) / NULLIF(t1.gross_eur_total, 0) * 100 AS fee_rate_pct, COALESCE(t2.fee_eur_total_prior, 0) AS fee_revenue_prior_year_eurFROM months t0LEFT JOIN current_year t1 ON t1.month_num = t0.month_numLEFT JOIN prior_year t2 ON t2.month_num = t0.month_numORDER BY t0.month_numORDER BY t0.month_num NULLS LAST, t0.month_label NULLS LAST
WITH months AS ( SELECT DISTINCT t0.month_num AS month_num, t0.month_label AS month_label FROM calendar_days t0 WHERE t0.year_num = 2025 ), converted_payouts AS ( SELECT t0.id AS payout_id, t2.year_num AS year_num, t2.month_num AS month_num, CAST(t0.gross_amount / t1.rate AS NUMERIC(18,6)) AS gross_eur, CAST(t0.fee_amount / t1.rate AS NUMERIC(18,6)) AS fee_eur, CAST(t0.net_amount / t1.rate AS NUMERIC(18,6)) AS net_eur FROM payouts t0 JOIN exchange_rates t1 ON t1.base_code = 'EUR' AND t1.quote_code = t0.currency_code AND t1.as_of = CAST(t0.paid_at AS date) JOIN calendar_days t2 ON t2.calendar_date = CAST(t0.paid_at AS date) WHERE t0.paid_at IS NOT NULL AND t0.status IN ('paid', 'completed') ), current_year AS ( SELECT t0.month_num AS month_num, COUNT(*) AS payout_count, SUM(t0.gross_eur) AS gross_eur_total, SUM(t0.fee_eur) AS fee_eur_total, SUM(t0.net_eur) AS net_eur_total FROM converted_payouts t0 WHERE t0.year_num = 2025 GROUP BY t0.month_num ), prior_year AS ( SELECT t0.month_num AS month_num, SUM(t0.fee_eur) AS fee_eur_total_prior FROM converted_payouts t0 WHERE t0.year_num = 2024 GROUP BY t0.month_num ) SELECT t0.month_label AS month, COALESCE(t1.payout_count, 0) AS payout_count, COALESCE(t1.gross_eur_total, 0) AS gross_volume_eur, COALESCE(t1.fee_eur_total, 0) AS fee_revenue_eur, COALESCE(t1.net_eur_total, 0) AS net_paid_out_eur, COALESCE(t1.fee_eur_total, 0) / NULLIF(t1.gross_eur_total, 0) * 100 AS fee_rate_pct, COALESCE(t2.fee_eur_total_prior, 0) AS fee_revenue_prior_year_eur FROM months t0 LEFT JOIN current_year t1 ON t1.month_num = t0.month_num LEFT JOIN prior_year t2 ON t2.month_num = t0.month_num ORDER BY t0.month_num NULLS LAST, t0.month_label NULLS LAST
The agents' own records, as the role play wrote them; only the personal data of the person who ran it is redacted
Report request from Kofi Mensah (analyst for the Chief Financial Officer): Title: Tidewick Processing Fee Revenue by Month, 2025 Question: How much processing-fee revenue did Tidewick earn each month in 2025, and how is it trending? Grain: one calendar month in 2025 Columns: - month (YYYY-MM) - payout count - gross volume (EUR) - fee revenue (EUR) — Tidewick's processing-fee take - net paid out to merchants (EUR) - fee rate (%) = fee revenue / gross volume - fee revenue same month prior year (EUR) Details: Source: payouts (gross_amount, fee_amount, net_amount, currency_code, paid_at/arrival_date). Include only payouts with a non-null paid_at and status indicating completed/paid settlement. Bucket by month of paid_at using calendar_days.month_label for 2025-01 through 2025-12. Convert each payout's amounts from its currency_code to EUR using exchange_rates (base_code='EUR', quote_code=payout currency, as_of = date(paid_at)); EUR amount = amount / rate. For the 'prior year' column, compute the same aggregation for the matching month in 2024. Round money to 2 decimals and the fee rate to 1 decimal place. Order by month ascending. Check your query with the warehouse's dry_run tool, then hand it in with submit.
dry_runWITH months AS ( SELECT DISTINCT t0.month_num AS month_num, t0.month_label AS month_label FROM calendar_days t0 WHERE t0.year_num = 2025 ), converted_payouts AS ( SELECT t0.id AS payout_id, t2.year_num AS year_num, t2.month_num AS month_num, t0.gross_amount / t1.rate AS gross_eur, t0.fee_amount / t1.rate AS fee_eur, t0.net_amount / t1.rate AS net_eur FROM payouts t0 JOIN exchange_rates t1 ON t1.base_code = 'EUR' AND t1.quote_code = t0.currency_code AND t1.as_of = CAST(t0.paid_at AS date) JOIN calendar_days t2 ON t2.calendar_date = CAST(t0.paid_at AS date) WHERE t0.paid_at IS NOT NULL AND t0.status IN ('paid', 'completed') ), current_year AS ( SELECT t0.month_num AS month_num, COUNT(*) AS payout_count, SUM(t0.gross_eur) AS gross_eur_total, SUM(t0.fee_eur) AS fee_eur_total, SUM(t0.net_eur) AS net_eur_total FROM converted_payouts t0 WHERE t0.year_num = 2025 GROUP BY t0.month_num ), prior_year AS ( SELECT t0.month_num AS month_num, SUM(t0.fee_eur) AS fee_eur_total_prior FROM converted_payouts t0 WHERE t0.year_num = 2024 GROUP BY t0.month_num ) SELECT t0.month_label AS month, COALESCE(t1.payout_count, 0) AS payout_count, ROUND(COALESCE(t1.gross_eur_total, 0), 2) AS gross_volume_eur, ROUND(COALESCE(t1.fee_eur_total, 0), 2) AS fee_revenue_eur, ROUND(COALESCE(t1.net_eur_total, 0), 2) AS net_paid_out_eur, ROUND(COALESCE(t1.fee_eur_total, 0) / NULLIF(t1.gross_eur_total, 0) * 100, 1) AS fee_rate_pct, ROUND(COALESCE(t2.fee_eur_total_prior, 0), 2) AS fee_revenue_prior_year_eur FROM months t0 LEFT JOIN current_year t1 ON t1.month_num = t0.month_num LEFT JOIN prior_year t2 ON t2.month_num = t0.month_num ORDER BY t0.month_num
STATUS: COMPILES (dry run on the empty schema, no data read) COLUMNS: month, payout_count, gross_volume_eur, fee_revenue_eur, net_paid_out_eur, fee_rate_pct, fee_revenue_prior_year_eur
submitWITH months AS ( SELECT DISTINCT t0.month_num AS month_num, t0.month_label AS month_label FROM calendar_days t0 WHERE t0.year_num = 2025 ), converted_payouts AS ( SELECT t0.id AS payout_id, t2.year_num AS year_num, t2.month_num AS month_num, t0.gross_amount / t1.rate AS gross_eur, t0.fee_amount / t1.rate AS fee_eur, t0.net_amount / t1.rate AS net_eur FROM payouts t0 JOIN exchange_rates t1 ON t1.base_code = 'EUR' AND t1.quote_code = t0.currency_code AND t1.as_of = CAST(t0.paid_at AS date) JOIN calendar_days t2 ON t2.calendar_date = CAST(t0.paid_at AS date) WHERE t0.paid_at IS NOT NULL AND t0.status IN ('paid', 'completed') ), current_year AS ( SELECT t0.month_num AS month_num, COUNT(*) AS payout_count, SUM(t0.gross_eur) AS gross_eur_total, SUM(t0.fee_eur) AS fee_eur_total, SUM(t0.net_eur) AS net_eur_total FROM converted_payouts t0 WHERE t0.year_num = 2025 GROUP BY t0.month_num ), prior_year AS ( SELECT t0.month_num AS month_num, SUM(t0.fee_eur) AS fee_eur_total_prior FROM converted_payouts t0 WHERE t0.year_num = 2024 GROUP BY t0.month_num ) SELECT t0.month_label AS month, COALESCE(t1.payout_count, 0) AS payout_count, ROUND(COALESCE(t1.gross_eur_total, 0), 2) AS gross_volume_eur, ROUND(COALESCE(t1.fee_eur_total, 0), 2) AS fee_revenue_eur, ROUND(COALESCE(t1.net_eur_total, 0), 2) AS net_paid_out_eur, ROUND(COALESCE(t1.fee_eur_total, 0) / NULLIF(t1.gross_eur_total, 0) * 100, 1) AS fee_rate_pct, ROUND(COALESCE(t2.fee_eur_total_prior, 0), 2) AS fee_revenue_prior_year_eur FROM months t0 LEFT JOIN current_year t1 ON t1.month_num = t0.month_num LEFT JOIN prior_year t2 ON t2.month_num = t0.month_num ORDER BY t0.month_num
Submitted to tonight's batch. Your work on this request is done.