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
yearandmonth(default: the current month), and afeatureto show one feature only.
Base URL
| Environment | URL |
|---|---|
| Production | https://production-api.shoutaboutus.com |
| Development | https://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
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
brandId | path | integer | Yes | The account's id. Digits only. |
year | query | integer | No | Billing year, 2000 to 2100. Defaults to the current year. |
month | query | integer | No | Billing month, 1 to 12. Defaults to the current month. |
feature | query | string | No | One 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.
| Field | Type | Description |
|---|---|---|
data.data | object | The account's billing detail. |
data.data.account_id | integer | The account's id. |
data.data.account_name | string or null | The account's name. |
data.data.period | string | The billing period, as YYYY-MM. |
data.data.billing_model | string or null | Your partner's billing model: plan-based or usage-based. null when not set. |
data.data.contract_model | string or null | Your 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_subtitle | string or null | A 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_options | array of objects | Choices for a feature filter: all first, then each feature enabled on the plan. Use a value as feature. |
data.data.feature_options[].value | string | The value to send as feature. |
data.data.feature_options[].label | string | Display name. |
data.data.per_location_drilldown | array of objects | The same feature rows as stores, flattened: one row per location and feature. |
data.data.per_location_drilldown[].store_id | integer | The location's id. |
data.data.per_location_drilldown[].store_name | string | The location's name. |
data.data.per_location_drilldown[].feature_name | string | Same as in stores[].features[]. |
data.data.per_location_drilldown[].feature_label | string | Same as in stores[].features[]. |
data.data.per_location_drilldown[].units_consumed | integer | Same as in stores[].features[]. |
data.data.per_location_drilldown[].limit | integer or null | Same as in stores[].features[]. |
data.data.per_location_drilldown[].units_over | integer | Same as in stores[].features[]. |
data.data.per_location_drilldown[].overage_rate | number or null | Same as in stores[].features[]. |
data.data.per_location_drilldown[].hard_stop | boolean | Same as in stores[].features[]. |
data.data.per_location_drilldown[].cost | number | Same as in stores[].features[]. |
data.data.per_location_drilldown[].overage_cost | number | Same as in stores[].features[]. |
data.data.per_location_drilldown[].percentage | number or null | Same as in stores[].features[]. |
data.data.per_location_drilldown[].row_status | string | Same as in stores[].features[]. |
data.data.per_location_drilldown[].flat_charge | number | Only for model-b: the location's flat charge. |
data.data.stores | array of objects | One entry per active or pending location of the account. |
data.data.stores[].store_id | integer | The location's id. |
data.data.stores[].store_name | string | The location's name. |
data.data.stores[].flat_charge | number | Only for model-b: the flat charge for the location in the period, to 2 decimals. |
data.data.stores[].features_enabled | array of strings | Only for model-b: the features enabled on the account's plan. |
data.data.stores[].features | array of objects | One row per feature enabled on the account's plan (only the one you asked for when you send feature). |
data.data.stores[].features[].feature_name | string | The feature, e.g. response_posting. |
data.data.stores[].features[].feature_label | string | Display name of the feature, e.g. Response posting. |
data.data.stores[].features[].units_consumed | integer | Units the location used in the period. |
data.data.stores[].features[].limit | integer or null | The location's limit for the feature, or null when there's no limit. |
data.data.stores[].features[].units_over | integer | Units above the limit. Always 0 for model-b. |
data.data.stores[].features[].overage_rate | number or null | Price per unit over the limit, to 4 decimals. null for model-b or when not set. |
data.data.stores[].features[].hard_stop | boolean | Whether the feature stops at the limit instead of charging overage. |
data.data.stores[].features[].cost | number | Charges for the feature, excluding overage, to 2 decimals. Always 0 for model-b. |
data.data.stores[].features[].overage_cost | number | Overage charges for the feature, to 2 decimals. Always 0 for model-b. |
data.data.stores[].features[].percentage | number or null | units_consumed as a percentage of limit, to 1 decimal. null when there is no limit. |
data.data.stores[].features[].row_status | string | For 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."
}| Status | Meaning |
|---|---|
200 | The account's billing detail. |
401 | The token is missing, expired or invalid. |
403 | The 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.). |
404 | No account has that id (Brand not found.). |
422 | feature 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. |
429 | More than 120 requests in a minute from this user. |
500 | Unexpected 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'Updated 17 days ago
Did this page help you?
