Tariff API
Manage tariffs, tariff transitions, and tariff items — list, create, read, update, delete, and catalogue operations.
Tariff API Documentation
Overview
The Tariff API provides endpoints for managing tariffs, their successor transitions, and their pricing components (tariff items). Tariffs are associated with organizations and meter types, and can be cloned from a global read-only catalogue. Tariffs can also reference a distribution provider (DSO) and, for time-of-use tariffs, a time-of-use schedule.
Authentication
All tariff endpoints require authentication. Include a valid Bearer token in the Authorization header: Authorization: Bearer <access_token>.
Account scoping
All endpoints require an account_id UUID to scope reads and writes to a specific organization.
- List / read / delete — pass
account_idas a query parameter - Create / update — include
account_idin the JSON request body
Response format
All endpoints return a unified envelope:
{
"success": true,
"message": "optional human readable message",
"data": {},
"count": 1,
"error": "error text"
}
Enum reference
These enums are shared across tariffs, tariff items, and time-of-use scheduling. Individual endpoints may accept a subset of these values — see each endpoint's field reference below.
- Meter type:
ELECTRICITY|WATER|GAS|SOLAR|GENSET|HVAC|CARBON|EV - Tariff type:
FLAT|TIME_OF_USE|BLOCK|DYNAMIC - Currency:
ZAR|USD|EUR|GBP|AUD|CAD|JPY|CNY - Season:
SUMMER|WINTER|TRANSITION(do not use) |ALL_YEAR - Tariff item type:
TOU_PEAK_ENERGY|TOU_OFF_PEAK_ENERGY|TOU_STANDARD_ENERGY|IMPORT_ENERGY|EXPORTED_ENERGY|ANCILLARY_SERVICE|REACTIVE_ENERGY|NETWORK_CAPACITY|NETWORK_DEMAND|URBAN_LV_SUBSIDY|SERVICE|ADMINISTRATION|RURAL_SUBSIDY|LEGACY|GENERATION_CAPACITY|TRANSMISSION_NETWORK|WATER_CONSUMPTION|CARBON|EV_MIN|EV_SESSION_FEE|AFFORDABILITY|EXCESS_NETWORK_CAPACITY|ELECTRIFICATION_AND_RURAL_SUBSIDY - TOU day type:
WEEKDAY|SATURDAY|SUNDAY|PUBLIC_HOLIDAY - TOU period:
PEAK|STANDARD|OFF_PEAK
Field reference
Tariff record (selected fields):
id(integer)name(string)description(string|null)account_id(uuid)dso_id(integer|null) — foreign key to the energy providertou_schedule_id(integer|null) — foreign key to a time-of-use schedule, only set whentariff_typeisTIME_OF_USEmeter_type(enum, see Enum reference)tariff_type(enum, see Enum reference)currency(enum, see Enum reference)tax_rate(decimal)is_active(boolean)effective_from/effective_to(date)timezone(string) — IANA timezone identifiernotes(string|null)cloned_from(integer|null) — source tariff ID when this tariff was cloned from the catalogue
DSO object (nested on tariff reads, null when no provider is set):
id(integer)name(string)tz(string) — IANA timezone identifier (e.g.Africa/Johannesburg)
Tariff item record (selected fields):
id(integer)tariff_id(integer)item_type_code(enum, see Enum reference)price(decimal)season(enum, see Enum reference)
Tariff transition record:
id(integer)predecessor_id(integer) — the current tariff being replacedsuccessor_id(integer) — the tariff that replaces iteffective_from(ISO 8601 timestamp) — when the successor takes overnotes(string)created_at(ISO 8601 timestamp)created_by(uuid|null)
Endpoints
Base path: /api/v1/tariffs
List Tariffs
GET /api/v1/tariffs
Query parameters:
account_id(uuid, required)page(int, default 1)limit(int, default 10, max 100)query(string, optional — text search)sortBy(string, defaultcreated_at)sortOrder(asc|desc, defaultdesc)meter_type(enum:ELECTRICITY|GAS|WATER|CARBON|SOLAR|GENSET|EV, optional)tariff_type(enum:FLAT|TIME_OF_USE|BLOCK, optional)dso_id(int, optional)
Response 200 — list envelope with data array and count.
Create Tariff
POST /api/v1/tariffs
Request body (JSON):
account_id(uuid, required)name(string, required)description(string, optional)timezone(string, optional)notes(string, optional)meter_type(enum:ELECTRICITY|GAS|WATER|CARBON|SOLAR|GENSET, optional)tariff_type(enum:FLAT|TIME_OF_USE|BLOCK, optional)dso_id(int, optional)effective_from(date string, required)effective_to(date string, required)currency(enum:ZAR, optional)tax_rate(positive integer, optional)is_active(boolean, optional)cloned_from(integer, optional)
Example:
{
"account_id": "<account-uuid>",
"name": "Residential Flat Rate",
"description": "Standard residential plan",
"meter_type": "ELECTRICITY",
"tariff_type": "FLAT",
"effective_from": "2026-01-01",
"effective_to": "2026-12-31"
}
Response 201 — created tariff object in data.
Get Tariff
GET /api/v1/tariffs/:id
Query parameters:
account_id(uuid, required)
Returns 200 with the tariff and its nested tariff_items array and dso object.
Update Tariff
PUT /api/v1/tariffs/:id
Request body:
account_id(uuid, required)name(string, optional)description(string, optional)timezone(string, optional)notes(string, optional)meter_type(enum:ELECTRICITY|GAS|WATER|CARBON|SOLAR|GENSET, optional)tariff_type(enum:FLAT|TIME_OF_USE|BLOCK, optional)dso_id(int, optional)effective_from(ISO 8601 string, optional)effective_to(ISO 8601 string, optional)deleted_at(ISO 8601 string, optional)currency(enum:USD|EUR|GBP|ZAR, optional)tax_rate(positive integer, optional)updated_by(uuid, optional)is_active(boolean, optional)cloned_from(integer, optional)
Response 200 — updated tariff object in data.
Delete Tariff
DELETE /api/v1/tariffs/:id
Query parameters:
account_id(uuid, required)
Response 204 — no content.
Catalogue guard: Attempts to create, update, or delete a tariff belonging to the catalogue account return 400 (Catalogue is read-only).
Tariff Transitions
Tariff transitions link the current tariff (predecessor) to the next tariff (successor). Each predecessor tariff can only have one successor transition.
When a transition is created or updated with an effective_from timestamp at or before the request time, the API immediately replaces the predecessor tariff with the successor tariff anywhere it is directly assigned to meters or building-block energy dashboard tariff fields. Future-dated transitions are recorded for scheduled tariff replacement.
Base path: /api/v1/tariffs/:id/transitions
Get Transition
GET /api/v1/tariffs/:id/transitions
Path parameter:
id(integer) — predecessor tariff ID
Query parameters:
account_id(uuid, required)
Returns 200 with the transition in data. Returns 404 if the tariff has no successor transition.
Create Transition
POST /api/v1/tariffs/:id/transitions
Path parameter:
id(integer) — predecessor tariff ID
Request body:
{
"account_id": "<account-uuid>",
"successor_id": 42,
"effective_from": "2026-08-01T00:00:00.000Z",
"notes": "Annual municipal tariff update"
}
account_id(uuid, required)successor_id(integer, required) — must belong to the same organization and must differ fromideffective_from(ISO 8601 timestamp, required)notes(string, optional)
Response 201 — created transition in data. The data.application object reports whether the transition was applied immediately and how many meter/config references were updated.
Errors: 400 invalid body or self-reference, 404 tariff not found for the account, 409 predecessor already has a successor transition.
Update Transition
PUT /api/v1/tariffs/:id/transitions
Request body:
{
"account_id": "<account-uuid>",
"successor_id": 43,
"effective_from": "2026-09-01T00:00:00.000Z",
"notes": "Revised effective date"
}
account_id(uuid, required)successor_id(integer, optional)effective_from(ISO 8601 timestamp, optional)notes(string, optional)
Response 200 — updated transition in data, including data.application.
Delete Transition
DELETE /api/v1/tariffs/:id/transitions
Query parameters:
account_id(uuid, required)
Response 204 — no content.
Catalogue guard: Transitions cannot be managed for catalogue tariffs.
Catalogue (read-only)
The catalogue provides globally maintained, immutable tariff definitions that can be cloned into an organization's scope for customization. Catalogue tariffs and their items cannot be modified directly.
Base path: /api/v1/tariffs/catalogue
List Catalogue Tariffs
GET /api/v1/tariffs/catalogue
Query parameters:
page(int, default 1)limit(int, default 10, max 100)query(string, optional — fuzzy match against name/description)sortBy(string, defaultcreated_at)sortOrder(asc|desc, defaultdesc)meter_type(enum:ELECTRICITY|GAS|WATER|CARBON|SOLAR|GENSET|HVAC|EV, optional)tariff_type(enum:FLAT|TIME_OF_USE|BLOCK|DYNAMIC, optional)dso_id(int, optional)effective_from_gte(date string, optional)effective_from_lte(date string, optional)
Response 200 — data array of catalogue tariff records with count.
Get Catalogue Tariff
GET /api/v1/tariffs/catalogue/:id
Path parameter:
id(integer)
Returns 200 with the catalogue tariff and its nested tariff_items and dso object. Returns 404 if not found.
Clone Catalogue Tariff
POST /api/v1/tariffs/catalogue/:id/clone
Creates a new fully mutable tariff in the destination account, copying all catalogue tariff fields and items.
Path parameter:
id(integer) — source catalogue tariff ID
Request body:
{
"account_id": "<destination-account-uuid>",
"name": "Optional override name"
}
account_id(uuid, required) — destination organizationname(string, optional) — falls back to source tariff name
Response 201 — cloned tariff with tariff_items array and dso object.
Errors: 400 invalid body, 404 catalogue tariff not found.
TOU Schedule
Get TOU Schedule
GET /api/v1/tariffs/:id/schedule
Returns the Time-of-Use schedule and its linked rules for a tariff. Only applicable when tariff_type is TIME_OF_USE.
Path parameter:
id(integer) — tariff ID
Query parameters:
account_id(uuid, required)
Fallback behaviour: When the tariff has no tou_schedule_id configured, schedule ID 3 is used as the default.
Response 200 — schedule object in data:
{
"success": true,
"data": {
"id": 3,
"name": "Standard TOU Schedule",
"dso_id": 1,
"holiday_default": "SUNDAY",
"is_active": true,
"tz": "Africa/Johannesburg",
"version": 1,
"dso": {
"id": 1,
"name": "Example Municipality",
"tz": "Africa/Johannesburg"
},
"tou_rules": [
{
"id": 1,
"schedule_id": 3,
"season_id": 1,
"day_type": "WEEKDAY",
"period": "PEAK",
"start_minute": 420,
"end_minute": 540
}
]
}
}
tou_schedule fields:
id(integer)dso_id(integer)name(string)version(integer) — a DSO/name pair may have multiple versionstz(string) — IANA timezone identifieris_active(boolean)holiday_default(enum, see Enum reference) — day type applied on unlisted holidays
tou_rule fields:
id(integer)schedule_id(integer)season_id(integer) — references a DSO seasonday_type(enum, see Enum reference)period(enum, see Enum reference)start_minute(integer) — minutes from midnight (0–1439), must be a multiple of 30end_minute(integer) — minutes from midnight (exclusive), must be a multiple of 30 and greater thanstart_minute
Errors:
400— tariff exists but is notTIME_OF_USE404— tariff or TOU schedule not found
Tariff Items
List Items
GET /api/v1/tariffs/:id/items
Query parameters: page, limit, query, sortBy, sortOrder.
Returns paginated tariff_items.
Bulk Create Items
POST /api/v1/tariffs/:id/items
Body may be a single object or an array:
[
{ "price": 100.5, "item_type_code": "IMPORT_ENERGY" },
{ "price": 80.0, "item_type_code": "IMPORT_ENERGY", "season": "WINTER" }
]
Fields:
price(decimal, required)item_type_code(enum, see Enum reference, required)season(enum, see Enum reference, optional — defaults toSUMMER)
A given tariff can only have one item per (item_type_code, season) combination.
Response 201 — array of created items.
Update Item
PUT /api/v1/tariffs/:id/items/:itemId
Body fields (all optional): price, item_type_code, season.
Response 200 — updated item.
Delete Item
DELETE /api/v1/tariffs/:id/items/:itemId
Response 204 — no content.
Catalogue guard: Any attempt to modify items of a catalogue tariff returns 400 with Catalogue is read-only.
Related entities
The following entities support the tariff data model. They do not currently have dedicated CRUD endpoints under /api/v1/tariffs, but they may appear as nested objects in responses (e.g. the dso object on a tariff or TOU schedule) or otherwise shape tariff behavior.
Tariff Item Types
Reference data describing each item_type_code value used on tariff items.
code(enum, see Enum reference) — primary keydisplayName(string) — human readable labelunit(string) — unit of measure for the item (e.g.kWh,kVA,day)
Tariff Blocks
Defines tiered/block consumption pricing for a tariff item, used when a tariff's tariff_type is BLOCK. Each row represents one consumption tier for a given tariff item.
id(integer)tariff_item_id(integer) — the tariff item this block belongs toblock_index(integer) — ordering of the tier (0-based)upper_limit(decimal|null) — upper consumption bound for this tier;nullmeans unbounded (the final tier)unit_price_cents(integer) — price per unit for consumption within this tier, in cents
A tariff item can only have one block per block_index.
DSO (Distribution System Operator)
Represents the energy provider/utility that a tariff or TOU schedule belongs to.
id(integer)name(string, unique)tz(string) — IANA timezone identifier
DSO Season
Defines the date ranges that make up each season for a given DSO. Referenced by tou_rule.season_id.
id(integer)dso_id(integer)label(enum, see Enum reference — Season)date_start/date_end(date)
DSO Public Holiday
Per-DSO public holiday calendar. A holiday may override the TOU day type that would otherwise apply on that date.
dso_id(integer)on_date(date)name(string)day_type_override(enum, see Enum reference — TOU day type, optional)