Billing detail

Overview

Get the full billing picture: every account with its plan, charges, usage, and overage.

Use this for a billing page or reconciliation.

You'll need a partner token.

Returns the itemized data.

Prerequisites

  • A Partner bearer token. See Authentication.
  • Optional: a year and month. Leave them out for the current month.

Base URL

EnvironmentURL
Productionhttps://production-api.shoutaboutus.com
Developmenthttps://development-api.shoutaboutus.com

Endpoint

GET /api/v1/partner/billing/dashboard

Authentication

Partner token

  • Only partner and sub-partner users can call it. Any other token gets 403 (Authenticated user is not associated with an partner.).
  • It always covers your own partner: the accounts directly under it. There is no id to send.

Rate limit

120 requests per minute, counted per user (the token’s user). See Rate limits.

Request parameters

ParameterInTypeRequiredDescription
yearqueryintegerNoBilling year, 2000 to 2100. Defaults to the current year.
monthqueryintegerNoBilling month, 1 to 12. Defaults to the current month.

Request body

None.

Request payload

None.

Response body

The payload is nested one level deeper than usual: it sits in data.data. It has the same fields as Billing summary, with more detail on each account, plus summary, overage_breakdown, allocation_status and context.

FieldTypeDescription
data.dataobjectThe billing dashboard.
data.data.total_billednumberTotal charged to your accounts for the period, to 2 decimals.
data.data.periodstringThe billing period, as YYYY-MM.
data.data.billing_modelstring or nullYour partner's billing model: plan-based or usage-based. null when not set.
data.data.contract_modelstring or nullYour partner's contract model: model-a (overage is charged) or model-b (a flat rate per location, hard stop, no overage). null when not set.
data.data.active_storesintegerActive and pending locations across your accounts.
data.data.accountsarray of objectsOne row per account directly under your partner.
data.data.accounts[].account_idintegerThe account's id.
data.data.accounts[].account_namestringThe account's name.
data.data.accounts[].plan_namestring or nullName of the account's latest plan, or null when it has none.
data.data.accounts[].store_countintegerThe account's active and pending locations.
data.data.accounts[].costnumberCharges for the period, excluding overage, to 2 decimals.
data.data.accounts[].overagenumberOverage charges for the period, to 2 decimals.
data.data.accounts[].statusstringhalted when a location hit a hard limit in the period, overage when there are overage charges, otherwise within_limits.
data.data.accounts[].plan_subtitlestringA one-line label for the plan: plan name, location count and how limits apply, e.g. Growth · 2 loc · Per-location cap.
data.data.accounts[].enforcement_modestringHow limits apply: per-location (each location has its own limit) or aggregate-pool (the account shares one pool).
data.data.accounts[].total_costnumbercost plus overage, to 2 decimals.
data.data.accounts[].allocation_notestringA short note on usage against the allocation, e.g. within allocation, hard stop active or ↑ $0.44 overage (Response posting). For model-b, the same text as billing_formula.
data.data.accounts[].allocation_statestringUsage against the allocation: within_allocation, overage or halted.
data.data.accounts[].billing_formulastring or nullOnly for model-b: how the charge is worked out, e.g. 2 × $20.00 · no overage, with (+ proration) when there is a proration charge. null otherwise.
data.data.summaryobjectHeadline numbers for the period.
data.data.summary.total_billednumberTotal charged for the period, to 2 decimals.
data.data.summary.total_billed_previous_monthnumberTotal charged for the month before, to 2 decimals.
data.data.summary.total_billed_change_pctnumber or nullChange from the month before, as a percentage to 1 decimal. null when the month before had no charges.
data.data.summary.total_billed_caption_model_bstring or nullOnly for model-b: how the total is worked out, e.g. 2 loc × $20.00 / loc. null otherwise.
data.data.summary.active_accountsintegerAccounts directly under your partner.
data.data.summary.active_storesintegerActive and pending locations across those accounts.
data.data.summary.total_overagenumberTotal overage charges, to 2 decimals.
data.data.summary.accounts_with_overageintegerAccounts with any overage charge.
data.data.summary.at_limit_locationsintegerLocations that hit a hard limit in the period.
data.data.summary.halted_locationsintegerSame as at_limit_locations.
data.data.overage_breakdownarray of objectsOverage charges by account and feature. Empty when there are none.
data.data.overage_breakdown[].account_idintegerThe account's id.
data.data.overage_breakdown[].account_namestringThe account's name.
data.data.overage_breakdown[].featuresarray of objectsOne entry per feature with overage, by feature name.
data.data.overage_breakdown[].features[].feature_namestringThe billed feature, e.g. response_posting.
data.data.overage_breakdown[].features[].feature_labelstringDisplay name of the feature, e.g. Response posting.
data.data.overage_breakdown[].features[].overage_amountnumberOverage charged for this feature, to 2 decimals.
data.data.overage_breakdown[].features[].units_overintegerUnits used above the limit.
data.data.overage_breakdown[].features[].usage_totalintegerUnits used across the account's locations in the period.
data.data.overage_breakdown[].features[].ratenumberPrice per unit over the limit, to 4 decimals.
data.data.overage_breakdown[].features[].unit_price_labelstringThe rate as a label, e.g. $0.04/unit.
data.data.allocation_statusarray of objectsOnly for model-b: usage against the allocation, one row per account and feature with a limit. Empty for model-a.
data.data.allocation_status[].account_idintegerThe account's id.
data.data.allocation_status[].account_namestringThe account's name.
data.data.allocation_status[].feature_namestringThe feature.
data.data.allocation_status[].feature_labelstringDisplay name of the feature, prefixed with Pool for an aggregate pool.
data.data.allocation_status[].row_titlestringA label for the row: account name and feature, with the number of locations at their limit when there are any.
data.data.allocation_status[].usedintegerUnits used across the account's locations in the period.
data.data.allocation_status[].allocationintegerUnits allowed: the per-location limit times the number of locations.
data.data.allocation_status[].percentagenumber or nullused as a percentage of allocation, to 1 decimal. null when the allocation is 0.
data.data.allocation_status[].statusstringhalted at or over the allocation, warning from 75%, otherwise on_track.
data.data.allocation_status[].enforcement_modestringper-location or aggregate-pool.
data.data.allocation_status[].locations_at_limitintegerLocations that reached their own limit. Always 0 for an aggregate pool.
data.data.contextobjectLabels for the page header.
data.data.context.headlinestringA heading for the billing setup, e.g. Usage-Based — Model A — Per-Unit Charges. Billing when the models aren't set.
data.data.context.billing_modelstring or nullYour partner's billing model: plan-based or usage-based. null when not set.
data.data.context.contract_modelstring or nullYour partner's contract model: model-a (overage is charged) or model-b (a flat rate per location, hard stop, no overage). null when not set.

Response payload

{
  "data": {
    "data": {
      "total_billed": 40.44,
      "period": "2026-09",
      "billing_model": "usage-based",
      "contract_model": "model-a",
      "active_stores": 2,
      "accounts": [
        {
          "account_id": 5994,
          "account_name": "Acme Coffee",
          "plan_name": "Growth",
          "store_count": 2,
          "cost": 40.0,
          "overage": 0.44,
          "status": "overage",
          "plan_subtitle": "Growth · 2 loc · Per-location cap",
          "enforcement_mode": "per-location",
          "total_cost": 40.44,
          "allocation_note": "↑ $0.44 overage (Response posting)",
          "allocation_state": "overage",
          "billing_formula": null
        }
      ],
      "summary": {
        "total_billed": 40.44,
        "total_billed_previous_month": 40.0,
        "total_billed_change_pct": 1.1,
        "total_billed_caption_model_b": null,
        "active_accounts": 1,
        "active_stores": 2,
        "total_overage": 0.44,
        "accounts_with_overage": 1,
        "at_limit_locations": 0,
        "halted_locations": 0
      },
      "overage_breakdown": [
        {
          "account_id": 5994,
          "account_name": "Acme Coffee",
          "features": [
            {
              "feature_name": "response_posting",
              "feature_label": "Response posting",
              "overage_amount": 0.44,
              "units_over": 11,
              "usage_total": 61,
              "rate": 0.04,
              "unit_price_label": "$0.04/unit"
            }
          ]
        }
      ],
      "allocation_status": [],
      "context": {
        "headline": "Usage-Based — Model A — Per-Unit Charges",
        "billing_model": "usage-based",
        "contract_model": "model-a"
      }
    }
  }
}

Examples

Success payload

See Response payload.

Error payload

403 Not a partner

{
  "message": "Forbidden",
  "status": "error",
  "errors": "Authenticated user is not associated with an partner."
}

422 Invalid period

{
  "message": "The year field must be at least 2000. (and 1 more error)",
  "errors": {
    "year": [
      "The year field must be at least 2000."
    ],
    "month": [
      "The month field must not be greater than 12."
    ]
  }
}

429 Too many requests

{
  "message": "Too Many Attempts."
}
StatusMeaning
200The billing dashboard.
401The token is missing, expired or invalid.
403The caller isn't a partner or sub-partner user.
422year is outside 2000 to 2100, or month is outside 1 to 12.
429More than 120 requests in a minute from this user.
500Unexpected server error.

Example request

curl --request GET \
  --url "https://production-api.shoutaboutus.com/api/v1/partner/billing/dashboard?year=2026&month=9" \
  --header 'Authorization: Bearer <bearer-token>' \
  --header 'Accept: application/json'

Did this page help you?