@epilot/sdk 2.4.4 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. package/definitions/app.json +137 -58
  2. package/definitions/customer-portal-runtime.json +1 -1
  3. package/definitions/customer-portal.json +496 -24
  4. package/definitions/entity.json +185 -8
  5. package/definitions/integration-toolkit-runtime.json +1 -1
  6. package/definitions/integration-toolkit.json +480 -4
  7. package/definitions/metering-runtime.json +1 -1
  8. package/definitions/metering.json +728 -64
  9. package/definitions/webhooks-runtime.json +1 -1
  10. package/definitions/webhooks.json +134 -4
  11. package/dist/apis/access-token.cjs +6 -6
  12. package/dist/apis/access-token.js +1 -1
  13. package/dist/apis/address-suggestions.cjs +6 -6
  14. package/dist/apis/address-suggestions.js +1 -1
  15. package/dist/apis/address.cjs +6 -6
  16. package/dist/apis/address.js +1 -1
  17. package/dist/apis/ai-agents.cjs +6 -6
  18. package/dist/apis/ai-agents.js +1 -1
  19. package/dist/apis/app.cjs +8 -8
  20. package/dist/apis/app.d.cts +2 -2
  21. package/dist/apis/app.d.ts +2 -2
  22. package/dist/apis/app.js +2 -2
  23. package/dist/apis/audit-logs.cjs +6 -6
  24. package/dist/apis/audit-logs.js +1 -1
  25. package/dist/apis/automation.cjs +6 -6
  26. package/dist/apis/automation.js +1 -1
  27. package/dist/apis/billing.cjs +6 -6
  28. package/dist/apis/billing.js +1 -1
  29. package/dist/apis/blueprint-manifest.cjs +6 -6
  30. package/dist/apis/blueprint-manifest.js +1 -1
  31. package/dist/apis/calendar.cjs +6 -6
  32. package/dist/apis/calendar.js +1 -1
  33. package/dist/apis/configuration-hub.cjs +6 -6
  34. package/dist/apis/configuration-hub.js +1 -1
  35. package/dist/apis/consent.cjs +6 -6
  36. package/dist/apis/consent.js +1 -1
  37. package/dist/apis/customer-portal.cjs +8 -8
  38. package/dist/apis/customer-portal.d.cts +2 -2
  39. package/dist/apis/customer-portal.d.ts +2 -2
  40. package/dist/apis/customer-portal.js +2 -2
  41. package/dist/apis/dashboard.cjs +6 -6
  42. package/dist/apis/dashboard.js +1 -1
  43. package/dist/apis/data-governance.cjs +6 -6
  44. package/dist/apis/data-governance.js +1 -1
  45. package/dist/apis/deduplication.cjs +6 -6
  46. package/dist/apis/deduplication.js +1 -1
  47. package/dist/apis/design.cjs +6 -6
  48. package/dist/apis/design.js +1 -1
  49. package/dist/apis/document.cjs +6 -6
  50. package/dist/apis/document.js +1 -1
  51. package/dist/apis/email-settings.cjs +6 -6
  52. package/dist/apis/email-settings.js +1 -1
  53. package/dist/apis/email-template.cjs +6 -6
  54. package/dist/apis/email-template.js +1 -1
  55. package/dist/apis/entity-mapping.cjs +6 -6
  56. package/dist/apis/entity-mapping.js +1 -1
  57. package/dist/apis/entity.cjs +6 -6
  58. package/dist/apis/entity.d.cts +2 -2
  59. package/dist/apis/entity.d.ts +2 -2
  60. package/dist/apis/entity.js +1 -1
  61. package/dist/apis/environments.cjs +6 -6
  62. package/dist/apis/environments.js +1 -1
  63. package/dist/apis/event-catalog.cjs +6 -6
  64. package/dist/apis/event-catalog.js +1 -1
  65. package/dist/apis/file.cjs +6 -6
  66. package/dist/apis/file.js +1 -1
  67. package/dist/apis/iban.cjs +6 -6
  68. package/dist/apis/iban.js +1 -1
  69. package/dist/apis/integration-toolkit.cjs +8 -8
  70. package/dist/apis/integration-toolkit.d.cts +2 -2
  71. package/dist/apis/integration-toolkit.d.ts +2 -2
  72. package/dist/apis/integration-toolkit.js +2 -2
  73. package/dist/apis/journey.cjs +6 -6
  74. package/dist/apis/journey.js +1 -1
  75. package/dist/apis/kanban.cjs +6 -6
  76. package/dist/apis/kanban.js +1 -1
  77. package/dist/apis/message.cjs +6 -6
  78. package/dist/apis/message.js +1 -1
  79. package/dist/apis/metering.cjs +8 -8
  80. package/dist/apis/metering.d.cts +2 -2
  81. package/dist/apis/metering.d.ts +2 -2
  82. package/dist/apis/metering.js +2 -2
  83. package/dist/apis/notes.cjs +6 -6
  84. package/dist/apis/notes.d.cts +2 -2
  85. package/dist/apis/notes.d.ts +2 -2
  86. package/dist/apis/notes.js +1 -1
  87. package/dist/apis/notification.cjs +6 -6
  88. package/dist/apis/notification.js +1 -1
  89. package/dist/apis/organization.cjs +6 -6
  90. package/dist/apis/organization.js +1 -1
  91. package/dist/apis/partner-directory.cjs +6 -6
  92. package/dist/apis/partner-directory.js +1 -1
  93. package/dist/apis/permissions.cjs +6 -6
  94. package/dist/apis/permissions.js +1 -1
  95. package/dist/apis/pricing-tier.cjs +6 -6
  96. package/dist/apis/pricing-tier.js +1 -1
  97. package/dist/apis/pricing.cjs +6 -6
  98. package/dist/apis/pricing.js +1 -1
  99. package/dist/apis/purpose.cjs +6 -6
  100. package/dist/apis/purpose.js +1 -1
  101. package/dist/apis/query.cjs +6 -6
  102. package/dist/apis/query.js +1 -1
  103. package/dist/apis/sandbox.cjs +6 -6
  104. package/dist/apis/sandbox.js +1 -1
  105. package/dist/apis/sharing.cjs +6 -6
  106. package/dist/apis/sharing.js +1 -1
  107. package/dist/apis/submission.cjs +6 -6
  108. package/dist/apis/submission.js +1 -1
  109. package/dist/apis/target.cjs +6 -6
  110. package/dist/apis/target.js +1 -1
  111. package/dist/apis/targeting.cjs +6 -6
  112. package/dist/apis/targeting.js +1 -1
  113. package/dist/apis/template-variables.cjs +6 -6
  114. package/dist/apis/template-variables.js +1 -1
  115. package/dist/apis/user.cjs +6 -6
  116. package/dist/apis/user.js +1 -1
  117. package/dist/apis/validation-rules.cjs +6 -6
  118. package/dist/apis/validation-rules.js +1 -1
  119. package/dist/apis/webhooks.cjs +8 -8
  120. package/dist/apis/webhooks.d.cts +2 -2
  121. package/dist/apis/webhooks.d.ts +2 -2
  122. package/dist/apis/webhooks.js +2 -2
  123. package/dist/apis/workflow-definition.cjs +6 -6
  124. package/dist/apis/workflow-definition.js +1 -1
  125. package/dist/apis/workflow.cjs +6 -6
  126. package/dist/apis/workflow.js +1 -1
  127. package/dist/app-34OBBTA4.js +7 -0
  128. package/dist/app-SSF545U7.cjs +7 -0
  129. package/dist/app-runtime-AFNM67YN.cjs +5 -0
  130. package/dist/{app-runtime-U7RGV7KT.js → app-runtime-DBUXZL6E.js} +1 -1
  131. package/dist/{app.d-u7Dq42kU.d.cts → app.d--5n0FQQ4.d.cts} +377 -11
  132. package/dist/{app.d-u7Dq42kU.d.ts → app.d--5n0FQQ4.d.ts} +377 -11
  133. package/dist/bin/cli.js +1 -1
  134. package/dist/{chunk-JTFWOEZF.cjs → chunk-4FFGRWLS.cjs} +1 -1
  135. package/dist/chunk-56MMZJOD.js +14 -0
  136. package/dist/chunk-5OBMZHRL.cjs +14 -0
  137. package/dist/{chunk-L66IX6GX.cjs → chunk-KMVJYNLB.cjs} +1 -1
  138. package/dist/chunk-KYCVV2XE.cjs +14 -0
  139. package/dist/{chunk-TSV242UN.js → chunk-QNUU4TTU.js} +13 -13
  140. package/dist/chunk-RGO3LTAM.js +14 -0
  141. package/dist/{chunk-CD4OX2U6.cjs → chunk-SDIGGISZ.cjs} +13 -13
  142. package/dist/chunk-T6A22LSD.cjs +14 -0
  143. package/dist/chunk-WM74R3BD.js +14 -0
  144. package/dist/{chunk-LLZYRINH.js → chunk-Z4H2FZ2K.js} +1 -1
  145. package/dist/chunk-Z6HGUGXK.js +14 -0
  146. package/dist/customer-portal-5I7ZGQDU.cjs +7 -0
  147. package/dist/customer-portal-YL2YMPZL.js +7 -0
  148. package/dist/{customer-portal-runtime-PSM55KGU.js → customer-portal-runtime-N4NWQ67M.js} +1 -1
  149. package/dist/{customer-portal-runtime-DULJLR7F.cjs → customer-portal-runtime-OEXTSMSE.cjs} +2 -2
  150. package/dist/{customer-portal.d-DyZ2n2dW.d.cts → customer-portal.d-ChSEioI5.d.cts} +394 -9
  151. package/dist/{customer-portal.d-DyZ2n2dW.d.ts → customer-portal.d-ChSEioI5.d.ts} +394 -9
  152. package/dist/entity-SS36LQO6.cjs +7 -0
  153. package/dist/entity-Z4YWKDVG.js +7 -0
  154. package/dist/{entity.d-BHR9dEon.d.cts → entity.d-fospShiN.d.cts} +234 -169
  155. package/dist/{entity.d-BHR9dEon.d.ts → entity.d-fospShiN.d.ts} +234 -169
  156. package/dist/index.cjs +18 -18
  157. package/dist/index.d.cts +7 -7
  158. package/dist/index.d.ts +7 -7
  159. package/dist/index.js +6 -6
  160. package/dist/integration-toolkit-3I3IPVFN.cjs +7 -0
  161. package/dist/integration-toolkit-XAFQXNQY.js +7 -0
  162. package/dist/{integration-toolkit-runtime-5KG3LGXF.cjs → integration-toolkit-runtime-YHTU4X5J.cjs} +2 -2
  163. package/dist/{integration-toolkit-runtime-LYHFVIRW.js → integration-toolkit-runtime-YR4CIMKH.js} +1 -1
  164. package/dist/{integration-toolkit.d-Ry-KC9ow.d.cts → integration-toolkit.d-BstNqiJb.d.cts} +460 -20
  165. package/dist/{integration-toolkit.d-Ry-KC9ow.d.ts → integration-toolkit.d-BstNqiJb.d.ts} +460 -20
  166. package/dist/{js-yaml-UPZKYVRY.js → js-yaml-DLCVPJ7G.js} +17 -15
  167. package/dist/metering-3IUSYAGN.js +7 -0
  168. package/dist/metering-MHBFU7QH.cjs +7 -0
  169. package/dist/{webhooks-runtime-EBM4M3SU.cjs → metering-runtime-IUZJHBVS.cjs} +2 -2
  170. package/dist/{metering-runtime-NJ6NEUMB.js → metering-runtime-KZMJKKUZ.js} +1 -1
  171. package/dist/{metering.d-DvtPv7wk.d.ts → metering.d-CUICZDiL.d.cts} +718 -130
  172. package/dist/{metering.d-DvtPv7wk.d.cts → metering.d-CUICZDiL.d.ts} +718 -130
  173. package/dist/notes-K2IIFCX3.cjs +7 -0
  174. package/dist/notes-Q7JGS7O3.js +7 -0
  175. package/dist/{notes.d-BC-scR42.d.cts → notes.d-BcV_m5fe.d.cts} +33 -11
  176. package/dist/{notes.d-BC-scR42.d.ts → notes.d-BcV_m5fe.d.ts} +33 -11
  177. package/dist/{pricing-XAET4G7T.cjs → pricing-FODHQFCB.cjs} +1 -1
  178. package/dist/{pricing-G67CH3XH.js → pricing-XRIDVZFC.js} +1 -1
  179. package/dist/webhooks-NZ3TM3AY.cjs +7 -0
  180. package/dist/webhooks-XLBUXXCS.js +7 -0
  181. package/dist/{metering-runtime-HAIWXZXJ.cjs → webhooks-runtime-QVAFNBDW.cjs} +2 -2
  182. package/dist/{webhooks-runtime-XCQYIGQK.js → webhooks-runtime-UYTPQQVG.js} +1 -1
  183. package/dist/{webhooks.d-pLHIL_io.d.cts → webhooks.d-D79qib9f.d.cts} +115 -5
  184. package/dist/{webhooks.d-pLHIL_io.d.ts → webhooks.d-D79qib9f.d.ts} +115 -5
  185. package/docs/app.md +244 -9
  186. package/docs/customer-portal.md +340 -113
  187. package/docs/entity.md +65 -0
  188. package/docs/integration-toolkit.md +334 -19
  189. package/docs/metering.md +570 -48
  190. package/docs/notes.md +19 -12
  191. package/docs/pricing.md +3 -1
  192. package/docs/webhooks.md +49 -0
  193. package/package.json +1 -1
  194. package/dist/app-5KSVSKDU.js +0 -7
  195. package/dist/app-PL3IOROO.cjs +0 -7
  196. package/dist/app-runtime-SSNHSBR7.cjs +0 -5
  197. package/dist/chunk-265TE2OV.js +0 -14
  198. package/dist/chunk-2MMSXUOL.cjs +0 -14
  199. package/dist/chunk-4ZMLRFDX.cjs +0 -14
  200. package/dist/chunk-A7AAVJGM.js +0 -14
  201. package/dist/chunk-GVNT76UU.js +0 -14
  202. package/dist/chunk-HHMXS5NC.cjs +0 -14
  203. package/dist/chunk-U4PUHKZC.js +0 -14
  204. package/dist/customer-portal-5LMHNBMD.cjs +0 -7
  205. package/dist/customer-portal-RZACUF4N.js +0 -7
  206. package/dist/entity-H6ESUH3Y.js +0 -7
  207. package/dist/entity-ZVDKWQSF.cjs +0 -7
  208. package/dist/integration-toolkit-FRPFOYT2.js +0 -7
  209. package/dist/integration-toolkit-VNMDI55O.cjs +0 -7
  210. package/dist/metering-DZNZPPY6.cjs +0 -7
  211. package/dist/metering-KOSCPPKB.js +0 -7
  212. package/dist/notes-3AO5GXIT.js +0 -7
  213. package/dist/notes-MPWOQC5J.cjs +0 -7
  214. package/dist/webhooks-H3RT33BF.cjs +0 -7
  215. package/dist/webhooks-RTOU3MS5.js +0 -7
@@ -2,16 +2,21 @@
2
2
  "openapi": "3.0.3",
3
3
  "info": {
4
4
  "title": "Metering API",
5
- "version": "1.0.0"
5
+ "version": "1.0.0",
6
+ "description": "The Metering API manages smart meter data, meter counters, and meter readings for epilot customers and administrators.\n\nIt supports two audiences:\n- **ECP (End Customer Portal)**: Portal users can view their meters, counters, and submit readings via the customer portal.\n- **ECP Admin**: Internal epilot users and ERP integrations can create, update, and bulk-manage meter readings.\n\nKey capabilities:\n- Retrieve meters and counters associated with a customer or contract\n- Submit individual or bulk meter readings (with optional validation skip)\n- Batch upsert/delete readings using the v2 endpoint\n- Query historical readings by date interval with cumulative or relative consumption modes\n- Retrieve allowed reading ranges to guide end customers entering readings\n"
6
7
  },
7
8
  "tags": [
8
9
  {
9
10
  "name": "ECP",
10
- "description": "APIs defined for a portal user"
11
+ "description": "APIs available to authenticated end customers via the Customer Portal (ECP).\nCustomers can view their meters, retrieve meter counters, and submit meter readings.\n"
11
12
  },
12
13
  {
13
14
  "name": "ECP Admin",
14
- "description": "APIs defined for a ECP Admin"
15
+ "description": "APIs available to epilot internal users and ERP integrations.\nAdministrators can create, update, delete, and bulk-manage meter readings, as well as trigger readings from journey submissions.\n"
16
+ },
17
+ {
18
+ "name": "Metering",
19
+ "description": "APIs for managing meter reading changesets. Changesets represent pending reading changes that require\napproval before being applied to ClickHouse.\n"
15
20
  },
16
21
  {
17
22
  "name": "meter_schema",
@@ -29,7 +34,8 @@
29
34
  "name": "APIs",
30
35
  "tags": [
31
36
  "ECP",
32
- "ECP Admin"
37
+ "ECP Admin",
38
+ "Metering"
33
39
  ]
34
40
  },
35
41
  {
@@ -55,8 +61,8 @@
55
61
  "/v1/metering/meter": {
56
62
  "get": {
57
63
  "operationId": "getCustomerMeters",
58
- "summary": "Get Customer Meters",
59
- "description": "Retrieves all meters related to a customer.",
64
+ "summary": "getCustomerMeters",
65
+ "description": "Retrieves all meters associated with the authenticated portal customer.\n\nReturns meter details along with any configured journey actions and the last meter reading timestamp and current consumption.\n",
60
66
  "tags": [
61
67
  "ECP"
62
68
  ],
@@ -65,6 +71,11 @@
65
71
  "PortalAuth": []
66
72
  }
67
73
  ],
74
+ "parameters": [
75
+ {
76
+ "$ref": "#/components/parameters/IncludePendingChangesetsQueryParam"
77
+ }
78
+ ],
68
79
  "responses": {
69
80
  "200": {
70
81
  "description": "Customer meters retrieved successfully.",
@@ -93,7 +104,7 @@
93
104
  "properties": {
94
105
  "last_reading": {
95
106
  "type": "string",
96
- "example": "2022-10-10T00:00:00.000Z",
107
+ "example": "2022-10-10",
97
108
  "description": "The timestamp of the last reading"
98
109
  },
99
110
  "current_consumption": {
@@ -130,7 +141,7 @@
130
141
  "get": {
131
142
  "operationId": "getMetersByContractId",
132
143
  "summary": "getMetersByContractId",
133
- "description": "Retrieves all meters related to a contract.",
144
+ "description": "Retrieves all meters associated with a given contract entity.\n\nUse this endpoint to display all meters linked to a customer's contract in the portal.\n",
134
145
  "tags": [
135
146
  "ECP"
136
147
  ],
@@ -187,8 +198,8 @@
187
198
  "/v1/metering/meter/{id}": {
188
199
  "patch": {
189
200
  "operationId": "updateMeter",
190
- "summary": "Update Meter",
191
- "description": "Updates the details of a meter.",
201
+ "summary": "updateMeter",
202
+ "description": "Partially updates the details of a meter entity by ID.",
192
203
  "tags": [
193
204
  "ECP"
194
205
  ],
@@ -249,8 +260,8 @@
249
260
  },
250
261
  "get": {
251
262
  "operationId": "getMeter",
252
- "summary": "Get Meter",
253
- "description": "Retrieves the details of a meter.",
263
+ "summary": "getMeter",
264
+ "description": "Retrieves the full details of a specific meter by ID, including related entities and available journey actions.\n\nThe response includes the meter entity, any configured journey action (e.g. a reading submission journey), and related entities such as contracts and contacts.\n",
254
265
  "tags": [
255
266
  "ECP"
256
267
  ],
@@ -318,8 +329,8 @@
318
329
  "/v1/metering/counter": {
319
330
  "get": {
320
331
  "operationId": "getMeterCounters",
321
- "summary": "Get Meter Counters",
322
- "description": "Retrieves all counters for a given meter.",
332
+ "summary": "getMeterCounters",
333
+ "description": "Retrieves all meter counters associated with a given meter.\n\nMeter counters represent individual measurement channels on a meter (e.g. HT/NT tariff channels, feed-in/feed-out directions).\n",
323
334
  "tags": [
324
335
  "ECP"
325
336
  ],
@@ -375,8 +386,8 @@
375
386
  "/v1/metering/counter/{counter_id}": {
376
387
  "get": {
377
388
  "operationId": "getCounterDetails",
378
- "summary": "Get Counter Details",
379
- "description": "Retrieves the details of a meter counter.",
389
+ "summary": "getCounterDetails",
390
+ "description": "Retrieves the full details of a single meter counter by its ID.\n\nCounter details include the OBIS number, direction, tariff type, transformer ratio, conversion factor, unit, and the last reading value and timestamp.\n",
380
391
  "tags": [
381
392
  "ECP"
382
393
  ],
@@ -430,8 +441,8 @@
430
441
  "/v1/metering/reading": {
431
442
  "post": {
432
443
  "operationId": "createMeterReading",
433
- "summary": "Create Meter Reading",
434
- "description": "Inserts a new meter reading.",
444
+ "summary": "createMeterReading",
445
+ "description": "Inserts a new meter reading.\n\nWhen `source: 'ERP'` is set in the request body, the write is treated as a direct confirmation (same as `?direct=true`). This triggers auto-clear of matching pending changesets.\n",
435
446
  "tags": [
436
447
  "ECP Admin",
437
448
  "ECP"
@@ -441,6 +452,11 @@
441
452
  "EitherAuth": []
442
453
  }
443
454
  ],
455
+ "parameters": [
456
+ {
457
+ "$ref": "#/components/parameters/DirectQueryParam"
458
+ }
459
+ ],
444
460
  "requestBody": {
445
461
  "description": "Meter reading payload.",
446
462
  "required": true,
@@ -448,6 +464,16 @@
448
464
  "application/json": {
449
465
  "schema": {
450
466
  "$ref": "#/components/schemas/MeterReading"
467
+ },
468
+ "example": {
469
+ "meter_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
470
+ "counter_id": "a1b2c3d4-5717-4562-b3fc-2c963f66afa6",
471
+ "direction": "feed-out",
472
+ "value": 1250.5,
473
+ "source": "360",
474
+ "timestamp": "2023-06-15T10:00:00.000Z",
475
+ "reason": "regular",
476
+ "read_by": "John Smith"
451
477
  }
452
478
  }
453
479
  }
@@ -464,6 +490,19 @@
464
490
  "$ref": "#/components/schemas/MeterReading"
465
491
  }
466
492
  }
493
+ },
494
+ "example": {
495
+ "data": {
496
+ "meter_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
497
+ "counter_id": "a1b2c3d4-5717-4562-b3fc-2c963f66afa6",
498
+ "direction": "feed-out",
499
+ "value": 1250.5,
500
+ "source": "360",
501
+ "timestamp": "2023-06-15T10:00:00.000Z",
502
+ "reason": "regular",
503
+ "read_by": "John Smith",
504
+ "status": "valid"
505
+ }
467
506
  }
468
507
  }
469
508
  }
@@ -486,8 +525,8 @@
486
525
  "/v1/metering/readings": {
487
526
  "post": {
488
527
  "operationId": "createMeterReadings",
489
- "summary": "Create Meter Readings",
490
- "description": "Inserts multiple meter readings at once. Limited to 100 readings per request.",
528
+ "summary": "createMeterReadings",
529
+ "description": "Inserts multiple meter readings at once. Limited to 100 readings per request.\n\nWhen `source: 'ERP'` is set in the request body, the write is treated as a direct confirmation (same as `?direct=true`). This triggers auto-clear of matching pending changesets.\n",
491
530
  "tags": [
492
531
  "ECP Admin"
493
532
  ],
@@ -512,6 +551,9 @@
512
551
  },
513
552
  {
514
553
  "$ref": "#/components/parameters/SkipValidationQueryParam"
554
+ },
555
+ {
556
+ "$ref": "#/components/parameters/DirectQueryParam"
515
557
  }
516
558
  ],
517
559
  "requestBody": {
@@ -571,7 +613,7 @@
571
613
  "post": {
572
614
  "operationId": "createPortalMeterReadings",
573
615
  "summary": "createPortalMeterReadings",
574
- "description": "Inserts multiple meter readings at once for a given meter. Limited to 2 readings per request.",
616
+ "description": "Inserts multiple meter readings at once for a given meter via the end customer portal.\nLimited to 100 readings per request.\n\nThis endpoint is designed for portal users submitting readings for a specific meter identified by path parameter.\n\nWhen `source: 'ERP'` is set in the request body, the write is treated as a direct confirmation (same as `?direct=true`). This triggers auto-clear of matching pending changesets.\n",
575
617
  "tags": [
576
618
  "ECP"
577
619
  ],
@@ -589,10 +631,13 @@
589
631
  },
590
632
  "required": true,
591
633
  "description": "The ID of the meter."
634
+ },
635
+ {
636
+ "$ref": "#/components/parameters/DirectQueryParam"
592
637
  }
593
638
  ],
594
639
  "requestBody": {
595
- "description": "Meter readings payload. Limited to 2 readings per request.",
640
+ "description": "Meter readings payload. Limited to 100 readings per request.",
596
641
  "required": true,
597
642
  "content": {
598
643
  "application/json": {
@@ -603,7 +648,8 @@
603
648
  "type": "array",
604
649
  "items": {
605
650
  "$ref": "#/components/schemas/PortalMeterReading"
606
- }
651
+ },
652
+ "minItems": 1
607
653
  }
608
654
  }
609
655
  }
@@ -647,8 +693,8 @@
647
693
  "/v2/metering/readings": {
648
694
  "post": {
649
695
  "operationId": "batchWriteMeterReadings",
650
- "summary": "Batch Write Readings",
651
- "description": "Upserts/Deletes multiple meter readings at once. Limited to 100 readings per request.",
696
+ "summary": "batchWriteMeterReadings",
697
+ "description": "Upserts or deletes multiple meter readings at once. Limited to 100 readings per request.\n\nUse the `operation` field on each reading to specify `create`, `update`, or `delete`.\nCustom `identifiers` can be provided to control how unique readings are matched (e.g. by `external_id` or `metadata` fields).\n\nWhen `source: 'ERP'` is set in the request body, the write is treated as a direct confirmation (same as `?direct=true`). This triggers auto-clear of matching pending changesets.\n",
652
698
  "tags": [
653
699
  "ECP Admin"
654
700
  ],
@@ -673,6 +719,9 @@
673
719
  },
674
720
  {
675
721
  "$ref": "#/components/parameters/ActivityIdQueryParam"
722
+ },
723
+ {
724
+ "$ref": "#/components/parameters/DirectQueryParam"
676
725
  }
677
726
  ],
678
727
  "requestBody": {
@@ -694,24 +743,7 @@
694
743
  "readings": {
695
744
  "type": "array",
696
745
  "items": {
697
- "allOf": [
698
- {
699
- "$ref": "#/components/schemas/MeterReading"
700
- },
701
- {
702
- "properties": {
703
- "operation": {
704
- "type": "string",
705
- "enum": [
706
- "create",
707
- "update",
708
- "delete"
709
- ],
710
- "default": "create"
711
- }
712
- }
713
- }
714
- ]
746
+ "$ref": "#/components/schemas/BatchReading"
715
747
  }
716
748
  }
717
749
  }
@@ -756,8 +788,8 @@
756
788
  "/v1/metering/reading/submission": {
757
789
  "post": {
758
790
  "operationId": "createMeterReadingFromSubmission",
759
- "summary": "Create Meter Reading from Submission",
760
- "description": "Creates a reading from a journey submission.",
791
+ "summary": "createMeterReadingFromSubmission",
792
+ "description": "Creates meter readings from a journey submission payload.\n\nThis endpoint is called internally by the journey automation engine when a customer submits meter readings through a journey. It parses the submission payload and stores the readings for the relevant meters.\n",
761
793
  "tags": [
762
794
  "ECP Admin"
763
795
  ],
@@ -840,7 +872,7 @@
840
872
  "get": {
841
873
  "operationId": "getAllowedReadingForMeter",
842
874
  "summary": "getAllowedReadingForMeter",
843
- "description": "Get allowed reading for the given meter",
875
+ "description": "Returns the allowed min/max reading range for each counter of the given meter.\n\nUse this endpoint to validate end-customer input before submitting a new reading.\nIf no timestamp is provided, the system defaults to the current time.\n",
844
876
  "tags": [
845
877
  "ECP Admin",
846
878
  "ECP"
@@ -921,8 +953,8 @@
921
953
  "/v1/metering/reading/with-meter": {
922
954
  "post": {
923
955
  "operationId": "createReadingWithMeter",
924
- "summary": "Create Reading with Meter",
925
- "description": "Creates a reading along with a meter.",
956
+ "summary": "createReadingWithMeter",
957
+ "description": "Creates a meter reading along with meter lookup or creation by MA-LO ID and OBIS number.\n\nIf a meter matching the provided `ma_lo_id` and `obis_number` already exists, the reading is added to it. Otherwise, a new meter counter is created as needed.\n",
926
958
  "tags": [
927
959
  "ECP Admin"
928
960
  ],
@@ -976,7 +1008,7 @@
976
1008
  "/v1/metering/reading/{meter_id}/{counter_id}": {
977
1009
  "get": {
978
1010
  "operationId": "getReadingsByInterval",
979
- "summary": "Get Readings by Interval",
1011
+ "summary": "getReadingsByInterval",
980
1012
  "description": "Retrieves all readings specified in an interval.\nIf the start_date and end_date are equal, then it returns the readings of the specified date.\nThe start_date should be less than or equal to the end_date.\n",
981
1013
  "tags": [
982
1014
  "ECP Admin",
@@ -1082,6 +1114,9 @@
1082
1114
  ]
1083
1115
  },
1084
1116
  "description": "If this value is provided as \"asc\", then the results will be sorted by the timestamp field in ascending order.\nIf this value is provided as \"desc\", then the results will be sorted by the timestamp field in descending order.\n"
1117
+ },
1118
+ {
1119
+ "$ref": "#/components/parameters/IncludePendingChangesetsQueryParam"
1085
1120
  }
1086
1121
  ],
1087
1122
  "responses": {
@@ -1127,8 +1162,8 @@
1127
1162
  },
1128
1163
  "put": {
1129
1164
  "operationId": "updateMeterReading",
1130
- "summary": "Update Meter Reading",
1131
- "description": "Updates a meter reading.",
1165
+ "summary": "updateMeterReading",
1166
+ "description": "Updates an existing meter reading identified by meter ID, counter ID, and timestamp.",
1132
1167
  "tags": [
1133
1168
  "ECP Admin"
1134
1169
  ],
@@ -1210,8 +1245,8 @@
1210
1245
  },
1211
1246
  "delete": {
1212
1247
  "operationId": "deleteMeterReading",
1213
- "summary": "Delete Meter Reading",
1214
- "description": "Deletes a meter reading.",
1248
+ "summary": "deleteMeterReading",
1249
+ "description": "Permanently deletes a meter reading identified by meter ID, counter ID, and timestamp.",
1215
1250
  "tags": [
1216
1251
  "ECP Admin"
1217
1252
  ],
@@ -1292,6 +1327,262 @@
1292
1327
  }
1293
1328
  }
1294
1329
  }
1330
+ },
1331
+ "/v1/metering/reading/{meter_id}/{counter_id}/changesets": {
1332
+ "get": {
1333
+ "operationId": "getReadingChangesets",
1334
+ "summary": "List pending reading changesets for a counter",
1335
+ "tags": [
1336
+ "Metering"
1337
+ ],
1338
+ "security": [
1339
+ {
1340
+ "EitherAuth": []
1341
+ }
1342
+ ],
1343
+ "parameters": [
1344
+ {
1345
+ "$ref": "#/components/parameters/MeterIdParam"
1346
+ },
1347
+ {
1348
+ "$ref": "#/components/parameters/CounterIdParam"
1349
+ }
1350
+ ],
1351
+ "responses": {
1352
+ "200": {
1353
+ "description": "Pending changesets for the counter",
1354
+ "content": {
1355
+ "application/json": {
1356
+ "schema": {
1357
+ "type": "object",
1358
+ "properties": {
1359
+ "changesets": {
1360
+ "type": "array",
1361
+ "items": {
1362
+ "$ref": "#/components/schemas/MeterReadingChangeset"
1363
+ }
1364
+ }
1365
+ }
1366
+ }
1367
+ }
1368
+ }
1369
+ },
1370
+ "401": {
1371
+ "$ref": "#/components/responses/Unauthorized"
1372
+ },
1373
+ "403": {
1374
+ "$ref": "#/components/responses/Forbidden"
1375
+ },
1376
+ "500": {
1377
+ "$ref": "#/components/responses/InternalServerError"
1378
+ }
1379
+ }
1380
+ }
1381
+ },
1382
+ "/v1/metering/reading/{meter_id}/{counter_id}/changesets/{changeset_id}:apply": {
1383
+ "post": {
1384
+ "operationId": "applyReadingChangeset",
1385
+ "summary": "Apply (approve) a pending reading changeset",
1386
+ "description": "Applies the proposed reading value to ClickHouse and removes the pending changeset.\n\nRequires `meter_reading:edit` permission (approval workflow action on the reading's approval state).\n",
1387
+ "tags": [
1388
+ "Metering"
1389
+ ],
1390
+ "security": [
1391
+ {
1392
+ "EpilotAuth": []
1393
+ }
1394
+ ],
1395
+ "parameters": [
1396
+ {
1397
+ "$ref": "#/components/parameters/MeterIdParam"
1398
+ },
1399
+ {
1400
+ "$ref": "#/components/parameters/CounterIdParam"
1401
+ },
1402
+ {
1403
+ "name": "changeset_id",
1404
+ "in": "path",
1405
+ "required": true,
1406
+ "schema": {
1407
+ "type": "string"
1408
+ }
1409
+ }
1410
+ ],
1411
+ "responses": {
1412
+ "200": {
1413
+ "description": "Changeset applied successfully",
1414
+ "content": {
1415
+ "application/json": {
1416
+ "schema": {
1417
+ "type": "object",
1418
+ "properties": {
1419
+ "reading": {
1420
+ "$ref": "#/components/schemas/Reading"
1421
+ },
1422
+ "changeset": {
1423
+ "$ref": "#/components/schemas/MeterReadingChangeset"
1424
+ }
1425
+ }
1426
+ }
1427
+ }
1428
+ }
1429
+ },
1430
+ "401": {
1431
+ "$ref": "#/components/responses/Unauthorized"
1432
+ },
1433
+ "403": {
1434
+ "$ref": "#/components/responses/Forbidden"
1435
+ },
1436
+ "404": {
1437
+ "description": "Changeset not found"
1438
+ },
1439
+ "500": {
1440
+ "$ref": "#/components/responses/InternalServerError"
1441
+ }
1442
+ }
1443
+ }
1444
+ },
1445
+ "/v1/metering/reading/{meter_id}/{counter_id}/changesets/{changeset_id}:dismiss": {
1446
+ "post": {
1447
+ "operationId": "dismissReadingChangeset",
1448
+ "summary": "Dismiss (reject) a pending reading changeset",
1449
+ "description": "Removes the pending changeset without applying it. The reading value remains unchanged.\n\nRequires `meter_reading:edit` permission (approval workflow action on the reading's approval state).\n",
1450
+ "tags": [
1451
+ "Metering"
1452
+ ],
1453
+ "security": [
1454
+ {
1455
+ "EpilotAuth": []
1456
+ }
1457
+ ],
1458
+ "parameters": [
1459
+ {
1460
+ "$ref": "#/components/parameters/MeterIdParam"
1461
+ },
1462
+ {
1463
+ "$ref": "#/components/parameters/CounterIdParam"
1464
+ },
1465
+ {
1466
+ "name": "changeset_id",
1467
+ "in": "path",
1468
+ "required": true,
1469
+ "schema": {
1470
+ "type": "string"
1471
+ }
1472
+ }
1473
+ ],
1474
+ "requestBody": {
1475
+ "content": {
1476
+ "application/json": {
1477
+ "schema": {
1478
+ "type": "object",
1479
+ "properties": {
1480
+ "reason": {
1481
+ "type": "string",
1482
+ "description": "Optional reason for dismissing the changeset"
1483
+ }
1484
+ }
1485
+ }
1486
+ }
1487
+ }
1488
+ },
1489
+ "responses": {
1490
+ "200": {
1491
+ "description": "Changeset dismissed successfully",
1492
+ "content": {
1493
+ "application/json": {
1494
+ "schema": {
1495
+ "$ref": "#/components/schemas/MeterReadingChangeset"
1496
+ }
1497
+ }
1498
+ }
1499
+ },
1500
+ "401": {
1501
+ "$ref": "#/components/responses/Unauthorized"
1502
+ },
1503
+ "403": {
1504
+ "$ref": "#/components/responses/Forbidden"
1505
+ },
1506
+ "404": {
1507
+ "description": "Changeset not found"
1508
+ },
1509
+ "500": {
1510
+ "$ref": "#/components/responses/InternalServerError"
1511
+ }
1512
+ }
1513
+ }
1514
+ },
1515
+ "/v1/metering/reading/{meter_id}/{counter_id}/changesets/{changeset_id}": {
1516
+ "patch": {
1517
+ "operationId": "updateReadingChangeset",
1518
+ "summary": "Edit a pending reading changeset",
1519
+ "description": "Updates the proposed value of a pending changeset without going through the normal write path.\n",
1520
+ "tags": [
1521
+ "Metering"
1522
+ ],
1523
+ "security": [
1524
+ {
1525
+ "EpilotAuth": []
1526
+ }
1527
+ ],
1528
+ "parameters": [
1529
+ {
1530
+ "$ref": "#/components/parameters/MeterIdParam"
1531
+ },
1532
+ {
1533
+ "$ref": "#/components/parameters/CounterIdParam"
1534
+ },
1535
+ {
1536
+ "name": "changeset_id",
1537
+ "in": "path",
1538
+ "required": true,
1539
+ "schema": {
1540
+ "type": "string"
1541
+ }
1542
+ }
1543
+ ],
1544
+ "requestBody": {
1545
+ "content": {
1546
+ "application/json": {
1547
+ "schema": {
1548
+ "type": "object",
1549
+ "properties": {
1550
+ "proposed": {
1551
+ "$ref": "#/components/schemas/ProposedReading"
1552
+ }
1553
+ }
1554
+ }
1555
+ }
1556
+ }
1557
+ },
1558
+ "responses": {
1559
+ "200": {
1560
+ "description": "Changeset updated",
1561
+ "content": {
1562
+ "application/json": {
1563
+ "schema": {
1564
+ "$ref": "#/components/schemas/MeterReadingChangeset"
1565
+ }
1566
+ }
1567
+ }
1568
+ },
1569
+ "400": {
1570
+ "$ref": "#/components/responses/InvalidRequest"
1571
+ },
1572
+ "401": {
1573
+ "$ref": "#/components/responses/Unauthorized"
1574
+ },
1575
+ "403": {
1576
+ "$ref": "#/components/responses/Forbidden"
1577
+ },
1578
+ "404": {
1579
+ "description": "Changeset not found"
1580
+ },
1581
+ "500": {
1582
+ "$ref": "#/components/responses/InternalServerError"
1583
+ }
1584
+ }
1585
+ }
1295
1586
  }
1296
1587
  },
1297
1588
  "components": {
@@ -1738,7 +2029,7 @@
1738
2029
  },
1739
2030
  "calibration_date": {
1740
2031
  "type": "string",
1741
- "example": "2022-10-10T00:00:00.000Z",
2032
+ "example": "2022-10-10",
1742
2033
  "description": "The calibration date of the meter"
1743
2034
  },
1744
2035
  "contract": {
@@ -1854,7 +2145,7 @@
1854
2145
  "timestamp": {
1855
2146
  "type": "string",
1856
2147
  "description": "If the value is not provided, the system will be set with the time the request is processed.",
1857
- "example": "2022-10-10T00:00:00.000Z",
2148
+ "example": "2022-10-10",
1858
2149
  "format": "date-time"
1859
2150
  },
1860
2151
  "source": {
@@ -1920,6 +2211,142 @@
1920
2211
  }
1921
2212
  ]
1922
2213
  },
2214
+ "BatchReadingBase": {
2215
+ "type": "object",
2216
+ "description": "Base properties shared by all batch reading operations",
2217
+ "properties": {
2218
+ "meter_id": {
2219
+ "$ref": "#/components/schemas/EntityId",
2220
+ "description": "The ID of the associated meter"
2221
+ },
2222
+ "counter_id": {
2223
+ "$ref": "#/components/schemas/EntityId",
2224
+ "description": "The ID of the associated meter counter"
2225
+ },
2226
+ "direction": {
2227
+ "$ref": "#/components/schemas/Direction",
2228
+ "description": "The direction of the reading (feed-in or feed-out)"
2229
+ },
2230
+ "timestamp": {
2231
+ "type": "string",
2232
+ "description": "The timestamp of the reading. If not provided, the system will use the current time.",
2233
+ "example": "2022-10-10T10:00:00Z",
2234
+ "format": "date-time"
2235
+ },
2236
+ "external_id": {
2237
+ "type": "string",
2238
+ "description": "The external ID of the reading"
2239
+ },
2240
+ "metadata": {
2241
+ "type": "object",
2242
+ "additionalProperties": {
2243
+ "type": "string"
2244
+ },
2245
+ "description": "Additional metadata for the reading",
2246
+ "example": {
2247
+ "registration_id": "1234567890",
2248
+ "business_unit": "ABC"
2249
+ }
2250
+ }
2251
+ }
2252
+ },
2253
+ "CreateOrUpdateBatchReading": {
2254
+ "description": "Schema for create or update operations - requires value, source, and meter_id",
2255
+ "allOf": [
2256
+ {
2257
+ "$ref": "#/components/schemas/BatchReadingBase"
2258
+ },
2259
+ {
2260
+ "type": "object",
2261
+ "required": [
2262
+ "value",
2263
+ "source",
2264
+ "meter_id"
2265
+ ],
2266
+ "properties": {
2267
+ "operation": {
2268
+ "type": "string",
2269
+ "enum": [
2270
+ "create",
2271
+ "update"
2272
+ ],
2273
+ "default": "create",
2274
+ "description": "The operation to perform. Defaults to \"create\" if omitted."
2275
+ },
2276
+ "value": {
2277
+ "type": "number",
2278
+ "example": 240,
2279
+ "description": "The reading value of the meter"
2280
+ },
2281
+ "source": {
2282
+ "$ref": "#/components/schemas/Source",
2283
+ "description": "The source of the reading"
2284
+ },
2285
+ "read_by": {
2286
+ "$ref": "#/components/schemas/ReadBy"
2287
+ },
2288
+ "reason": {
2289
+ "$ref": "#/components/schemas/Reason"
2290
+ },
2291
+ "status": {
2292
+ "$ref": "#/components/schemas/ReadingStatus",
2293
+ "description": "The status of the reading"
2294
+ },
2295
+ "remark": {
2296
+ "type": "string",
2297
+ "nullable": true,
2298
+ "description": "A remark or comment for the reading",
2299
+ "example": "Customer reported unusual consumption"
2300
+ },
2301
+ "note": {
2302
+ "type": "string",
2303
+ "description": "Notes to record a meter reading"
2304
+ },
2305
+ "unit": {
2306
+ "$ref": "#/components/schemas/Unit",
2307
+ "description": "The unit of measurement for the reading"
2308
+ }
2309
+ }
2310
+ }
2311
+ ]
2312
+ },
2313
+ "DeleteBatchReading": {
2314
+ "description": "Schema for delete operations - only requires identifier fields specified in the identifiers parameter",
2315
+ "allOf": [
2316
+ {
2317
+ "$ref": "#/components/schemas/BatchReadingBase"
2318
+ },
2319
+ {
2320
+ "type": "object",
2321
+ "required": [
2322
+ "operation"
2323
+ ],
2324
+ "properties": {
2325
+ "operation": {
2326
+ "type": "string",
2327
+ "enum": [
2328
+ "delete"
2329
+ ],
2330
+ "description": "The operation to perform (must be \"delete\")"
2331
+ }
2332
+ }
2333
+ }
2334
+ ]
2335
+ },
2336
+ "BatchReading": {
2337
+ "description": "A meter reading for batch operations. The required fields depend on the operation:\n- create/update: requires value, source, and meter_id\n- delete: only requires the fields specified in the identifiers parameter\n",
2338
+ "oneOf": [
2339
+ {
2340
+ "$ref": "#/components/schemas/CreateOrUpdateBatchReading"
2341
+ },
2342
+ {
2343
+ "$ref": "#/components/schemas/DeleteBatchReading"
2344
+ }
2345
+ ],
2346
+ "discriminator": {
2347
+ "propertyName": "operation"
2348
+ }
2349
+ },
1923
2350
  "UpdateMeterReading": {
1924
2351
  "type": "object",
1925
2352
  "required": [
@@ -1954,7 +2381,7 @@
1954
2381
  "timestamp": {
1955
2382
  "type": "string",
1956
2383
  "description": "If the value is not provided, the system will be set with the time the request is processed.",
1957
- "example": "2022-10-10T00:00:00.000Z",
2384
+ "example": "2022-10-10",
1958
2385
  "format": "date-time"
1959
2386
  },
1960
2387
  "source": {
@@ -2033,7 +2460,7 @@
2033
2460
  },
2034
2461
  "forecast_as_of": {
2035
2462
  "type": "string",
2036
- "example": "2022-12-10T00:00:00.000Z",
2463
+ "example": "2022-12-10",
2037
2464
  "description": "The date as of which the forecast reading value is applicable"
2038
2465
  },
2039
2466
  "current_consumption": {
@@ -2043,7 +2470,7 @@
2043
2470
  },
2044
2471
  "last_reading": {
2045
2472
  "type": "string",
2046
- "example": "2022-10-10T00:00:00.000Z",
2473
+ "example": "2022-10-10",
2047
2474
  "description": "The timestamp of the last reading"
2048
2475
  },
2049
2476
  "conversion_factor": {
@@ -2167,7 +2594,8 @@
2167
2594
  }
2168
2595
  },
2169
2596
  "Unit": {
2170
- "type": "string"
2597
+ "type": "string",
2598
+ "deprecated": true
2171
2599
  },
2172
2600
  "Source": {
2173
2601
  "type": "string",
@@ -2284,6 +2712,208 @@
2284
2712
  }
2285
2713
  }
2286
2714
  },
2715
+ "MeterReadingChangeset": {
2716
+ "type": "object",
2717
+ "required": [
2718
+ "changeset_id",
2719
+ "proposed",
2720
+ "edit_mode",
2721
+ "created_at"
2722
+ ],
2723
+ "properties": {
2724
+ "changeset_id": {
2725
+ "type": "string",
2726
+ "description": "Unique changeset identifier (UUID v4)"
2727
+ },
2728
+ "meter_id": {
2729
+ "type": "string",
2730
+ "format": "uuid"
2731
+ },
2732
+ "counter_id": {
2733
+ "type": "string",
2734
+ "format": "uuid"
2735
+ },
2736
+ "proposed": {
2737
+ "$ref": "#/components/schemas/ProposedReading"
2738
+ },
2739
+ "previous": {
2740
+ "$ref": "#/components/schemas/ProposedReading",
2741
+ "description": "Snapshot of the reading at changeset creation time"
2742
+ },
2743
+ "edit_mode": {
2744
+ "type": "string",
2745
+ "enum": [
2746
+ "external",
2747
+ "approval"
2748
+ ]
2749
+ },
2750
+ "match_strategy": {
2751
+ "type": "string",
2752
+ "enum": [
2753
+ "exact",
2754
+ "fuzzy"
2755
+ ],
2756
+ "default": "exact"
2757
+ },
2758
+ "timestamp_tolerance": {
2759
+ "$ref": "#/components/schemas/TimestampTolerance"
2760
+ },
2761
+ "created_at": {
2762
+ "type": "string",
2763
+ "format": "date-time"
2764
+ },
2765
+ "created_by": {
2766
+ "$ref": "#/components/schemas/ChangesetCreator"
2767
+ },
2768
+ "source": {
2769
+ "type": "string",
2770
+ "enum": [
2771
+ "360",
2772
+ "ECP",
2773
+ "ERP",
2774
+ "journey-submission"
2775
+ ]
2776
+ },
2777
+ "fuzzy_config": {
2778
+ "$ref": "#/components/schemas/FuzzyConfig"
2779
+ },
2780
+ "dismissed_reason": {
2781
+ "type": "string",
2782
+ "description": "Reason for dismissing the changeset. Only present in the dismiss HTTP response\nand on the unified `MeterReadings` EventBridge event payload (when the\n`activity_type` discriminator is `ChangesetDismissed`). Not persisted —\nthe event stream is the authoritative audit trail (aligned with entity-api's\napproach where dismissal context lives in the activity record).\n"
2783
+ },
2784
+ "dismissed_at": {
2785
+ "type": "string",
2786
+ "format": "date-time",
2787
+ "description": "ISO 8601 timestamp of when the changeset was dismissed. Like dismissed_reason,\nthis is only present in the dismiss response and EventBridge event, not persisted.\n"
2788
+ }
2789
+ }
2790
+ },
2791
+ "FuzzyConfig": {
2792
+ "type": "object",
2793
+ "description": "Numeric-threshold fuzzy matching for meter reading auto-clear.\n\nNOTE: This is intentionally different from entity-api's FuzzyConfig. Entity-api's\nfuzzy strategies (suffix, digits_only, normalize_phone, ignore_fields,\ncontains_entry, regex) are designed for strings and structured objects (IBAN, phone\nnumbers, arrays of relations). Meter readings are purely numeric, so numeric\ntolerance (percentage + absolute) is the right primitive. Do not try to unify\nthese two types — they serve different domains.\n\nWhen match_strategy is 'fuzzy', the auto-clear logic uses these thresholds to\ndecide whether an incoming ERP value is \"close enough\" to the proposed value.\nIf both percentage_threshold and absolute_threshold are provided, a match\nsucceeds if either threshold is satisfied (logical OR).\n",
2794
+ "properties": {
2795
+ "percentage_threshold": {
2796
+ "type": "number",
2797
+ "description": "Percentage threshold (0-1). 0.01 = 1%. Default 0.01.",
2798
+ "minimum": 0,
2799
+ "maximum": 1,
2800
+ "default": 0.01
2801
+ },
2802
+ "absolute_threshold": {
2803
+ "type": "number",
2804
+ "description": "Absolute threshold in the reading's unit.",
2805
+ "minimum": 0
2806
+ }
2807
+ }
2808
+ },
2809
+ "ProposedReading": {
2810
+ "type": "object",
2811
+ "required": [
2812
+ "value"
2813
+ ],
2814
+ "properties": {
2815
+ "value": {
2816
+ "type": "number"
2817
+ },
2818
+ "direction": {
2819
+ "type": "string",
2820
+ "enum": [
2821
+ "feed-in",
2822
+ "feed-out"
2823
+ ]
2824
+ },
2825
+ "timestamp": {
2826
+ "type": "string",
2827
+ "format": "date-time"
2828
+ },
2829
+ "reason": {
2830
+ "type": "string"
2831
+ },
2832
+ "remark": {
2833
+ "type": "string"
2834
+ },
2835
+ "read_by": {
2836
+ "type": "string"
2837
+ },
2838
+ "status": {
2839
+ "type": "string",
2840
+ "enum": [
2841
+ "valid",
2842
+ "in-validation",
2843
+ "implausible"
2844
+ ]
2845
+ },
2846
+ "external_id": {
2847
+ "type": "string"
2848
+ }
2849
+ }
2850
+ },
2851
+ "ChangesetCreator": {
2852
+ "type": "object",
2853
+ "properties": {
2854
+ "type": {
2855
+ "type": "string",
2856
+ "enum": [
2857
+ "user",
2858
+ "portal_user",
2859
+ "api_client",
2860
+ "automation"
2861
+ ]
2862
+ },
2863
+ "id": {
2864
+ "type": "string"
2865
+ }
2866
+ }
2867
+ },
2868
+ "TimestampTolerance": {
2869
+ "description": "Slack on `reading.timestamp` when auto-clear matches an incoming reading\nagainst a pending changeset. Both sides reference the SAME physical\nmeter-read event — one as stored when the user submitted, the other as\nechoed back by the ERP. The tolerance accommodates round-trip format\ndrift between the two writes.\n\nVariants:\n- `'exact'`: strict millisecond equality. Right when both sides\n round-trip the timestamp cleanly.\n- `{ type: 'same-day', timezone? }`: strip the time component and\n compare year-month-day. Use this when the ERP emits date-only\n readings (the inbound pipeline promotes those to midnight UTC).\n Optional `timezone` is an IANA name (e.g. `'Europe/Berlin'`); the\n day is bucketed in that zone. Defaults to UTC when omitted. Set\n a local zone for tenants whose readings are taken near midnight\n in non-UTC timezones — UTC bucketing would otherwise split such\n readings across two day-buckets and miss matches.\n- `{ type: 'within-seconds', seconds }`: symmetric ±N-second\n window. Use for sub-minute normalization drift (`seconds: 60`).\n",
2870
+ "oneOf": [
2871
+ {
2872
+ "type": "string",
2873
+ "enum": [
2874
+ "exact"
2875
+ ]
2876
+ },
2877
+ {
2878
+ "type": "object",
2879
+ "required": [
2880
+ "type"
2881
+ ],
2882
+ "properties": {
2883
+ "type": {
2884
+ "type": "string",
2885
+ "enum": [
2886
+ "same-day"
2887
+ ]
2888
+ },
2889
+ "timezone": {
2890
+ "type": "string",
2891
+ "description": "IANA timezone identifier (e.g. `'Europe/Berlin'`, `'UTC'`).\nWhen omitted, the day is bucketed in UTC.\n"
2892
+ }
2893
+ }
2894
+ },
2895
+ {
2896
+ "type": "object",
2897
+ "required": [
2898
+ "type",
2899
+ "seconds"
2900
+ ],
2901
+ "properties": {
2902
+ "type": {
2903
+ "type": "string",
2904
+ "enum": [
2905
+ "within-seconds"
2906
+ ]
2907
+ },
2908
+ "seconds": {
2909
+ "type": "integer",
2910
+ "minimum": 0,
2911
+ "description": "Tolerance in seconds. e.g. 60 = ±1 minute, 3600 = ±1 hour."
2912
+ }
2913
+ }
2914
+ }
2915
+ ]
2916
+ },
2287
2917
  "ActivityId": {
2288
2918
  "type": "string",
2289
2919
  "format": "ulid",
@@ -2292,6 +2922,44 @@
2292
2922
  }
2293
2923
  },
2294
2924
  "parameters": {
2925
+ "MeterIdParam": {
2926
+ "name": "meter_id",
2927
+ "in": "path",
2928
+ "required": true,
2929
+ "schema": {
2930
+ "$ref": "#/components/schemas/Id"
2931
+ },
2932
+ "description": "The ID of the meter."
2933
+ },
2934
+ "CounterIdParam": {
2935
+ "name": "counter_id",
2936
+ "in": "path",
2937
+ "required": true,
2938
+ "schema": {
2939
+ "$ref": "#/components/schemas/Id"
2940
+ },
2941
+ "description": "The ID of the counter."
2942
+ },
2943
+ "DirectQueryParam": {
2944
+ "name": "direct",
2945
+ "in": "query",
2946
+ "required": false,
2947
+ "description": "When true, bypasses changeset interception and writes directly to ClickHouse. Used by trusted integrations (e.g., ERP sync) to confirm changes and auto-clear matching pending changesets.",
2948
+ "schema": {
2949
+ "type": "boolean",
2950
+ "default": false
2951
+ }
2952
+ },
2953
+ "IncludePendingChangesetsQueryParam": {
2954
+ "name": "include_pending_changesets",
2955
+ "in": "query",
2956
+ "required": false,
2957
+ "description": "When true, the response includes a `pending_changesets` field with the list of pending reading changesets alongside the confirmed readings.",
2958
+ "schema": {
2959
+ "type": "boolean",
2960
+ "default": false
2961
+ }
2962
+ },
2295
2963
  "ActivityIdQueryParam": {
2296
2964
  "name": "activity_id",
2297
2965
  "description": "Activity to include in event feed",
@@ -2313,9 +2981,5 @@
2313
2981
  }
2314
2982
  }
2315
2983
  },
2316
- "servers": [
2317
- {
2318
- "url": "https://metering.sls.epilot.io"
2319
- }
2320
- ]
2984
+ "servers": []
2321
2985
  }