{
  "info": {
    "name": "PHY Energy API",
    "description": "Public API collection for PHY Energy platform. Includes endpoints for authentication, organizations, tariffs (with catalogue), meters, gateways, building blocks, timeseries, and invoices. Last updated: 2026-05-08",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
    "version": "1.4.0"
  },
  "auth": {
    "type": "bearer",
    "bearer": [
      { "key": "token", "value": "{{access_token}}", "type": "string" }
    ]
  },
  "variable": [
    { "key": "base_url", "value": "https://phy.energy", "type": "string" },
    { "key": "access_token", "value": "", "type": "string" },
    { "key": "refresh_token", "value": "", "type": "string" },
    { "key": "account_id", "value": "", "type": "string" },
    { "key": "user_id", "value": "", "type": "string" }
  ],
  "item": [
    {
      "name": "Authentication",
      "description": "Login, signup, token refresh, and organization membership endpoints. Login and Refresh automatically update the collection variables access_token and refresh_token.",
      "item": [
        {
          "name": "Login",
          "event": [
            {
              "listen": "test",
              "script": {
                "exec": [
                  "if (pm.response.code === 200) {",
                  "  const res = pm.response.json();",
                  "  if (res.success && res.data) {",
                  "    if (res.data.access_token) pm.collectionVariables.set('access_token', res.data.access_token);",
                  "    if (res.data.refresh_token) pm.collectionVariables.set('refresh_token', res.data.refresh_token);",
                  "    if (res.data.user && res.data.user.id) pm.collectionVariables.set('user_id', res.data.user.id);",
                  "  }",
                  "}"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "request": {
            "auth": { "type": "noauth" },
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"user@example.com\",\n  \"password\": \"your_password\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/auth/login",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "auth", "login"]
            },
            "description": "Authenticate with email and password. On success, access_token and refresh_token are automatically stored as collection variables."
          },
          "response": [
            {
              "name": "Success (200)",
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "body": "{\n  \"success\": true,\n  \"message\": \"Authentication successful\",\n  \"data\": {\n    \"access_token\": \"eyJhbGci...\",\n    \"refresh_token\": \"...\",\n    \"expires_at\": 1735689600,\n    \"expires_in\": 3600,\n    \"user\": {\n      \"id\": \"8b4cdb8d-...\",\n      \"email\": \"user@example.com\",\n      \"created_at\": \"2025-11-13T10:00:00Z\"\n    }\n  }\n}"
            }
          ]
        },
        {
          "name": "Signup",
          "event": [
            {
              "listen": "test",
              "script": {
                "exec": [
                  "if (pm.response.code === 200) {",
                  "  const res = pm.response.json();",
                  "  if (res.success && res.data && res.data.user) {",
                  "    pm.collectionVariables.set('user_id', res.data.user.id);",
                  "  }",
                  "}"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "request": {
            "auth": { "type": "noauth" },
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"newuser@example.com\",\n  \"password\": \"SecretPass123!\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/auth/signup",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "auth", "signup"]
            },
            "description": "Register a new user account. Returns the created user object. Note: tokens are not returned on signup — use the Login endpoint to obtain an access token after registration."
          },
          "response": [
            {
              "name": "Success (200)",
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "body": "{\n  \"success\": true,\n  \"message\": \"Authentication successful\",\n  \"data\": {\n    \"user\": {\n      \"id\": \"f47ac10b-...\",\n      \"email\": \"newuser@example.com\",\n      \"created_at\": \"2025-11-13T10:00:00Z\"\n    }\n  }\n}"
            }
          ]
        },
        {
          "name": "Refresh Token",
          "event": [
            {
              "listen": "test",
              "script": {
                "exec": [
                  "if (pm.response.code === 200) {",
                  "  const res = pm.response.json();",
                  "  if (res.success && res.data) {",
                  "    if (res.data.access_token) pm.collectionVariables.set('access_token', res.data.access_token);",
                  "    if (res.data.refresh_token) pm.collectionVariables.set('refresh_token', res.data.refresh_token);",
                  "  }",
                  "}"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "request": {
            "auth": { "type": "noauth" },
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"refresh_token\": \"{{refresh_token}}\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/auth/refresh",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "auth", "refresh"]
            },
            "description": "Exchange a refresh token for a new access token. Automatically updates the access_token and refresh_token collection variables on success."
          },
          "response": []
        },
        {
          "name": "List My Organizations",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/auth/organizations",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "auth", "organizations"]
            },
            "description": "List all organizations the authenticated user belongs to, along with the user's role in each."
          },
          "response": [
            {
              "name": "Success (200)",
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "body": "{\n  \"success\": true,\n  \"message\": \"Retrieved 2 organizations\",\n  \"data\": [\n    {\n      \"id\": \"8b4cdb8d-...\",\n      \"name\": \"Example Utilities\",\n      \"is_personal_account\": false,\n      \"role\": \"ADMIN\"\n    },\n    {\n      \"id\": \"a17ac10b-...\",\n      \"name\": \"Personal Account\",\n      \"is_personal_account\": true,\n      \"role\": \"OWNER\"\n    }\n  ],\n  \"count\": 2\n}"
            }
          ]
        },
        {
          "name": "List Organizations by User ID",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/auth/users/{{user_id}}/organizations",
              "host": ["{{base_url}}"],
              "path": [
                "api",
                "v1",
                "auth",
                "users",
                "{{user_id}}",
                "organizations"
              ]
            },
            "description": "List organizations for a specified user ID. Subject to authorization rules; only organizations the requester is permitted to view are returned."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Organizations",
      "description": "Create, read, and update organizations (team accounts). Manage organization members.",
      "item": [
        {
          "name": "Create Organization",
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Example Utilities\",\n  \"primary_owner_user_id\": \"{{user_id}}\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/organizations",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "organizations"]
            },
            "description": "Create a new organization (team account). Requires a valid Bearer token and the user_id of the primary owner."
          },
          "response": [
            {
              "name": "Success (201)",
              "status": "Created",
              "code": 201,
              "_postman_previewlanguage": "json",
              "body": "{\n  \"success\": true,\n  \"message\": \"Organization created successfully\",\n  \"data\": {\n    \"id\": \"8b4cdb8d-...\",\n    \"name\": \"Example Utilities\",\n    \"is_personal_account\": false\n  }\n}"
            }
          ]
        },
        {
          "name": "Get Organization",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/organizations/{{account_id}}",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "organizations", "{{account_id}}"]
            },
            "description": "Retrieve an organization by its UUID."
          },
          "response": []
        },
        {
          "name": "Update Organization",
          "request": {
            "method": "PUT",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Example Utilities Group\",\n  \"email\": \"contact@example.com\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/organizations/{{account_id}}",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "organizations", "{{account_id}}"]
            },
            "description": "Update mutable organization fields: name, email, picture_url, public_data."
          },
          "response": []
        },
        {
          "name": "Members",
          "item": [
            {
              "name": "List Members",
              "request": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/api/v1/organizations/{{account_id}}/members",
                  "host": ["{{base_url}}"],
                  "path": [
                    "api",
                    "v1",
                    "organizations",
                    "{{account_id}}",
                    "members"
                  ]
                },
                "description": "List all members of an organization with their roles."
              },
              "response": []
            },
            {
              "name": "Add Member",
              "request": {
                "method": "POST",
                "header": [
                  { "key": "Content-Type", "value": "application/json" }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"userId\": \"{{user_id}}\",\n  \"role\": \"MEMBER\"\n}"
                },
                "url": {
                  "raw": "{{base_url}}/api/v1/organizations/{{account_id}}/members",
                  "host": ["{{base_url}}"],
                  "path": [
                    "api",
                    "v1",
                    "organizations",
                    "{{account_id}}",
                    "members"
                  ]
                },
                "description": "Add an existing user to an organization. The user must already exist in the system."
              },
              "response": []
            },
            {
              "name": "Remove Member",
              "request": {
                "method": "DELETE",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/api/v1/organizations/{{account_id}}/members/{{user_id}}",
                  "host": ["{{base_url}}"],
                  "path": [
                    "api",
                    "v1",
                    "organizations",
                    "{{account_id}}",
                    "members",
                    "{{user_id}}"
                  ]
                },
                "description": "Remove a user from an organization. Cannot remove the primary owner."
              },
              "response": []
            }
          ]
        }
      ]
    },
    {
      "name": "Building Blocks",
      "description": "Manage hierarchical building structures (sites, buildings, units) and list their meters.",
      "item": [
        {
          "name": "List Building Blocks",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/building_blocks?account_id={{account_id}}&page=1&limit=10",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "building_blocks"],
              "query": [
                {
                  "key": "account_id",
                  "value": "{{account_id}}",
                  "description": "Account UUID (required)"
                },
                { "key": "page", "value": "1" },
                { "key": "limit", "value": "10" },
                { "key": "query", "value": "", "disabled": true },
                { "key": "sortBy", "value": "created_at" },
                { "key": "sortOrder", "value": "desc" },
                {
                  "key": "parent_id",
                  "value": "",
                  "disabled": true,
                  "description": "Filter by parent building block ID"
                },
                {
                  "key": "building_type",
                  "value": "",
                  "disabled": true,
                  "description": "e.g. RESIDENTIAL, COMMERCIAL, INDUSTRIAL"
                }
              ]
            },
            "description": "List building blocks for an account with optional filters."
          },
          "response": []
        },
        {
          "name": "Get Building Block",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/building_blocks/1?account_id={{account_id}}",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "building_blocks", "1"],
              "query": [{ "key": "account_id", "value": "{{account_id}}" }]
            },
            "description": "Retrieve a single building block by ID."
          },
          "response": []
        },
        {
          "name": "Create Building Block",
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"account_id\": \"{{account_id}}\",\n  \"name\": \"Building A\",\n  \"description\": \"Main building\",\n  \"parent_id\": null,\n  \"building_type\": \"RESIDENTIAL\",\n  \"address\": \"123 Main St\",\n  \"city\": \"Cape Town\",\n  \"state\": \"Western Cape\",\n  \"postal_code\": 8001,\n  \"country\": \"South Africa\",\n  \"area\": 120.5,\n  \"is_active\": true,\n  \"location\": null\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/building_blocks",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "building_blocks"]
            },
            "description": "Create a new building block. Required fields: account_id, name, building_type, address, city, state, postal_code, country."
          },
          "response": [
            {
              "name": "Success (201)",
              "status": "Created",
              "code": 201,
              "_postman_previewlanguage": "json",
              "body": "{\n  \"success\": true,\n  \"message\": \"Building block created successfully\",\n  \"data\": {\n    \"id\": 123,\n    \"name\": \"Building A\"\n  }\n}"
            },
            {
              "name": "Error - Missing required field (400)",
              "status": "Bad Request",
              "code": 400,
              "_postman_previewlanguage": "json",
              "body": "{\n  \"success\": false,\n  \"error\": \"{\\\"message\\\":\\\"null value in column \\\\\\\"city\\\\\\\" of relation \\\\\\\"building_blocks\\\\\\\" violates not-null constraint\\\",\\\"code\\\":\\\"23502\\\"}\",\n  \"message\": null\n}"
            }
          ]
        },
        {
          "name": "Update Building Block",
          "request": {
            "method": "PUT",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"account_id\": \"{{account_id}}\",\n  \"name\": \"Updated Building\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/building_blocks/1",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "building_blocks", "1"]
            },
            "description": "Update a building block. Mutable fields: name, description, parent_id, address."
          },
          "response": []
        },
        {
          "name": "Delete Building Block",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/building_blocks/1?account_id={{account_id}}",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "building_blocks", "1"],
              "query": [{ "key": "account_id", "value": "{{account_id}}" }]
            },
            "description": "Delete a building block. Returns 204 on success."
          },
          "response": []
        },
        {
          "name": "List Meters for Building Block",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/building_blocks/1/meters?account_id={{account_id}}&page=1&limit=10",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "building_blocks", "1", "meters"],
              "query": [
                {
                  "key": "account_id",
                  "value": "{{account_id}}",
                  "description": "Account UUID (required)"
                },
                { "key": "page", "value": "1" },
                { "key": "limit", "value": "10" },
                { "key": "query", "value": "", "disabled": true },
                { "key": "sortBy", "value": "created_at" },
                { "key": "sortOrder", "value": "desc" }
              ]
            },
            "description": "List all meters assigned to a specific building block."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Gateways",
      "description": "CRUD operations for network gateways.",
      "item": [
        {
          "name": "List Gateways",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/gateways?account_id={{account_id}}&page=1&limit=10",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "gateways"],
              "query": [
                {
                  "key": "account_id",
                  "value": "{{account_id}}",
                  "description": "Account UUID (required)"
                },
                { "key": "page", "value": "1" },
                { "key": "limit", "value": "10" },
                { "key": "query", "value": "", "disabled": true },
                { "key": "sortBy", "value": "created_at" },
                { "key": "sortOrder", "value": "desc" }
              ]
            },
            "description": "List gateways for an account."
          },
          "response": []
        },
        {
          "name": "Get Gateway",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/gateways/1?account_id={{account_id}}",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "gateways", "1"],
              "query": [{ "key": "account_id", "value": "{{account_id}}" }]
            },
            "description": "Retrieve a single gateway by ID."
          },
          "response": []
        },
        {
          "name": "Create Gateway",
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"account_id\": \"{{account_id}}\",\n  \"name\": \"Main Gateway\",\n  \"serial_number\": \"GW-001\",\n  \"gateway_type\": \"PHY_CONNECT\",\n  \"communication_type\": \"LORAWAN\",\n  \"building_block_id\": null,\n  \"firmware_version\": \"1.0.0\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/gateways",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "gateways"]
            },
            "description": "Create a new gateway. Required: account_id, name, serial_number, gateway_type, communication_type.\n\ngateway_type values: PHY_CONNECT, MILESIGHT, KOCOS, TELTONIKA, APEX_MCS\ncommunication_type values: NONE, LORAWAN, MODBUS, MQTT, HTTP, DLMS, VIRTUAL"
          },
          "response": []
        },
        {
          "name": "Update Gateway",
          "request": {
            "method": "PUT",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"account_id\": \"{{account_id}}\",\n  \"name\": \"Updated Gateway\",\n  \"firmware_version\": \"1.1.0\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/gateways/1",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "gateways", "1"]
            },
            "description": "Update a gateway. Mutable fields: name, building_block_id, firmware_version, gateway_type, communication_type. Note: serial_number is not mutable."
          },
          "response": []
        },
        {
          "name": "Delete Gateway",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/gateways/1?account_id={{account_id}}",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "gateways", "1"],
              "query": [{ "key": "account_id", "value": "{{account_id}}" }]
            },
            "description": "Delete a gateway. Returns 204 on success."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Meters",
      "description": "CRUD operations for meters.",
      "item": [
        {
          "name": "List Meters",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/meters?account_id={{account_id}}&page=1&limit=10",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "meters"],
              "query": [
                {
                  "key": "account_id",
                  "value": "{{account_id}}",
                  "description": "Account UUID (required)"
                },
                { "key": "page", "value": "1" },
                { "key": "limit", "value": "10" },
                { "key": "query", "value": "", "disabled": true },
                { "key": "sortBy", "value": "created_at" },
                { "key": "sortOrder", "value": "desc" }
              ]
            },
            "description": "List meters for an account."
          },
          "response": []
        },
        {
          "name": "Get Meter",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/meters/1?account_id={{account_id}}",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "meters", "1"],
              "query": [{ "key": "account_id", "value": "{{account_id}}" }]
            },
            "description": "Retrieve a single meter by ID."
          },
          "response": []
        },
        {
          "name": "Create Meter",
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"account_id\": \"{{account_id}}\",\n  \"name\": \"Main Meter\",\n  \"serial_number\": \"MTR-001\",\n  \"meter_type\": \"ELECTRICITY\",\n  \"communication_type\": \"LORAWAN\",\n  \"building_block_id\": null,\n  \"gateway_id\": null,\n  \"tariff_id\": null,\n  \"location\": \"Level 1 - DB Room\",\n  \"billing_direction\": \"ACCREC\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/meters",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "meters"]
            },
            "description": "Create a new meter. Required: account_id, name, serial_number, meter_type, communication_type.\n\nmeter_type values: ELECTRICITY, GAS, WATER, SOLAR, GENSET, HVAC, EV, CARBON\ncommunication_type values: NONE, LORAWAN, MODBUS, MQTT, HTTP, DLMS, VIRTUAL\nbilling_direction values: ACCREC (receivable/tenant), ACCPAY (payable/supply cost)"
          },
          "response": []
        },
        {
          "name": "Update Meter",
          "request": {
            "method": "PUT",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"account_id\": \"{{account_id}}\",\n  \"name\": \"Updated Meter\",\n  \"location\": \"Level 2 - DB Room\",\n  \"reading_interval\": 900,\n  \"tariff_id\": 5,\n  \"gateway_id\": 2\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/meters/1",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "meters", "1"]
            },
            "description": "Update a meter. Mutable fields: name, description, reading_interval, gateway_id, building_block_id, last_reading_value, last_reading_at, location, tariff_id, billing_direction. Note: serial_number, meter_type, and communication_type are NOT mutable."
          },
          "response": []
        },
        {
          "name": "Delete Meter",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/meters/1?account_id={{account_id}}",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "meters", "1"],
              "query": [{ "key": "account_id", "value": "{{account_id}}" }]
            },
            "description": "Delete a meter. Returns 204 on success."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Tariffs",
      "description": "CRUD operations for tariffs and their line items. Includes a read-only global catalogue with clone support.",
      "item": [
        {
          "name": "List Tariffs",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/tariffs?account_id={{account_id}}&page=1&limit=10&sortBy=created_at&sortOrder=desc",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "tariffs"],
              "query": [
                {
                  "key": "account_id",
                  "value": "{{account_id}}",
                  "description": "Account UUID (required)"
                },
                { "key": "page", "value": "1" },
                { "key": "limit", "value": "10" },
                { "key": "query", "value": "", "disabled": true },
                { "key": "sortBy", "value": "created_at" },
                { "key": "sortOrder", "value": "desc" },
                {
                  "key": "meter_type",
                  "value": "ELECTRICITY",
                  "disabled": true,
                  "description": "ELECTRICITY | GAS | WATER | CARBON | SOLAR | GENSET | EV"
                },
                {
                  "key": "tariff_type",
                  "value": "FLAT",
                  "disabled": true,
                  "description": "FLAT | TIME_OF_USE | BLOCK"
                },
                {
                  "key": "dso_id",
                  "value": "1",
                  "disabled": true,
                  "description": "Filter by DSO ID"
                }
              ]
            },
            "description": "List tariffs for an account with optional filters."
          },
          "response": []
        },
        {
          "name": "Get Tariff",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/tariffs/1?account_id={{account_id}}",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "tariffs", "1"],
              "query": [{ "key": "account_id", "value": "{{account_id}}" }]
            },
            "description": "Retrieve a tariff by ID, including its tariff_items."
          },
          "response": []
        },
        {
          "name": "Create Tariff",
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"account_id\": \"{{account_id}}\",\n  \"name\": \"Residential Flat Rate\",\n  \"description\": \"Standard residential plan\",\n  \"meter_type\": \"ELECTRICITY\",\n  \"tariff_type\": \"FLAT\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/tariffs",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "tariffs"]
            },
            "description": "Create a new tariff. Note: catalogue tariffs are read-only and cannot be created via this endpoint."
          },
          "response": [
            {
              "name": "Success (201)",
              "status": "Created",
              "code": 201,
              "_postman_previewlanguage": "json",
              "body": "{\n  \"success\": true,\n  \"message\": \"Tariff created successfully\",\n  \"data\": {\n    \"id\": 200,\n    \"name\": \"Residential Flat Rate\"\n  }\n}"
            },
            {
              "name": "Error - Validation (400)",
              "status": "Bad Request",
              "code": 400,
              "_postman_previewlanguage": "json",
              "body": "{\n  \"success\": false,\n  \"error\": \"{\\\"message\\\":\\\"Invalid request body\\\"}\",\n  \"message\": null\n}"
            }
          ]
        },
        {
          "name": "Update Tariff",
          "request": {
            "method": "PUT",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"account_id\": \"{{account_id}}\",\n  \"name\": \"Updated Residential Flat Rate\",\n  \"timezone\": \"Africa/Johannesburg\",\n  \"notes\": \"Seasonal adjustment\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/tariffs/1",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "tariffs", "1"]
            },
            "description": "Update a tariff. Catalogue tariffs cannot be modified and return 400."
          },
          "response": []
        },
        {
          "name": "Delete Tariff",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/tariffs/1?account_id={{account_id}}",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "tariffs", "1"],
              "query": [{ "key": "account_id", "value": "{{account_id}}" }]
            },
            "description": "Delete a tariff. Returns 204 on success. Catalogue tariffs cannot be deleted."
          },
          "response": []
        },
        {
          "name": "Tariff Items",
          "description": "Manage line items (pricing components) for a tariff.",
          "item": [
            {
              "name": "List Tariff Items",
              "request": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/api/v1/tariffs/1/items?page=1&limit=10",
                  "host": ["{{base_url}}"],
                  "path": ["api", "v1", "tariffs", "1", "items"],
                  "query": [
                    { "key": "page", "value": "1" },
                    { "key": "limit", "value": "10" },
                    { "key": "sortBy", "value": "created_at" },
                    { "key": "sortOrder", "value": "desc" }
                  ]
                },
                "description": "List items for a tariff (paginated)."
              },
              "response": []
            },
            {
              "name": "Bulk Create Tariff Items",
              "request": {
                "method": "POST",
                "header": [
                  { "key": "Content-Type", "value": "application/json" }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "[\n  {\n    \"price\": 100.50,\n    \"item_type_code\": \"ENERGY\"\n  },\n  {\n    \"price\": 80.00,\n    \"item_type_code\": \"ENERGY\",\n    \"season\": \"WINTER\"\n  }\n]"
                },
                "url": {
                  "raw": "{{base_url}}/api/v1/tariffs/1/items",
                  "host": ["{{base_url}}"],
                  "path": ["api", "v1", "tariffs", "1", "items"]
                },
                "description": "Bulk create tariff items. Body may be a single object or an array of objects."
              },
              "response": []
            },
            {
              "name": "Update Tariff Item",
              "request": {
                "method": "PUT",
                "header": [
                  { "key": "Content-Type", "value": "application/json" }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"price\": 150.00,\n  \"applies_to\": \"peak\"\n}"
                },
                "url": {
                  "raw": "{{base_url}}/api/v1/tariffs/1/items/1",
                  "host": ["{{base_url}}"],
                  "path": ["api", "v1", "tariffs", "1", "items", "1"]
                },
                "description": "Update a single tariff item. Mutable fields: price, item_type_code, season, applies_to."
              },
              "response": []
            },
            {
              "name": "Delete Tariff Item",
              "request": {
                "method": "DELETE",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/api/v1/tariffs/1/items/1",
                  "host": ["{{base_url}}"],
                  "path": ["api", "v1", "tariffs", "1", "items", "1"]
                },
                "description": "Delete a single tariff item. Returns 204 on success."
              },
              "response": []
            }
          ]
        },
        {
          "name": "Catalogue",
          "description": "Global read-only tariff catalogue. Tariffs can be cloned into an organization for customization.",
          "item": [
            {
              "name": "List Catalogue Tariffs",
              "request": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/api/v1/tariffs/catalogue?page=1&limit=10&sortBy=created_at&sortOrder=desc",
                  "host": ["{{base_url}}"],
                  "path": ["api", "v1", "tariffs", "catalogue"],
                  "query": [
                    { "key": "page", "value": "1" },
                    { "key": "limit", "value": "10" },
                    { "key": "query", "value": "", "disabled": true },
                    { "key": "sortBy", "value": "created_at" },
                    { "key": "sortOrder", "value": "desc" },
                    { "key": "meter_type", "value": "", "disabled": true },
                    { "key": "tariff_type", "value": "", "disabled": true }
                  ]
                },
                "description": "List read-only global catalogue tariffs."
              },
              "response": []
            },
            {
              "name": "Get Catalogue Tariff",
              "request": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/api/v1/tariffs/catalogue/101",
                  "host": ["{{base_url}}"],
                  "path": ["api", "v1", "tariffs", "catalogue", "101"]
                },
                "description": "Retrieve a single catalogue tariff with its items."
              },
              "response": []
            },
            {
              "name": "Clone Catalogue Tariff",
              "request": {
                "method": "POST",
                "header": [
                  { "key": "Content-Type", "value": "application/json" }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"account_id\": \"{{account_id}}\",\n  \"name\": \"Optional override name\"\n}"
                },
                "url": {
                  "raw": "{{base_url}}/api/v1/tariffs/catalogue/101/clone",
                  "host": ["{{base_url}}"],
                  "path": ["api", "v1", "tariffs", "catalogue", "101", "clone"]
                },
                "description": "Clone a catalogue tariff into the destination account as a fully mutable copy."
              },
              "response": []
            }
          ]
        }
      ]
    },
    {
      "name": "Timeseries",
      "description": "Electricity timeseries endpoints for profiles, latest power, and energy sums.",
      "item": [
        {
          "name": "Electricity Energy Profile",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/timeseries/electricity/energy-profile?account_id={{account_id}}&meter_sn=METER-001&start=2026-05-01T00:00:00.000Z&end=2026-05-08T00:00:00.000Z&bucket_minutes=30",
              "host": ["{{base_url}}"],
              "path": [
                "api",
                "v1",
                "timeseries",
                "electricity",
                "energy-profile"
              ],
              "query": [
                { "key": "account_id", "value": "{{account_id}}" },
                { "key": "meter_sn", "value": "METER-001" },
                { "key": "start", "value": "2026-05-01T00:00:00.000Z" },
                { "key": "end", "value": "2026-05-08T00:00:00.000Z" },
                { "key": "bucket_minutes", "value": "30" }
              ]
            },
            "description": "Read interval electricity energy/profile buckets for one meter."
          },
          "response": []
        },
        {
          "name": "Electricity Power Profile",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/timeseries/electricity/power-profile?account_id={{account_id}}&meter_sn=METER-001&start=2026-05-01T00:00:00.000Z&end=2026-05-08T00:00:00.000Z&bucket_minutes=30",
              "host": ["{{base_url}}"],
              "path": [
                "api",
                "v1",
                "timeseries",
                "electricity",
                "power-profile"
              ],
              "query": [
                { "key": "account_id", "value": "{{account_id}}" },
                { "key": "meter_sn", "value": "METER-001" },
                { "key": "start", "value": "2026-05-01T00:00:00.000Z" },
                { "key": "end", "value": "2026-05-08T00:00:00.000Z" },
                { "key": "bucket_minutes", "value": "30" }
              ]
            },
            "description": "Read interval electricity power buckets for one meter."
          },
          "response": []
        },
        {
          "name": "Electricity Latest Power",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/timeseries/electricity/latest-power?account_id={{account_id}}&meter_sn=METER-001&start=2026-05-01T00:00:00.000Z&end=2026-05-08T00:00:00.000Z",
              "host": ["{{base_url}}"],
              "path": [
                "api",
                "v1",
                "timeseries",
                "electricity",
                "latest-power"
              ],
              "query": [
                { "key": "account_id", "value": "{{account_id}}" },
                { "key": "meter_sn", "value": "METER-001" },
                { "key": "start", "value": "2026-05-01T00:00:00.000Z" },
                { "key": "end", "value": "2026-05-08T00:00:00.000Z" }
              ]
            },
            "description": "Read the latest electricity power point inside a bounded range."
          },
          "response": []
        },
        {
          "name": "Electricity Energy Sum",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/timeseries/electricity/energy-sum?account_id={{account_id}}&meter_sn=METER-001&start=2026-05-01T00:00:00.000Z&end=2026-05-08T00:00:00.000Z&bucket_minutes=30",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "timeseries", "electricity", "energy-sum"],
              "query": [
                { "key": "account_id", "value": "{{account_id}}" },
                { "key": "meter_sn", "value": "METER-001" },
                { "key": "start", "value": "2026-05-01T00:00:00.000Z" },
                { "key": "end", "value": "2026-05-08T00:00:00.000Z" },
                { "key": "bucket_minutes", "value": "30" }
              ]
            },
            "description": "Read summed electricity import, export, and kVA values for one meter."
          },
          "response": []
        },
        {
          "name": "Electricity Batch Energy Sums",
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"account_id\": \"{{account_id}}\",\n  \"meter_sns\": [\"METER-001\", \"METER-002\"],\n  \"start\": \"2026-05-01T00:00:00.000Z\",\n  \"end\": \"2026-05-08T00:00:00.000Z\",\n  \"bucket_minutes\": 30,\n  \"max_meters_per_query\": 100\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/timeseries/electricity/energy-sums",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "timeseries", "electricity", "energy-sums"]
            },
            "description": "Read summed electricity import, export, and kVA values for many meters. The server chunks large batches internally."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Downstream Bills",
      "description": "CRUD operations for invoices. Invoice line items are read-only via a nested route.",
      "item": [
        {
          "name": "List Downstream Bills",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/invoices?account_id={{account_id}}&page=1&limit=10",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "invoices"],
              "query": [
                {
                  "key": "account_id",
                  "value": "{{account_id}}",
                  "description": "Account UUID (required)"
                },
                { "key": "page", "value": "1" },
                { "key": "limit", "value": "10" },
                { "key": "query", "value": "", "disabled": true },
                { "key": "sortBy", "value": "created_at" },
                { "key": "sortOrder", "value": "desc" }
              ]
            },
            "description": "List invoices for an account."
          },
          "response": []
        },
        {
          "name": "Get Invoice",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/invoices/1?account_id={{account_id}}",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "invoices", "1"],
              "query": [{ "key": "account_id", "value": "{{account_id}}" }]
            },
            "description": "Retrieve a single invoice by ID."
          },
          "response": []
        },
        {
          "name": "Create Invoice",
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"account_id\": \"{{account_id}}\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/invoices",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "invoices"]
            },
            "description": "Create a new invoice."
          },
          "response": []
        },
        {
          "name": "Update Invoice",
          "request": {
            "method": "PUT",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"account_id\": \"{{account_id}}\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/invoices/1",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "invoices", "1"]
            },
            "description": "Update an invoice."
          },
          "response": []
        },
        {
          "name": "Delete Invoice",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/invoices/1?account_id={{account_id}}",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "invoices", "1"],
              "query": [{ "key": "account_id", "value": "{{account_id}}" }]
            },
            "description": "Delete an invoice. Returns 204 on success."
          },
          "response": []
        },
        {
          "name": "List Invoice Line Items",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/invoices/1/items?page=1&limit=10",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "invoices", "1", "items"],
              "query": [
                { "key": "page", "value": "1" },
                { "key": "limit", "value": "10" },
                { "key": "sortBy", "value": "created_at" },
                { "key": "sortOrder", "value": "desc" },
                {
                  "key": "id",
                  "value": "",
                  "disabled": true,
                  "description": "Pass a specific item ID to retrieve a single item"
                }
              ]
            },
            "description": "List line items for an invoice (read-only). Pass the id query param to retrieve a single item."
          },
          "response": []
        }
      ]
    }
  ]
}
