Billing for one account

Overview

Get an account's charges for the period, by location.

Use this to explain a customer's bill.

You'll need the account's id.

Returns the account's billing detail.

Prerequisites

  • A Partner bearer token. See Authentication.
  • The account's id (brandId). The account must be under your own partner.
  • Optional: a year and month (default: the current month), and a feature to show one feature only.

Base URL

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

Endpoint

GET /api/v1/partner/billing/account/{brandId}

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.).
  • The account must be under your own partner. A sub-partner can open its own accounts, but not its parent partner's or a sibling's (403).
  • An id that matches no account returns 404.

Rate limit

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

Request parameters

ParameterInTypeRequiredDescription
brandIdpathintegerYesThe account's id. Digits only.
yearqueryintegerNoBilling year, 2000 to 2100. Defaults to the current year.
monthqueryintegerNoBilling month, 1 to 12. Defaults to the current month.
featurequerystringNoOne feature to show, e.g. response_posting (a value from feature_options). Send all or leave it out for every feature. A feature that isn't enabled on the account's plan returns 422. Max 128 characters.

Request body

None.

Request payload

None.

Response body

The payload is nested one level deeper than usual: it sits in data.data. Location rows differ by contract model: model-b locations add flat_charge and features_enabled, and their feature rows carry no overage.

FieldTypeDescription
data.dataobjectThe account's billing detail.
data.data.account_idintegerThe account's id.
data.data.account_namestring or nullThe account's name.
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.plan_subtitlestring or nullA one-line label for the account's plan: plan name, location count and how limits apply. null when the account has no plan.
data.data.feature_optionsarray of objectsChoices for a feature filter: all first, then each feature enabled on the plan. Use a value as feature.
data.data.feature_options[].valuestringThe value to send as feature.
data.data.feature_options[].labelstringDisplay name.
data.data.per_location_drilldownarray of objectsThe same feature rows as stores, flattened: one row per location and feature.
data.data.per_location_drilldown[].store_idintegerThe location's id.
data.data.per_location_drilldown[].store_namestringThe location's name.
data.data.per_location_drilldown[].feature_namestringSame as in stores[].features[].
data.data.per_location_drilldown[].feature_labelstringSame as in stores[].features[].
data.data.per_location_drilldown[].units_consumedintegerSame as in stores[].features[].
data.data.per_location_drilldown[].limitinteger or nullSame as in stores[].features[].
data.data.per_location_drilldown[].units_overintegerSame as in stores[].features[].
data.data.per_location_drilldown[].overage_ratenumber or nullSame as in stores[].features[].
data.data.per_location_drilldown[].hard_stopbooleanSame as in stores[].features[].
data.data.per_location_drilldown[].costnumberSame as in stores[].features[].
data.data.per_location_drilldown[].overage_costnumberSame as in stores[].features[].
data.data.per_location_drilldown[].percentagenumber or nullSame as in stores[].features[].
data.data.per_location_drilldown[].row_statusstringSame as in stores[].features[].
data.data.per_location_drilldown[].flat_chargenumberOnly for model-b: the location's flat charge.
data.data.storesarray of objectsOne entry per active or pending location of the account.
data.data.stores[].store_idintegerThe location's id.
data.data.stores[].store_namestringThe location's name.
data.data.stores[].flat_chargenumberOnly for model-b: the flat charge for the location in the period, to 2 decimals.
data.data.stores[].features_enabledarray of stringsOnly for model-b: the features enabled on the account's plan.
data.data.stores[].featuresarray of objectsOne row per feature enabled on the account's plan (only the one you asked for when you send feature).
data.data.stores[].features[].feature_namestringThe feature, e.g. response_posting.
data.data.stores[].features[].feature_labelstringDisplay name of the feature, e.g. Response posting.
data.data.stores[].features[].units_consumedintegerUnits the location used in the period.
data.data.stores[].features[].limitinteger or nullThe location's limit for the feature, or null when there's no limit.
data.data.stores[].features[].units_overintegerUnits above the limit. Always 0 for model-b.
data.data.stores[].features[].overage_ratenumber or nullPrice per unit over the limit, to 4 decimals. null for model-b or when not set.
data.data.stores[].features[].hard_stopbooleanWhether the feature stops at the limit instead of charging overage.
data.data.stores[].features[].costnumberCharges for the feature, excluding overage, to 2 decimals. Always 0 for model-b.
data.data.stores[].features[].overage_costnumberOverage charges for the feature, to 2 decimals. Always 0 for model-b.
data.data.stores[].features[].percentagenumber or nullunits_consumed as a percentage of limit, to 1 decimal. null when there is no limit.
data.data.stores[].features[].row_statusstringFor model-a: on_track, approaching (75% or more), overage_billing, hard_stop_over_limit or halted. For model-b: on_track, warning (75% or more) or halted.

Response payload

{
  "data": {
    "data": {
      "account_id": 5994,
      "account_name": "Acme Coffee",
      "period": "2026-09",
      "billing_model": "usage-based",
      "contract_model": "model-a",
      "plan_subtitle": "Growth · 2 loc · Per-location cap",
      "feature_options": [
        {
          "value": "all",
          "label": "All"
        },
        {
          "value": "ai_response",
          "label": "AI responses"
        },
        {
          "value": "response_posting",
          "label": "Response posting"
        }
      ],
      "per_location_drilldown": [
        {
          "store_id": 3007,
          "store_name": "Acme Coffee",
          "feature_name": "ai_response",
          "feature_label": "AI responses",
          "units_consumed": 42,
          "limit": 50,
          "units_over": 0,
          "overage_rate": 0.02,
          "hard_stop": false,
          "cost": 0,
          "overage_cost": 0,
          "percentage": 84.0,
          "row_status": "approaching"
        },
        {
          "store_id": 3007,
          "store_name": "Acme Coffee",
          "feature_name": "response_posting",
          "feature_label": "Response posting",
          "units_consumed": 61,
          "limit": 50,
          "units_over": 11,
          "overage_rate": 0.04,
          "hard_stop": false,
          "cost": 0,
          "overage_cost": 0.44,
          "percentage": 122.0,
          "row_status": "overage_billing"
        },
        {
          "store_id": 3008,
          "store_name": "Acme Coffee - Circular Quay",
          "feature_name": "ai_response",
          "feature_label": "AI responses",
          "units_consumed": 8,
          "limit": 50,
          "units_over": 0,
          "overage_rate": 0.02,
          "hard_stop": false,
          "cost": 0,
          "overage_cost": 0,
          "percentage": 16.0,
          "row_status": "on_track"
        },
        {
          "store_id": 3008,
          "store_name": "Acme Coffee - Circular Quay",
          "feature_name": "response_posting",
          "feature_label": "Response posting",
          "units_consumed": 12,
          "limit": 50,
          "units_over": 0,
          "overage_rate": 0.04,
          "hard_stop": false,
          "cost": 0,
          "overage_cost": 0,
          "percentage": 24.0,
          "row_status": "on_track"
        }
      ],
      "stores": [
        {
          "store_id": 3007,
          "store_name": "Acme Coffee",
          "features": [
            {
              "feature_name": "ai_response",
              "feature_label": "AI responses",
              "units_consumed": 42,
              "limit": 50,
              "units_over": 0,
              "overage_rate": 0.02,
              "hard_stop": false,
              "cost": 0,
              "overage_cost": 0,
              "percentage": 84.0,
              "row_status": "approaching"
            },
            {
              "feature_name": "response_posting",
              "feature_label": "Response posting",
              "units_consumed": 61,
              "limit": 50,
              "units_over": 11,
              "overage_rate": 0.04,
              "hard_stop": false,
              "cost": 0,
              "overage_cost": 0.44,
              "percentage": 122.0,
              "row_status": "overage_billing"
            }
          ]
        },
        {
          "store_id": 3008,
          "store_name": "Acme Coffee - Circular Quay",
          "features": [
            {
              "feature_name": "ai_response",
              "feature_label": "AI responses",
              "units_consumed": 8,
              "limit": 50,
              "units_over": 0,
              "overage_rate": 0.02,
              "hard_stop": false,
              "cost": 0,
              "overage_cost": 0,
              "percentage": 16.0,
              "row_status": "on_track"
            },
            {
              "feature_name": "response_posting",
              "feature_label": "Response posting",
              "units_consumed": 12,
              "limit": 50,
              "units_over": 0,
              "overage_rate": 0.04,
              "hard_stop": false,
              "cost": 0,
              "overage_cost": 0,
              "percentage": 24.0,
              "row_status": "on_track"
            }
          ]
        }
      ]
    }
  }
}

Examples

Success payload

See Response payload.

A model-b account (flat charge per location, no overage):

{
  "data": {
    "data": {
      "account_id": 5994,
      "account_name": "Acme Coffee",
      "period": "2026-09",
      "billing_model": "plan-based",
      "contract_model": "model-b",
      "plan_subtitle": "Growth · 2 loc · Per-location cap",
      "feature_options": [
        {
          "value": "all",
          "label": "All"
        },
        {
          "value": "response_posting",
          "label": "Response posting"
        }
      ],
      "per_location_drilldown": [
        {
          "store_id": 3007,
          "store_name": "Acme Coffee",
          "flat_charge": 20.0,
          "feature_name": "response_posting",
          "feature_label": "Response posting",
          "units_consumed": 40,
          "limit": 50,
          "units_over": 0,
          "overage_rate": null,
          "hard_stop": true,
          "cost": 0.0,
          "overage_cost": 0.0,
          "percentage": 80.0,
          "row_status": "warning"
        },
        {
          "store_id": 3008,
          "store_name": "Acme Coffee - Circular Quay",
          "flat_charge": 20.0,
          "feature_name": "response_posting",
          "feature_label": "Response posting",
          "units_consumed": 12,
          "limit": 50,
          "units_over": 0,
          "overage_rate": null,
          "hard_stop": true,
          "cost": 0.0,
          "overage_cost": 0.0,
          "percentage": 24.0,
          "row_status": "on_track"
        }
      ],
      "stores": [
        {
          "store_id": 3007,
          "store_name": "Acme Coffee",
          "flat_charge": 20.0,
          "features_enabled": [
            "response_posting"
          ],
          "features": [
            {
              "feature_name": "response_posting",
              "feature_label": "Response posting",
              "units_consumed": 40,
              "limit": 50,
              "units_over": 0,
              "overage_rate": null,
              "hard_stop": true,
              "cost": 0.0,
              "overage_cost": 0.0,
              "percentage": 80.0,
              "row_status": "warning"
            }
          ]
        },
        {
          "store_id": 3008,
          "store_name": "Acme Coffee - Circular Quay",
          "flat_charge": 20.0,
          "features_enabled": [
            "response_posting"
          ],
          "features": [
            {
              "feature_name": "response_posting",
              "feature_label": "Response posting",
              "units_consumed": 12,
              "limit": 50,
              "units_over": 0,
              "overage_rate": null,
              "hard_stop": true,
              "cost": 0.0,
              "overage_cost": 0.0,
              "percentage": 24.0,
              "row_status": "on_track"
            }
          ]
        }
      ]
    }
  }
}

Error payload

403 Account not under your partner

{
  "message": "Forbidden",
  "status": "error",
  "errors": "Brand does not belong to your partner."
}

403 Not a partner

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

404 Account not found

{
  "message": "Not Found",
  "status": "error",
  "errors": "Brand not found."
}

422 Feature not on the plan

{
  "message": "Unknown or disabled feature for this brand's plan. Use `all` or omit the parameter to load every metered feature.",
  "errors": {
    "feature": [
      "Unknown or disabled feature for this brand's plan. Use `all` or omit the parameter to load every metered feature."
    ]
  }
}

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 account's billing detail.
401The token is missing, expired or invalid.
403The account isn't under your partner (Brand does not belong to your partner.), or the caller isn't a partner or sub-partner user (Authenticated user is not associated with an partner.).
404No account has that id (Brand not found.).
422feature isn't enabled on the account's plan or is longer than 128 characters, year 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/account/5994?year=2026&month=9" \
  --header 'Authorization: Bearer <bearer-token>' \
  --header 'Accept: application/json'

Did this page help you?