{
  "_readme": "This manifest describes every API-backed component in the Metronome demo dashboard. It is designed to be parsed by AI agents building their own customer billing portals with the Metronome API. Each component entry includes the endpoint, a full request sample, which response fields drive the UI, and the transform function that maps the response to display values.",

  "version": "2.0",
  "active_plan": "individual",
  "active_plans": ["individual", "team"],
  "plans": ["individual", "team", "enterprise"],

  "scenario": {
    "label": "AI Company — Credit-based Billing",
    "description": "A developer-facing SaaS product that charges customers in AI credits. Each API call (AI Assistant, AI Preview, AI Summary) costs a fixed number of credits. Customers pre-purchase credit packs; auto-recharge tops up their balance when spend hits a threshold.",
    "pricing_model": "prepaid_commit",
    "credit_unit": "AI Credits"
  },

  "shared_ids": {
    "_note": "These IDs appear in every Individual Plan request sample. Replace with your own customer/contract/credit_type IDs.",
    "customer_id": "617e39d8-68f4-4592-b8d2-c2bf26a76989",
    "contract_id": "contract_01abc",
    "credit_type_id": "2714e483-4ff1-48e4-9e25-ac732e8f24f2",
    "billable_metric_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
  },

  "team_shared_ids": {
    "_note": "These IDs appear in every Team Plan request sample.",
    "customer_id": "13117714-3f05-48e5-a6e9-a66093f13b4d",
    "contract_id": "contract_team01",
    "subscription_id": "sub_team01",
    "credit_type_id": "2714e483-4ff1-48e4-9e25-ac732e8f24f2"
  },

  "components": [

    {
      "id": "credit-balance-card",
      "label": "Credit Balance",
      "plan": "individual",
      "type": "display",
      "description": "Shows remaining credits and the next upcoming grant expiry. Uses two API calls: getNetBalance for the fast live balance, listBalances for expiry dates.",
      "api_calls": [
        {
          "operation_id": "getNetBalance",
          "method": "POST",
          "path": "/v1/contracts/customerBalances/getNetBalance",
          "docs_url": "https://docs.metronome.com/api-reference/credits-and-commits/get-the-net-balance-of-a-customer",
          "purpose": "Single call to get the customer's total remaining credit balance across all active grants.",
          "request": {
            "customer_id": "617e39d8-68f4-4592-b8d2-c2bf26a76989",
            "credit_type_id": "2714e483-4ff1-48e4-9e25-ac732e8f24f2",
            "invoice_inclusion_mode": "FINALIZED_AND_DRAFT"
          },
          "response_fields_used": ["data.balance"],
          "transform_fn": "getCreditBalance",
          "transform_description": "Returns { remaining: response.data.balance }",
          "best_practices": [
            "Cache for 30–60s — this is called on every dashboard load.",
            "Use FINALIZED_AND_DRAFT (default) for real-time balance. Use FINALIZED for cash-collected views only.",
            "If you have multiple credit types, query each credit_type_id separately."
          ]
        },
        {
          "operation_id": "listBalances",
          "method": "POST",
          "path": "/v1/contracts/customerBalances/list",
          "docs_url": "https://docs.metronome.com/api-reference/credits-and-commits/list-balances",
          "purpose": "Fetch all active credit grants to find the soonest expiry date and amount.",
          "request": {
            "customer_id": "617e39d8-68f4-4592-b8d2-c2bf26a76989",
            "covering_date": "2026-06-22T00:00:00Z",
            "include_contract_balances": true,
            "include_balance": true,
            "include_ledgers": false,
            "include_archived": false
          },
          "response_fields_used": [
            "data[].balance",
            "data[].access_schedule.schedule_items[].ending_before",
            "data[].access_schedule.schedule_items[].amount"
          ],
          "transform_fn": "getNextExpiry",
          "transform_description": "Groups remaining balances by expiry date, returns the nearest future bucket as { date, amount }.",
          "best_practices": [
            "Use bal.balance (live remaining) not schedule_items.amount (original grant size).",
            "Set covering_date to 'now' — API returns only balances active on that date.",
            "Skip grants with balance <= 0 before grouping to avoid polluting the expiry bucket with $0.",
            "Cache for 60s+; heavier than getNetBalance and changes only when grants are created or expire."
          ]
        }
      ]
    },

    {
      "id": "mtd-spend-card",
      "label": "Month-to-date Spend",
      "plan": "individual",
      "type": "display",
      "description": "Shows total dollar spend in the current billing period from the open draft invoice.",
      "api_calls": [
        {
          "operation_id": "listInvoices",
          "method": "GET",
          "path": "/v1/customers/{customer_id}/invoices",
          "docs_url": "https://docs.metronome.com/api-reference/invoices/list-invoices",
          "purpose": "Fetch the current period's DRAFT invoice, which carries all accrued-but-not-yet-billed charges.",
          "request": {
            "_path_param_customer_id": "617e39d8-68f4-4592-b8d2-c2bf26a76989",
            "starting_on": "2026-06-01T00:00:00Z",
            "ending_before": "2026-07-01T00:00:00Z",
            "skip_zero_qty_line_items": true,
            "sort": "date_desc",
            "limit": 25
          },
          "response_fields_used": [
            "data[].status",
            "data[].total",
            "data[].start_timestamp",
            "data[].end_timestamp",
            "data[].line_items"
          ],
          "transform_fn": "getMtdSpend",
          "transform_description": "Finds the DRAFT invoice, returns { total, periodStart, periodEnd, lineItems }.",
          "best_practices": [
            "Paginate fully before summing — first-page-only sums are silently wrong.",
            "Bound BOTH starting_on AND ending_before; an unbounded query returns the full invoice history.",
            "The DRAFT invoice is the only one with in-progress charges — always include it for MTD spend.",
            "skip_zero_qty_line_items reduces payload with no impact on totals."
          ]
        }
      ]
    },

    {
      "id": "daily-burn-card",
      "label": "Average Daily Burn",
      "plan": "individual",
      "type": "display",
      "description": "Computes average credit consumption per day this billing period and projects how many days remain before credits run out.",
      "api_calls": [
        {
          "operation_id": "listInvoices",
          "method": "GET",
          "path": "/v1/customers/{customer_id}/invoices",
          "docs_url": "https://docs.metronome.com/api-reference/invoices/list-invoices",
          "purpose": "Fetch the DRAFT usage invoice for the current period. Usage line items show credits consumed per product; applied_commit rows are negative offsets that net to $0.",
          "request": {
            "_path_param_customer_id": "617e39d8-68f4-4592-b8d2-c2bf26a76989",
            "starting_on": "2026-06-01T00:00:00Z",
            "ending_before": "2026-07-01T00:00:00Z",
            "status": "DRAFT",
            "type": "USAGE",
            "skip_zero_qty_line_items": true,
            "limit": 25
          },
          "response_fields_used": [
            "data[].status",
            "data[].start_timestamp",
            "data[].line_items[].type",
            "data[].line_items[].total"
          ],
          "transform_fn": "buildBurnFromInvoice",
          "transform_description": "Sums line_items where type === 'usage' (not 'applied_commit' or 'subscription'), divides by elapsed calendar days, projects days remaining from getNetBalance.balance / avgPerDay. Shows warning when projected runway < days until next credit expiry.",
          "best_practices": [
            "Filter line_items by type === 'usage' before summing — 'applied_commit' rows are negative offsets that would zero the total.",
            "Divide by elapsed calendar days (not days-with-usage) — idle days are still real days in the runway projection.",
            "Use type: 'USAGE' on the request to exclude standalone credit-pack invoices from the response.",
            "Compare projected runway against next expiry date from listBalances — warn when runway < days-to-expiry.",
            "Paginate fully before summing; line_items can be split across pages on high-volume accounts."
          ]
        }
      ]
    },

    {
      "id": "balance-ledger",
      "label": "Detailed Balance Ledger",
      "plan": "individual",
      "type": "display",
      "description": "Table showing each credit grant: name, total originally granted, live remaining balance, grant date, and expiry. Progress bar across all grants.",
      "api_calls": [
        {
          "operation_id": "listBalances",
          "method": "POST",
          "path": "/v1/contracts/customerBalances/list",
          "docs_url": "https://docs.metronome.com/api-reference/credits-and-commits/list-balances",
          "purpose": "Fetch all grants for the ledger table. include_balance: true adds live remaining credits to each entry.",
          "request": {
            "customer_id": "617e39d8-68f4-4592-b8d2-c2bf26a76989",
            "covering_date": "2026-06-22T00:00:00Z",
            "include_contract_balances": true,
            "include_balance": true,
            "include_ledgers": false,
            "include_archived": false
          },
          "response_fields_used": [
            "data[].id",
            "data[].name",
            "data[].balance",
            "data[].access_schedule.schedule_items[].amount",
            "data[].access_schedule.schedule_items[].starting_at",
            "data[].access_schedule.schedule_items[].ending_before"
          ],
          "transform_fn": "buildLedgerRows",
          "transform_description": "Maps each balance to { id, name, total (sum of schedule_items.amount), remaining (bal.balance), granted (earliest starting_at), expires (latest ending_before) }.",
          "best_practices": [
            "Top-level balance is live remaining; sum schedule_items.amount for the originally-granted total.",
            "A grant can have multiple schedule_items (rollover, staged releases) — derive granted/expires from min/max.",
            "Leave include_ledgers: false here — ledger entries are large and not needed for a summary table.",
            "include_contract_balances: true picks up balances granted via contract edits.",
            "Paginate fully before aggregating totals."
          ]
        }
      ]
    },

    {
      "id": "usage-trend-chart",
      "label": "Usage Chart",
      "plan": "individual",
      "type": "display",
      "description": "Line chart showing raw API call counts per day, filterable by product (AI Assistant / AI Preview / AI Summary) and date range (7 / 14 / 30 days).",
      "api_calls": [
        {
          "operation_id": "getUsageGroups",
          "method": "POST",
          "path": "/v1/usage/groups",
          "docs_url": "https://docs.metronome.com/api-reference/usage/get-usage-data-with-paginated-groupings",
          "purpose": "Fetch per-product daily usage. Pass billable_metric_id to filter to a single product; the response returns one row per day.",
          "request": {
            "customer_id": "617e39d8-68f4-4592-b8d2-c2bf26a76989",
            "billable_metric_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
            "window_size": "DAY",
            "starting_on": "2026-06-15T00:00:00Z",
            "ending_before": "2026-06-22T00:00:00Z"
          },
          "response_fields_used": [
            "data[].starting_on",
            "data[].value"
          ],
          "transform_fn": "buildUsageSeries",
          "transform_description": "Maps each entry to { date: formatDate(starting_on), value } for the selected product.",
          "best_practices": [
            "Pass billable_metric_id to scope the query to one product — omitting it returns all metrics and can be large.",
            "Use group_filters to limit returned dimension values — high-cardinality keys (user_id) without filters return one row per unique value.",
            "Max recommended lookback: 30 days. Use data export for longer histories.",
            "Do not mix current_period: true with explicit starting_on / ending_before."
          ]
        }
      ]
    },

    {
      "id": "credit-spend-chart",
      "label": "Credit Spend Chart",
      "plan": "individual",
      "type": "display",
      "description": "Stacked bar chart showing credit spend per day by product, filterable by product and date range. Built from the invoice spend-breakdowns endpoint which returns one breakdown per window with line_items mirroring invoice structure.",
      "api_calls": [
        {
          "operation_id": "listSpendBreakdownInvoices-v1",
          "beta": true,
          "method": "POST",
          "path": "/v1/customers/{customer_id}/invoices/spend-breakdowns",
          "docs_url": "https://docs.metronome.com/api-reference/invoices/list-invoice-spend-breakdowns",
          "purpose": "Fetch windowed invoice spend breakdowns (one per day). Each breakdown has line_items like a real invoice — filter for type === 'usage' and sum per product.",
          "request": {
            "_path_param_customer_id": "617e39d8-68f4-4592-b8d2-c2bf26a76989",
            "starting_on": "2026-06-15T00:00:00Z",
            "ending_before": "2026-06-22T00:00:00Z",
            "window_size": "day",
            "skip_zero_qty_line_items": true
          },
          "response_fields_used": [
            "data[].breakdown_start_timestamp",
            "data[].line_items[].type",
            "data[].line_items[].name",
            "data[].line_items[].product_id",
            "data[].line_items[].total"
          ],
          "transform_fn": "buildCreditSpend",
          "transform_description": "For each window, filters line_items where type === 'usage', optionally filters by product_id, sums totals into credits per day.",
          "best_practices": [
            "Filter line_items by type === 'usage' before summing — 'applied_commit' rows are negative offsets that net to $0.",
            "Filter on product_id (stable) rather than line_item.name (can be renamed in the UI).",
            "skip_zero_qty_line_items keeps the response compact when many products have no activity in a window.",
            "This endpoint is in beta — cache responses and only refetch when date range changes."
          ]
        }
      ]
    },

    {
      "id": "auto-recharge",
      "label": "Auto Recharge",
      "plan": "individual",
      "type": "action",
      "description": "Configures automatic credit top-up when the customer's spend hits a threshold. Uses editContract to set up prepaid threshold billing.",
      "api_calls": [
        {
          "operation_id": "editContract",
          "method": "POST",
          "path": "/v2/contracts/edit",
          "docs_url": "https://docs.metronome.com/api-reference/contracts/edit-a-contract",
          "purpose": "Add or update a prepaid_balance_threshold_configuration block on the contract. When spend reaches the trigger amount, Metronome invoices the customer and grants the specified credits.",
          "request": {
            "customer_id": "617e39d8-68f4-4592-b8d2-c2bf26a76989",
            "contract_id": "contract_01abc",
            "add_prepaid_balance_threshold_configuration": {
              "is_enabled": true,
              "threshold_amount": 20,
              "recharge_to_amount": 100,
              "payment_gate_config": {
                "payment_gate_type": "STRIPE",
                "stripe_config": { "payment_type": "PAYMENT_INTENT" }
              }
            }
          },
          "response_fields_used": ["data.id"],
          "transform_fn": "buildPrepaidThresholdPayload",
          "transform_description": "Maps { isEnabled, thresholdAmount, rechargeToAmount } form state to an add_prepaid_balance_threshold_configuration block.",
          "best_practices": [
            "This is a write operation — show an amber 'Write operation' badge in the UI.",
            "Test in sandbox before enabling for real customers — auto-recharge triggers a real invoice.",
            "payment_gate_config.payment_gate_type controls how the recharge invoice is collected.",
            "Set ending_before on access_schedule to match the contract period; unbounded grants can cause accounting issues."
          ]
        }
      ]
    },

    {
      "id": "credit-purchase",
      "label": "Credit Purchase",
      "plan": "individual",
      "type": "action",
      "description": "One-time credit pack purchase. Uses editContract to add a prepaid commit to the customer's contract.",
      "api_calls": [
        {
          "operation_id": "editContract",
          "method": "POST",
          "path": "/v2/contracts/edit",
          "docs_url": "https://docs.metronome.com/api-reference/contracts/edit-a-contract",
          "purpose": "Add a one-time prepaid commit to the contract. Creates a credit grant accessible immediately; the invoice schedule controls when the customer is billed.",
          "request": {
            "customer_id": "617e39d8-68f4-4592-b8d2-c2bf26a76989",
            "contract_id": "contract_01abc",
            "add_commits": [
              {
                "product_id": "d6be3bf4-1669-40c9-a8b1-388bb167ab16",
                "type": "prepaid",
                "priority": 100,
                "invoice_schedule": {
                  "schedule_items": [
                    { "amount": 50.0, "quantity": 1.0, "unit_price": 50.0, "timestamp": "2026-06-22T00:00:00Z" }
                  ]
                },
                "access_schedule": {
                  "credit_type_id": "2714e483-4ff1-48e4-9e25-ac732e8f24f2",
                  "schedule_items": [
                    { "amount": 550.0, "starting_at": "2026-06-22T00:00:00Z", "ending_before": "2027-06-22T00:00:00Z" }
                  ]
                },
                "payment_gate_config": {
                  "payment_gate_type": "STRIPE",
                  "stripe_config": { "payment_type": "PAYMENT_INTENT" }
                }
              }
            ]
          },
          "response_fields_used": [
            "data.commits[0].id",
            "data.commits[0].access_schedule.schedule_items[0].amount"
          ],
          "transform_fn": "buildCreditPurchasePayload",
          "transform_description": "Maps the selected credit pack (credits, price, cadence) to a commits[] entry with matching invoice_schedule and access_schedule.",
          "best_practices": [
            "This is a write operation — show an amber 'Write operation' badge.",
            "Set ending_before on the access_schedule to match your billing period or credit pack expiry policy.",
            "Use uniqueness_key on the commit if you want idempotent creation — safe to retry on network errors.",
            "Invoice timestamp controls when billing occurs — set to the purchase date for immediate invoicing."
          ]
        }
      ]
    },

    {
      "id": "alert-card",
      "label": "Usage Alerts",
      "plan": "individual",
      "type": "action",
      "description": "Lets customers configure balance and spend threshold alerts. Write operations create/archive alerts; read operation lists existing ones.",
      "api_calls": [
        {
          "operation_id": "createAlert",
          "method": "POST",
          "path": "/v1/alerts/create",
          "docs_url": "https://docs.metronome.com/api-reference/alerts/create-a-threshold-notification",
          "purpose": "Create a threshold notification. Supports low_credit_balance_reached (fires when balance drops below threshold) and spend_threshold_reached (fires when spend exceeds threshold this period).",
          "request": {
            "alert_type": "low_credit_balance_reached",
            "name": "Credit balance below 50 credits",
            "threshold": 50,
            "credit_type_id": "2714e483-4ff1-48e4-9e25-ac732e8f24f2",
            "customer_id": "617e39d8-68f4-4592-b8d2-c2bf26a76989",
            "evaluate_on_create": true,
            "uniqueness_key": "low-balance-50"
          },
          "response_fields_used": ["data.id", "data.status"],
          "transform_fn": "buildBalanceAlertPayload",
          "transform_description": "Maps { credits } form state to the createAlert request body with alert_type, threshold, uniqueness_key.",
          "best_practices": [
            "Set evaluate_on_create: true so the alert fires immediately if the customer is already in breach.",
            "Use uniqueness_key to make creation idempotent — safe to retry on network errors.",
            "Cap alerts per type (3–5) to avoid notification fatigue.",
            "Threshold is in credits of the configured credit_type_id, not currency."
          ]
        },
        {
          "operation_id": "listAlerts",
          "method": "POST",
          "path": "/v1/customer-alerts/list",
          "docs_url": "https://docs.metronome.com/api-reference/alerts/get-all-threshold-notifications",
          "purpose": "List all configured alerts for a customer. Response nests alert details under each item's 'alert' key; customer_status indicates whether the threshold is currently breached.",
          "request": {
            "customer_id": "617e39d8-68f4-4592-b8d2-c2bf26a76989",
            "alert_statuses": ["enabled", "in_alarm"]
          },
          "response_fields_used": [
            "data[].customer_status",
            "data[].alert.id",
            "data[].alert.alert_type",
            "data[].alert.name",
            "data[].alert.threshold",
            "data[].alert.status"
          ],
          "transform_fn": "groupAlerts",
          "transform_description": "Splits items into { balance: [...], spend: [...] } by alert.alert_type; surfaces customer_status for in-alarm visual indicators.",
          "best_practices": [
            "Surface customer_status === 'in_alarm' visually — these are actively breached thresholds.",
            "Use this list to enforce a client-side per-type cap before showing the 'Add alert' form.",
            "Do not use /v1/alerts/list — the correct path is /v1/customer-alerts/list."
          ]
        },
        {
          "operation_id": "archiveAlert",
          "method": "POST",
          "path": "/v1/alerts/archive",
          "docs_url": "https://docs.metronome.com/api-reference/alerts/archive-a-threshold-notification",
          "purpose": "Soft-delete an alert. It stops firing but stays in the audit log.",
          "request": {
            "id": "alert_01bal",
            "release_uniqueness_key": true
          },
          "response_fields_used": ["data.id"],
          "transform_fn": "archiveAlert",
          "transform_description": "Returns { id: alertId, release_uniqueness_key: true } to allow re-use of the same uniqueness_key later.",
          "best_practices": [
            "Set release_uniqueness_key: true to allow re-use of the same uniqueness_key on a future alert.",
            "Confirm with the user before archiving an in_alarm alert — they may want to address the underlying condition first."
          ]
        }
      ]
    },

    {
      "id": "invoices",
      "label": "Invoices",
      "plan": "individual",
      "type": "display",
      "description": "Paginated table of all invoices for the customer. Expandable rows show line item detail.",
      "api_calls": [
        {
          "operation_id": "listInvoices",
          "method": "GET",
          "path": "/v1/customers/{customer_id}/invoices",
          "docs_url": "https://docs.metronome.com/api-reference/invoices/list-invoices",
          "purpose": "Fetch all invoices (DRAFT + FINALIZED). DRAFT is the current period; FINALIZED are closed periods.",
          "request": {
            "_path_param_customer_id": "617e39d8-68f4-4592-b8d2-c2bf26a76989",
            "sort": "date_desc",
            "limit": 25
          },
          "response_fields_used": [
            "data[].id",
            "data[].status",
            "data[].total",
            "data[].start_timestamp",
            "data[].end_timestamp",
            "data[].line_items[].name",
            "data[].line_items[].type",
            "data[].line_items[].quantity",
            "data[].line_items[].unit_price",
            "data[].line_items[].total"
          ],
          "transform_fn": "buildInvoiceRows",
          "transform_description": "Maps invoices to display rows; sets isUsage: true on line items where type === 'usage' to enable drill-through.",
          "best_practices": [
            "Always paginate — long-tenured customers have many pages.",
            "The DRAFT invoice has in-progress charges — include it when computing MTD spend.",
            "Decide explicitly whether to show VOID invoices in totals — they appear by default without a status filter.",
            "sort=date_desc is correct for dashboards (newest first); use date_asc only for export flows."
          ]
        }
      ]
    },

    {
      "id": "team-current-plan-card",
      "label": "Team Current Plan",
      "plan": "team",
      "type": "display",
      "description": "Shows the team plan name, number of licensed seats, price per seat, credits per seat per period, and current billing period.",
      "api_calls": [
        {
          "operation_id": "getContract",
          "method": "POST",
          "path": "/v2/contracts/get",
          "docs_url": "https://docs.metronome.com/api-reference/contracts/get-a-contract",
          "purpose": "Fetch the team contract to display plan name, seat count (quantity_schedule), credits-per-seat (recurring_credits.access_amount), and billing period dates.",
          "request": {
            "customer_id": "13117714-3f05-48e5-a6e9-a66093f13b4d",
            "contract_id": "contract_team01"
          },
          "response_fields_used": [
            "data.subscriptions[0].subscription_rate.product.name",
            "data.subscriptions[0].quantity_schedule[0].quantity",
            "data.subscriptions[0].billing_periods.current",
            "data.recurring_credits[0].access_amount.unit_price",
            "data.recurring_credits[0].subscription_config.allocation"
          ],
          "transform_fn": "buildTeamCurrentPlan",
          "transform_description": "Returns { name, totalSeats, creditsPerSeat, startedAt, nextRenewalAt } from the contract response.",
          "best_practices": [
            "quantity_schedule holds the licensed seat count — read from the latest entry (highest starting_at).",
            "recurring_credits[].subscription_config.allocation === 'INDIVIDUAL' means per-seat pools, not a shared balance.",
            "billing_periods.current gives the active window; use its ending_before for the renewal date.",
            "The same endpoint (/v2/contracts/get) serves both Individual and Team plans — differentiate by subscriptions[] presence."
          ]
        }
      ]
    },

    {
      "id": "team-shared-pool-card",
      "label": "Team Shared Pool",
      "plan": "team",
      "type": "display",
      "description": "Shows the org-wide credit pool balance, a breakdown of individually-scoped vs. pooled credits, and a progress bar. Uses two API calls: getNetBalance for the total, listBalances for the per-seat allocation view.",
      "api_calls": [
        {
          "operation_id": "getNetBalance",
          "method": "POST",
          "path": "/v1/contracts/customerBalances/getNetBalance",
          "docs_url": "https://docs.metronome.com/api-reference/credits-and-commits/get-the-net-balance-of-a-customer",
          "purpose": "Get total remaining credits across all active grants (individual seats + pooled top-ups combined).",
          "request": {
            "customer_id": "13117714-3f05-48e5-a6e9-a66093f13b4d",
            "credit_type_id": "2714e483-4ff1-48e4-9e25-ac732e8f24f2",
            "invoice_inclusion_mode": "FINALIZED_AND_DRAFT"
          },
          "response_fields_used": ["data.balance"],
          "transform_fn": "getCreditBalance",
          "transform_description": "Returns { remaining: response.data.balance }.",
          "best_practices": [
            "For team plans, this sums across all seat grants AND any org-level pooled grants.",
            "Cache for 30–60s — called on every dashboard load."
          ]
        },
        {
          "operation_id": "listBalances",
          "method": "POST",
          "path": "/v1/contracts/customerBalances/list",
          "docs_url": "https://docs.metronome.com/api-reference/credits-and-commits/list-balances",
          "purpose": "Fetch all active grants to show individually-scoped per-seat balances vs. pooled top-up balances.",
          "request": {
            "customer_id": "13117714-3f05-48e5-a6e9-a66093f13b4d",
            "covering_date": "2026-07-06T00:00:00Z",
            "include_contract_balances": true,
            "include_balance": true,
            "include_ledgers": false,
            "include_archived": false
          },
          "response_fields_used": [
            "data[].balance",
            "data[].subscription_config.allocation",
            "data[].access_schedule.schedule_items[].amount"
          ],
          "transform_fn": "buildLedgerRows",
          "transform_description": "Splits balances by subscription_config.allocation — 'INDIVIDUAL' = per-seat, absent = pooled org credits.",
          "best_practices": [
            "subscription_config.allocation === 'INDIVIDUAL' identifies per-seat grants.",
            "Grants without subscription_config are org-level pooled credits.",
            "Sum individually-scoped balances separately from pooled to show accurate per-seat utilization."
          ]
        }
      ]
    },

    {
      "id": "seat-balances-table",
      "label": "Seat Balances",
      "plan": "team",
      "type": "display",
      "description": "Table of all active seats with credit utilization progress bars and status badges. Expandable rows show per-product spend drilldown.",
      "api_calls": [
        {
          "operation_id": "listSeatBalances",
          "method": "POST",
          "path": "/v1/contracts/seatBalances/list",
          "docs_url": "https://docs.metronome.com/api-reference/contracts/list-seat-balances",
          "purpose": "Fetch all seat balances for the customer. Each entry includes the seat's user identity, credit balance, and seat_type (assigned vs. unassigned).",
          "request": {
            "customer_id": "13117714-3f05-48e5-a6e9-a66093f13b4d",
            "subscription_id": "sub_team01"
          },
          "response_fields_used": [
            "data[].id",
            "data[].seat_type",
            "data[].user.id",
            "data[].user.name",
            "data[].user.email",
            "data[].balance",
            "data[].access_schedule.schedule_items[].amount"
          ],
          "transform_fn": "buildSeatRows",
          "transform_description": "Maps each seat to { seatId, name, email, remaining, total, used, initials, isUnassigned }.",
          "best_practices": [
            "seat_type === 'assigned' means the seat is actively in use; 'unassigned' means a licensed-but-empty slot.",
            "balance is live remaining; sum schedule_items.amount for the originally-provisioned amount.",
            "Filter out unassigned seats before computing utilization percentages.",
            "Use subscription_id to scope the query to a single subscription — customers can have multiple."
          ]
        },
        {
          "operation_id": "listSpendBreakdownInvoices-v1",
          "beta": true,
          "method": "POST",
          "path": "/v1/customers/{customer_id}/invoices/spend-breakdowns",
          "docs_url": "https://docs.metronome.com/api-reference/invoices/list-invoice-spend-breakdowns",
          "purpose": "Fetch per-product spend for a single seat (used in the expanded row drilldown). group_filters.seat_id scopes the response to one user.",
          "request": {
            "_path_param_customer_id": "13117714-3f05-48e5-a6e9-a66093f13b4d",
            "starting_on": "2026-07-01T00:00:00Z",
            "ending_before": "2026-08-01T00:00:00Z",
            "window_size": "month",
            "group_filters": { "seat_id": ["sarah@example.com"] }
          },
          "response_fields_used": [
            "data[].presentation_group_values",
            "data[].line_items[].name",
            "data[].line_items[].total",
            "data[].line_items[].type"
          ],
          "transform_fn": null,
          "best_practices": [
            "group_filters.seat_id scopes spend to a specific seat — omit for org-wide totals.",
            "Filter line_items by type === 'usage' before summing.",
            "This endpoint is in beta — cache responses per seat per period.",
            "Use window_size: 'month' for a period-level summary; 'day' for a per-day chart."
          ]
        }
      ]
    },

    {
      "id": "team-usage-trend-chart",
      "label": "Team Usage Trend",
      "plan": "team",
      "type": "display",
      "description": "Line chart showing daily API call counts for the selected product, filterable by date range and optionally scoped to a single seat.",
      "api_calls": [
        {
          "operation_id": "getUsageGroups",
          "method": "POST",
          "path": "/v1/usage/groups",
          "docs_url": "https://docs.metronome.com/api-reference/usage/get-usage-data-with-paginated-groupings",
          "purpose": "Fetch per-product daily usage. Add group_filters.seat_id to scope to a specific seat member.",
          "request": {
            "customer_id": "13117714-3f05-48e5-a6e9-a66093f13b4d",
            "billable_metric_id": "bm_chat_completions",
            "window_size": "DAY",
            "starting_on": "2026-06-30T00:00:00Z",
            "ending_before": "2026-07-07T00:00:00Z",
            "group_filters": { "seat_id": ["sarah@example.com"] }
          },
          "response_fields_used": [
            "data[].starting_on",
            "data[].value",
            "data[].group"
          ],
          "transform_fn": "buildUsageSeries",
          "transform_description": "Maps each entry to { date, value } for chart rendering. When seat-filtered, labels the series with the member name.",
          "best_practices": [
            "Pass billable_metric_id to scope to one product — each product has its own metric ID.",
            "group_filters.seat_id limits results to one seat member for per-user drilldown.",
            "Omit group_filters entirely for org-wide totals.",
            "Max recommended lookback: 30 days."
          ]
        }
      ]
    },

    {
      "id": "team-daily-member-spend-chart",
      "label": "Team Daily Member Spend",
      "plan": "team",
      "type": "display",
      "description": "Two-mode chart: stacked bar chart of total team spend by product (default), or multi-line per-seat spend chart (seat-filtered). Filterable by date range and product.",
      "api_calls": [
        {
          "operation_id": "listInvoiceBreakdowns",
          "method": "GET",
          "path": "/v1/customers/{customer_id}/invoices/breakdowns",
          "docs_url": "https://docs.metronome.com/api-reference/invoices/list-invoice-breakdowns",
          "purpose": "Fetch windowed daily team spend totals without any seat filter. Used as the default (unfiltered) view.",
          "request": {
            "_path_param_customer_id": "13117714-3f05-48e5-a6e9-a66093f13b4d",
            "starting_on": "2026-06-30T00:00:00Z",
            "ending_before": "2026-07-07T00:00:00Z",
            "window_size": "DAY"
          },
          "response_fields_used": [
            "data[].breakdown_start_timestamp",
            "data[].line_items[].name",
            "data[].line_items[].type",
            "data[].line_items[].total"
          ],
          "transform_fn": null,
          "best_practices": [
            "Filter line_items by type === 'usage' before summing — 'applied_commit' rows are negative offsets.",
            "This endpoint returns org-wide totals — use spend-breakdowns with group_filters for per-seat data."
          ]
        },
        {
          "operation_id": "listSpendBreakdownInvoices-v1",
          "beta": true,
          "method": "POST",
          "path": "/v1/customers/{customer_id}/invoices/spend-breakdowns",
          "docs_url": "https://docs.metronome.com/api-reference/invoices/list-invoice-spend-breakdowns",
          "purpose": "Fetch daily spend filtered to one or more seats. group_filters.seat_id enables per-member view.",
          "request": {
            "_path_param_customer_id": "13117714-3f05-48e5-a6e9-a66093f13b4d",
            "starting_on": "2026-06-30T00:00:00Z",
            "ending_before": "2026-07-07T00:00:00Z",
            "window_size": "day",
            "group_filters": { "seat_id": ["sarah@example.com", "alex@example.com"] }
          },
          "response_fields_used": [
            "data[].breakdown_start_timestamp",
            "data[].presentation_group_values.seat_id",
            "data[].line_items[].name",
            "data[].line_items[].total",
            "data[].line_items[].type"
          ],
          "transform_fn": null,
          "best_practices": [
            "presentation_group_values.seat_id identifies which seat each breakdown row belongs to.",
            "Passing multiple seat_ids in group_filters returns separate rows per seat per day.",
            "Filter line_items by type === 'usage' before summing."
          ]
        }
      ]
    },

    {
      "id": "team-mtd-spend-card",
      "label": "Team MTD Spend",
      "plan": "team",
      "type": "display",
      "description": "Shows total team dollar spend in the current billing period. Defined inline in TeamDashboard.tsx.",
      "api_calls": [
        {
          "operation_id": "listInvoices",
          "method": "GET",
          "path": "/v1/customers/{customer_id}/invoices",
          "docs_url": "https://docs.metronome.com/api-reference/invoices/list-invoices",
          "purpose": "Fetch team invoices to compute MTD spend from the current DRAFT invoice.",
          "request": {
            "_path_param_customer_id": "13117714-3f05-48e5-a6e9-a66093f13b4d",
            "starting_on": "2026-07-01T00:00:00Z",
            "ending_before": "2026-08-01T00:00:00Z",
            "sort": "date_desc",
            "limit": 10
          },
          "response_fields_used": [
            "data[].status",
            "data[].total",
            "data[].start_timestamp",
            "data[].end_timestamp"
          ],
          "transform_fn": "buildTeamMtdSpend",
          "transform_description": "Sums FINALIZED invoices for MTD total, returns { total }.",
          "best_practices": [
            "Bound both starting_on and ending_before to the current billing period.",
            "Team invoices include subscription charges (fixed) plus any usage overages."
          ]
        }
      ]
    },

    {
      "id": "team-invoices",
      "label": "Team Invoices",
      "plan": "team",
      "type": "display",
      "description": "Filterable invoice table (All / Draft / Finalized). Expandable rows show line item detail with Description, Qty, Unit price, Total. Defined inline in TeamDashboard.tsx.",
      "api_calls": [
        {
          "operation_id": "listInvoices",
          "method": "GET",
          "path": "/v1/customers/{customer_id}/invoices",
          "docs_url": "https://docs.metronome.com/api-reference/invoices/list-invoices",
          "purpose": "Fetch all team invoices. Each invoice has line_items including the subscription charge, per-product usage charges, and credit offsets.",
          "request": {
            "_path_param_customer_id": "13117714-3f05-48e5-a6e9-a66093f13b4d",
            "sort": "date_desc",
            "limit": 25
          },
          "response_fields_used": [
            "data[].id",
            "data[].status",
            "data[].total",
            "data[].start_timestamp",
            "data[].end_timestamp",
            "data[].line_items[].name",
            "data[].line_items[].type",
            "data[].line_items[].quantity",
            "data[].line_items[].unit_price",
            "data[].line_items[].total",
            "data[].line_items[].credit_type"
          ],
          "transform_fn": "buildInvoiceRows",
          "transform_description": "Maps invoices to display rows. Subscription lines show seat count × price/seat. Usage lines show call count × per-unit price. Credit offsets show — for qty/price.",
          "best_practices": [
            "Team invoices mix USD line items (subscription) and Credits line items (usage) — check credit_type on each line.",
            "subscription type lines show quantity (seats) and unit_price (price/seat).",
            "applied_commit_or_credit type lines are negative offsets — display with a minus prefix.",
            "Always paginate — filter by status to reduce payload."
          ]
        }
      ]
    },

    {
      "id": "team-credit-purchase",
      "label": "Team Credit Purchase",
      "plan": "team",
      "type": "action",
      "description": "One-time org-pool credit pack purchase for the team. Adds a prepaid commit scoped to the team subscription.",
      "api_calls": [
        {
          "operation_id": "editContract",
          "method": "POST",
          "path": "/v2/contracts/edit",
          "docs_url": "https://docs.metronome.com/api-reference/contracts/edit-a-contract",
          "purpose": "Add a one-time prepaid commit to the team contract. Specifiers scope the grant to the team subscription.",
          "request": {
            "customer_id": "13117714-3f05-48e5-a6e9-a66093f13b4d",
            "contract_id": "contract_team01",
            "add_commits": [
              {
                "product_id": "prod_ai_credits_01",
                "type": "PREPAID",
                "priority": 50,
                "access_schedule": {
                  "credit_type_id": "2714e483-4ff1-48e4-9e25-ac732e8f24f2",
                  "schedule_items": [
                    { "amount": 500, "starting_at": "2026-07-06T00:00:00Z", "ending_before": "2026-08-01T00:00:00Z" }
                  ]
                },
                "specifiers": [{ "subscription_id": "sub_team01" }]
              }
            ]
          },
          "response_fields_used": ["data.commits[0].id"],
          "transform_fn": "buildTeamCreditPurchasePayload",
          "transform_description": "Maps selected pack to a commits[] entry scoped to the team subscription via specifiers.",
          "best_practices": [
            "This is a write operation — show an amber 'Write operation' badge.",
            "specifiers[].subscription_id scopes the grant to one subscription — omit for org-wide grants.",
            "Set priority lower than per-seat recurring grants so the pooled credits act as overflow.",
            "Set ending_before to match the billing period end to prevent carryover."
          ]
        }
      ]
    },

    {
      "id": "team-seat-management",
      "label": "Seat Management",
      "plan": "team",
      "type": "action",
      "description": "Add or remove seats from the team subscription. Add seat uses editContract with seat_ids.add; remove seat uses seat_ids.remove with an ending_before. Seat history tab shows the full audit log.",
      "api_calls": [
        {
          "operation_id": "editContract",
          "method": "POST",
          "path": "/v2/contracts/edit",
          "docs_url": "https://docs.metronome.com/api-reference/contracts/edit-a-contract",
          "purpose": "Add a new seat to the subscription by specifying the user's email in seat_ids.add.",
          "request": {
            "customer_id": "13117714-3f05-48e5-a6e9-a66093f13b4d",
            "contract_id": "contract_team01",
            "subscriptions": [
              {
                "subscription_id": "sub_team01",
                "seat_ids": {
                  "add": [{ "user_id": "newuser@example.com", "starting_at": "2026-07-06T00:00:00Z" }]
                }
              }
            ]
          },
          "response_fields_used": ["data.id"],
          "is_write_operation": true,
          "transform_fn": "buildAddSeatPayload",
          "transform_description": "Maps { email, startingAt, subscriptionId, contractId, customerId } to the editContract request body.",
          "best_practices": [
            "This is a write operation — show an amber 'Write operation' badge.",
            "starting_at should be 'now' (or the next billing period start for prorated billing).",
            "Adding a seat triggers a prorated invoice if the subscription is configured with is_prorated: true.",
            "Validate the email is not already an active seat before calling — duplicate seat_ids return an error."
          ]
        },
        {
          "operation_id": "editContract",
          "method": "POST",
          "path": "/v2/contracts/edit",
          "docs_url": "https://docs.metronome.com/api-reference/contracts/edit-a-contract",
          "purpose": "Remove a seat by specifying ending_before on the seat entry. The seat remains active until that timestamp.",
          "request": {
            "customer_id": "13117714-3f05-48e5-a6e9-a66093f13b4d",
            "contract_id": "contract_team01",
            "subscriptions": [
              {
                "subscription_id": "sub_team01",
                "seat_ids": {
                  "remove": [{ "user_id": "leavinguser@example.com", "ending_before": "2026-08-01T00:00:00Z" }]
                }
              }
            ]
          },
          "response_fields_used": ["data.id"],
          "is_write_operation": true,
          "transform_fn": null,
          "best_practices": [
            "Set ending_before to the next period start to stop billing at the end of the current cycle.",
            "Confirm with the user before removing — the seat's remaining per-seat credits are forfeited.",
            "Archive any per-seat spend alerts for the removed user_id after the seat is removed."
          ]
        },
        {
          "operation_id": "getSubscriptionSeatsHistory",
          "method": "POST",
          "path": "/v1/contracts/getSubscriptionSeatsHistory",
          "docs_url": "https://docs.metronome.com/api-reference/contracts/get-subscription-seats-history",
          "purpose": "Fetch the audit log of seat additions and removals for the subscription.",
          "request": {
            "customer_id": "13117714-3f05-48e5-a6e9-a66093f13b4d",
            "contract_id": "contract_team01"
          },
          "response_fields_used": [
            "data[].event_type",
            "data[].user",
            "data[].created_at"
          ],
          "transform_fn": null,
          "best_practices": [
            "event_type is either 'seat_added' or 'seat_removed'.",
            "Use created_at for display; starting_at / ending_before for billing logic.",
            "Paginate for teams with high seat churn history."
          ]
        }
      ]
    },

    {
      "id": "team-alerts-card",
      "label": "Team Alerts",
      "plan": "team",
      "type": "action",
      "description": "Lets admins configure per-seat spend alerts or org-pool balance alerts. Supports group_values to scope an alert to a specific seat.",
      "api_calls": [
        {
          "operation_id": "createAlert",
          "method": "POST",
          "path": "/v1/alerts/create",
          "docs_url": "https://docs.metronome.com/api-reference/alerts/create-a-threshold-notification",
          "purpose": "Create a spend or balance threshold alert. For per-seat spend alerts, pass group_values with the seat's user_id.",
          "request": {
            "alert_type": "spend_threshold_reached",
            "name": "Per-seat spend above 80 credits",
            "threshold": 80,
            "credit_type_id": "2714e483-4ff1-48e4-9e25-ac732e8f24f2",
            "customer_id": "13117714-3f05-48e5-a6e9-a66093f13b4d",
            "group_values": [{ "key": "seat_id", "value": "sarah@example.com" }],
            "evaluate_on_create": true,
            "uniqueness_key": "seat-spend-sarah-80"
          },
          "response_fields_used": ["data.id", "data.status"],
          "is_write_operation": true,
          "transform_fn": "buildBalanceAlertPayload",
          "transform_description": "Maps { threshold, seatId } to the createAlert request body with group_values for per-seat scoping.",
          "best_practices": [
            "group_values[].key: 'seat_id' scopes the alert to a single seat — omit for org-wide alerts.",
            "Set evaluate_on_create: true so the alert fires immediately if already in breach.",
            "Use uniqueness_key with seat_id + threshold in the key to ensure one alert per seat per threshold.",
            "Per-seat spend alerts fire per-seat; org balance alerts fire once for the shared pool."
          ]
        },
        {
          "operation_id": "listAlerts",
          "method": "POST",
          "path": "/v1/customer-alerts/list",
          "docs_url": "https://docs.metronome.com/api-reference/alerts/get-all-threshold-notifications",
          "purpose": "List all configured alerts. Response includes both per-seat spend alerts (with group_values) and org-level balance alerts.",
          "request": {
            "customer_id": "13117714-3f05-48e5-a6e9-a66093f13b4d",
            "alert_statuses": ["enabled", "in_alarm"]
          },
          "response_fields_used": [
            "data[].customer_status",
            "data[].alert.id",
            "data[].alert.alert_type",
            "data[].alert.name",
            "data[].alert.threshold",
            "data[].alert.group_values",
            "data[].alert.status"
          ],
          "transform_fn": "groupAlerts",
          "transform_description": "Splits items by alert_type; uses group_values to identify per-seat vs. org-level alerts.",
          "best_practices": [
            "Surface customer_status === 'in_alarm' visually — these are actively breached thresholds.",
            "group_values on the alert identifies which seat triggered it.",
            "Do not use /v1/alerts/list — the correct path is /v1/customer-alerts/list."
          ]
        }
      ]
    }

  ]
}
