7 Counters (Meters)
Set up utility meters, track readings, manage tariff zones and measurement units. The system monitors timely submission of readings by tenants.
Why Counters Are Critical
Most of the key utility expenses — electricity, gas, water, heating — are calculated based on meter readings. Accurate and timely readings ensure correct billing, prevent disputes between landlords and tenants, and provide transparent consumption data.
What counters enable
- Automatic expense charge calculation based on consumption
- Multi-zone tariff support (day/night, peak/off-peak)
- Consumption tracking per period with daily averages
- Monitoring timely reading submissions by tenants
- Adjustment mechanism for meter replacements or corrections
Expense Types with Counters
Five expense types support meter-based tracking. Each has a default measurement unit that can be overridden per counter.
Counter Fields
When creating a counter, you specify the expense type, serial number, tariff type, and the initial reading with its date.
expense_type_id
Expense type (required)
serial_number
Factory serial number, unique per property
tariff_type
SINGLE_ZONE, TWO_ZONE, or THREE_ZONE
initial_value
Starting reading (for single-zone)
initial_value_date
Date of initial reading (required)
initial_value_t1/t2/t3
Zone values for multi-zone tariffs
Tariff Zones
Electricity counters support multi-zone tariffs. For two-zone and three-zone counters, you provide separate readings per zone — the total is calculated automatically.
| Tariff Type | Zones | Fields | Description |
|---|---|---|---|
| SINGLE_ZONE | 1 | value |
Standard single-reading meter |
| TWO_ZONE | 2 | value_t1, value_t2 |
T1 = Day, T2 = Night (coeff. 50%) |
| THREE_ZONE | 3 | value_t1, value_t2, value_t3 |
T1 = Peak (150%), T2 = Half-peak (100%), T3 = Night (40%) |
For multi-zone counters, the total value is always calculated as the sum of zone values: value = value_t1 + value_t2 [+ value_t3]. You do not need to provide the total — it is computed automatically.
Types of Readings
The system supports four types of counter readings, each serving a specific purpose in the billing workflow.
Only one reading per day per counter. New readings must be >= current value (otherwise auto-detected as adjustment). If a reading is lower than current, the system automatically creates an adjustment entry.
Timeliness Tracking
The system monitors whether tenants submit their readings on time. You can set a reading day for each counter, and the system tracks compliance.
Tracking features
- Configurable reading day (1-31) per counter
- Automatic interpolation for missed periods
- Consumption statistics: total, per period, daily average
- Counter photos and reading photos for verification
Documents
You can attach documents (photos, PDFs) to counters and their readings. Documents are stored securely in S3 and accessed via signed URLs.
Maximum file size: 20 MB. Supported formats: JPEG, PNG, GIF, WebP, HEIC, PDF. Multiple files can be attached to each counter or reading.
Documents API
POST /properties/{propertyId}/counters/{id}/documents/upload # Upload counter document
POST /properties/{propertyId}/counters/{id}/documents/{documentId}/delete # Delete counter document
POST /properties/{propertyId}/counters/{id}/values/{valueId}/documents/upload # Upload reading document
POST /properties/{propertyId}/counters/{id}/values/{valueId}/documents/{documentId}/delete # Delete reading document
All uploaded documents appear in the Documents section (/my/documents) and can be filtered by type: "Counter" or "Counter Reading".
How to Add a Counter
Navigate to any property, then click "Counters" in the property menu. From there you can add new counters and submit readings.
Property page > Property menu > Counters > Add counter
For detailed documentation, see the Counters section in the Library.
For Developers: REST API
All counter and reading operations are available via the REST API. Use the demo credentials to get a JWT token first.
GET List Counters
▶Get all counters for a property with their current values and status.
GET /api/v1/properties/{propertyId}/counters
Response (200 OK):
{
"counters": [
{
"id": 4,
"expense_type": {"id": 3, "code": "ELECTRICITY"},
"serial_number": "NIK-2281-073915",
"tariff_type": "THREE_ZONE",
"zone_count": 3,
"current_value": "5729.000",
"documents_count": 2,
"is_active": true,
"created_at": "2026-02-20T15:45:43+00:00"
},
{
"id": 5,
"expense_type": {"id": 10, "code": "GAS"},
"serial_number": "G4-BK-2023-041822",
"tariff_type": "SINGLE_ZONE",
"zone_count": 1,
"current_value": "1612.400",
"documents_count": 0,
"is_active": true,
"created_at": "2026-02-20T15:45:44+00:00"
}
],
"total": 5
}
POST Create Counter
▶Create a new counter with initial reading. Specify zone values for multi-zone tariffs — the total is calculated automatically.
POST /api/v1/properties/{propertyId}/counters
Headers:
Authorization: Bearer {token}
Content-Type: application/json
Single-zone example:
{
"expense_type_id": 10,
"serial_number": "G4-BK-2023-041822",
"tariff_type": "SINGLE_ZONE",
"initial_value": "1245.500",
"initial_value_date": "2025-09-01"
}
Three-zone example (electricity):
{
"expense_type_id": 3,
"serial_number": "EA-2024-073915",
"tariff_type": "THREE_ZONE",
"initial_value_t1": "2100.000",
"initial_value_t2": "1500.000",
"initial_value_t3": "850.000",
"initial_value_date": "2025-09-01"
}
Response (201 Created):
{
"id": 1,
"expense_type": {"id": 3, "code": "ELECTRICITY"},
"serial_number": "EA-2024-073915",
"tariff_type": "THREE_ZONE",
"zone_count": 3,
"initial_value": "4450.000",
"initial_value_t1": "2100.000",
"initial_value_t2": "1500.000",
"initial_value_t3": "850.000",
"initial_value_date": "2025-09-01",
"current_value": "4450.000",
"documents_count": 0,
"is_active": true,
"can_delete": true,
"created_at": "2025-09-01T00:00:00+00:00"
}
GET Counter Details
▶Get full counter details including all readings sorted by date (newest first).
GET /api/v1/properties/{propertyId}/counters/{id}
Response (200 OK):
{
"id": 4,
"expense_type": {"id": 3, "code": "ELECTRICITY"},
"serial_number": "NIK-2281-073915",
"tariff_type": "THREE_ZONE",
"zone_count": 3,
"initial_value": "4450.000",
"initial_value_t1": "2100.000",
"initial_value_t2": "1500.000",
"initial_value_t3": "850.000",
"initial_value_date": "2025-08-01",
"current_value": "5729.000",
"documents_count": 2,
"is_active": true,
"has_linked_charges": false,
"can_delete": true,
"values": [
{
"id": 8,
"value": "5729.000",
"value_t1": "2752.100",
"value_t2": "1851.300",
"value_t3": "1125.600",
"date": "2025-12-01",
"is_initial": false,
"is_adjustment": false,
"documents_count": 0,
"has_linked_charge": false,
"can_edit": false,
"can_delete": true
},
{
"id": 5,
"value": "4450.000",
"value_t1": "2100.000",
"value_t2": "1500.000",
"value_t3": "850.000",
"date": "2025-08-01",
"is_initial": true,
"is_adjustment": false,
"documents_count": 0,
"has_linked_charge": false,
"can_edit": true,
"can_delete": true
}
]
}
PUT Update Counter
▶Update counter metadata (serial number, expense type, tariff type). Only provided fields are updated.
PUT /api/v1/properties/{propertyId}/counters/{id}
Updatable fields:
Request Body:
{
"serial_number": "EA-2024-073915-NEW"
}
Response (200 OK):
{
"id": 4,
"expense_type": {"id": 3, "code": "ELECTRICITY"},
"serial_number": "EA-2024-073915-NEW",
"tariff_type": "THREE_ZONE",
"zone_count": 3,
"current_value": "5729.000",
"documents_count": 2,
"is_active": true,
"has_linked_charges": false,
"can_delete": true,
"created_at": "2026-02-20T15:45:43+00:00"
}
DELETE Delete Counter
▶Delete a counter and all its readings. Returns 204 on success.
DELETE /api/v1/properties/{propertyId}/counters/{id}
POST Toggle Active/Inactive
▶Toggle counter active status. Inactive counters are excluded from automatic expense charge calculations.
POST /api/v1/properties/{propertyId}/counters/{id}/toggle-active
Response (200 OK):
{"id": 1, "is_active": false}
POST Add Reading
▶Submit a new meter reading. For single-zone counters, provide "value". For multi-zone, provide zone values (value_t1, value_t2, value_t3) — the total is computed automatically.
POST /api/v1/properties/{propertyId}/counters/{id}/values
Single-zone reading:
{
"value": "1587.340",
"date": "2025-11-01"
}
Multi-zone reading:
{
"value_t1": "2280.000",
"value_t2": "1620.000",
"value_t3": "925.700",
"date": "2025-11-01"
}
Response (201 Created):
{
"id": 5,
"value": "4825.700",
"value_t1": "2280.000",
"value_t2": "1620.000",
"value_t3": "925.700",
"date": "2025-11-01",
"is_initial": false,
"is_adjustment": false,
"documents_count": 0,
"has_linked_charge": false
}
PUT Update Reading
▶Update an existing meter reading. Only initial readings can be edited via the web interface; the API allows updating any reading.
PUT /api/v1/properties/{propertyId}/counters/{id}/values/{valueId}
Request Body:
{
"value": "1590.000",
"date": "2025-11-02"
}
Response (200 OK):
{
"id": 5,
"value": "1590.000",
"value_t1": null,
"value_t2": null,
"value_t3": null,
"date": "2025-11-02",
"is_initial": false,
"is_adjustment": false,
"documents_count": 0,
"has_linked_charge": false,
"can_edit": true,
"can_delete": true
}
DELETE Delete Reading
▶Delete a counter reading. Returns 204 on success.
DELETE /api/v1/properties/{propertyId}/counters/{id}/values/{valueId}
POST Counter Documents
▶Attach photos of the meter, installation certificates, calibration documents. Upload on the counter detail page.
POST /properties/{propertyId}/counters/{id}/documents/upload
Headers:
Content-Type: multipart/form-data
Request Body (form-data):
documents[] — file (jpg, jpeg, png, gif, webp, heic, pdf), max 20 MB
comment — string (optional)
Response (200 OK):
{
"success": true,
"uploaded": 1
}
DELETE /properties/{propertyId}/counters/{id}/documents/{documentId}/delete
{
"success": true
}
POST Reading Documents
▶Attach photos of the meter display when adding or viewing readings. Upload when creating a reading or on the counter detail page.
POST /properties/{propertyId}/counters/{id}/values/{valueId}/documents/upload
Request Body (form-data):
documents[] — file (jpg, jpeg, png, gif, webp, heic, pdf), max 20 MB
comment — string (optional)
Response (200 OK):
{
"success": true,
"uploaded": 2
}
DELETE /properties/{propertyId}/counters/{id}/values/{valueId}/documents/{documentId}/delete
{
"success": true
}
cURL Examples
▶1. Create Counter (Single-zone example)
curl -X POST https://landlordkeeper.com/api/v1/properties/5/counters \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN_HERE" \
-d '{
"expense_type_id": 10,
"serial_number": "G4-BK-2023-041822",
"tariff_type": "SINGLE_ZONE",
"initial_value": "1245.500",
"initial_value_date": "2025-09-01"
}'
2. Create Counter (Three-zone example (electricity))
curl -X POST https://landlordkeeper.com/api/v1/properties/5/counters \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN_HERE" \
-d '{
"expense_type_id": 3,
"serial_number": "EA-2024-073915",
"tariff_type": "THREE_ZONE",
"initial_value_t1": "2100.000",
"initial_value_t2": "1500.000",
"initial_value_t3": "850.000",
"initial_value_date": "2025-09-01"
}'
3. Add Reading
curl -X POST https://landlordkeeper.com/api/v1/properties/5/counters/1/values \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN_HERE" \
-d '{
"value_t1": "2280.000",
"value_t2": "1620.000",
"value_t3": "925.700",
"date": "2025-11-01"
}'
4. List Counters
curl https://landlordkeeper.com/api/v1/properties/5/counters \
-H "Authorization: Bearer YOUR_TOKEN_HERE"
5. Counter Documents
curl -X POST https://landlordkeeper.com/properties/5/counters/4/documents/upload \
-H "Cookie: PHPSESSID=your_session" \
-F "documents[][email protected]" \
-F "comment=Meter photo"
6. Reading Documents
curl -X POST https://landlordkeeper.com/properties/5/counters/4/values/8/documents/upload \
-H "Cookie: PHPSESSID=your_session" \
-F "documents[]=@reading_jan.jpg" \
-F "documents[]=@reading_jan_2.pdf"