@epilot/cli 0.1.60 → 0.1.62

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.
@@ -2190,6 +2190,170 @@
2190
2190
  }
2191
2191
  }
2192
2192
  },
2193
+ "/v3/portal/email-templates:migrate-references": {
2194
+ "post": {
2195
+ "operationId": "migrateEmailTemplateReferences",
2196
+ "summary": "migrateEmailTemplateReferences",
2197
+ "description": "Walk every email-template config row in the caller's org and re-point any\nfield on `email_templates` that currently references `source_template_id`\nat `destination_template_id`. Intended to be called from the email-template\nmigration flow when a duplicated template is refined.\n\nOnly v3-shaped rows are migrated (those carrying `portal_sk_v3`). Returns\nthe portal IDs that were rewritten and any whose update failed.\n",
2198
+ "tags": [
2199
+ "ECP Admin"
2200
+ ],
2201
+ "security": [
2202
+ {
2203
+ "EpilotAuth": []
2204
+ }
2205
+ ],
2206
+ "requestBody": {
2207
+ "description": "Source and destination template ids",
2208
+ "required": true,
2209
+ "content": {
2210
+ "application/json": {
2211
+ "schema": {
2212
+ "type": "object",
2213
+ "required": [
2214
+ "source_template_id",
2215
+ "destination_template_id"
2216
+ ],
2217
+ "properties": {
2218
+ "source_template_id": {
2219
+ "type": "string",
2220
+ "description": "Template id currently referenced on portal rows"
2221
+ },
2222
+ "destination_template_id": {
2223
+ "type": "string",
2224
+ "description": "Template id to write in place of the source"
2225
+ }
2226
+ }
2227
+ }
2228
+ }
2229
+ }
2230
+ },
2231
+ "responses": {
2232
+ "200": {
2233
+ "description": "Migration completed (may have partial failures in failed_portal_ids).",
2234
+ "content": {
2235
+ "application/json": {
2236
+ "schema": {
2237
+ "type": "object",
2238
+ "required": [
2239
+ "migrated_portal_count",
2240
+ "migrated_portal_ids",
2241
+ "failed_portal_ids"
2242
+ ],
2243
+ "properties": {
2244
+ "migrated_portal_count": {
2245
+ "type": "integer",
2246
+ "example": 2
2247
+ },
2248
+ "migrated_portal_ids": {
2249
+ "type": "array",
2250
+ "items": {
2251
+ "type": "string"
2252
+ }
2253
+ },
2254
+ "failed_portal_ids": {
2255
+ "type": "array",
2256
+ "items": {
2257
+ "type": "string"
2258
+ }
2259
+ }
2260
+ }
2261
+ }
2262
+ }
2263
+ }
2264
+ },
2265
+ "401": {
2266
+ "$ref": "#/components/responses/Unauthorized"
2267
+ },
2268
+ "403": {
2269
+ "$ref": "#/components/responses/Forbidden"
2270
+ },
2271
+ "500": {
2272
+ "$ref": "#/components/responses/InternalServerError"
2273
+ }
2274
+ }
2275
+ }
2276
+ },
2277
+ "/v3/portal/email-templates:list-references": {
2278
+ "post": {
2279
+ "operationId": "listEmailTemplateReferences",
2280
+ "summary": "listEmailTemplateReferences",
2281
+ "description": "Read-only sibling of migrateEmailTemplateReferences. Lists every portal in\nthe caller's org whose `email_templates` config references `template_id`,\nwithout rewriting anything. Used by the email-template MFE to show which\nportals a template affects (in template settings and as a pre-migrate\npreview). Uses the same discovery as the migrate path.\n",
2282
+ "tags": [
2283
+ "ECP Admin"
2284
+ ],
2285
+ "security": [
2286
+ {
2287
+ "EpilotAuth": []
2288
+ }
2289
+ ],
2290
+ "requestBody": {
2291
+ "description": "Template id to look up references for",
2292
+ "required": true,
2293
+ "content": {
2294
+ "application/json": {
2295
+ "schema": {
2296
+ "type": "object",
2297
+ "required": [
2298
+ "template_id"
2299
+ ],
2300
+ "properties": {
2301
+ "template_id": {
2302
+ "type": "string",
2303
+ "description": "Email template id to find portal references for"
2304
+ }
2305
+ }
2306
+ }
2307
+ }
2308
+ }
2309
+ },
2310
+ "responses": {
2311
+ "200": {
2312
+ "description": "Portals referencing the template.",
2313
+ "content": {
2314
+ "application/json": {
2315
+ "schema": {
2316
+ "type": "object",
2317
+ "required": [
2318
+ "portals"
2319
+ ],
2320
+ "properties": {
2321
+ "portals": {
2322
+ "type": "array",
2323
+ "items": {
2324
+ "type": "object",
2325
+ "required": [
2326
+ "id"
2327
+ ],
2328
+ "properties": {
2329
+ "id": {
2330
+ "type": "string"
2331
+ },
2332
+ "name": {
2333
+ "type": "string",
2334
+ "nullable": true,
2335
+ "description": "Portal display name (or domain); falls back to id when unavailable"
2336
+ }
2337
+ }
2338
+ }
2339
+ }
2340
+ }
2341
+ }
2342
+ }
2343
+ }
2344
+ },
2345
+ "401": {
2346
+ "$ref": "#/components/responses/Unauthorized"
2347
+ },
2348
+ "403": {
2349
+ "$ref": "#/components/responses/Forbidden"
2350
+ },
2351
+ "500": {
2352
+ "$ref": "#/components/responses/InternalServerError"
2353
+ }
2354
+ }
2355
+ }
2356
+ },
2193
2357
  "/v3/portal/email-templates/{portal_id}": {
2194
2358
  "post": {
2195
2359
  "operationId": "upsertEmailTemplatesByPortalId",
@@ -3831,9 +3995,8 @@
3831
3995
  "properties": {
3832
3996
  "message": {
3833
3997
  "type": "string",
3834
- "enum": [
3835
- "User Succesfully Deleted"
3836
- ]
3998
+ "description": "`User Succesfully Deleted` when the user was deleted, or `Account deletion requested`\nwhen an asynchronous deleteAccount portal extension hook handed the deletion over to a third party.\n",
3999
+ "example": "User Succesfully Deleted"
3837
4000
  },
3838
4001
  "data": {
3839
4002
  "$ref": "#/components/schemas/EntityId"
@@ -3873,8 +4036,7 @@
3873
4036
  "schema": {
3874
4037
  "type": "object",
3875
4038
  "required": [
3876
- "email",
3877
- "password"
4039
+ "email"
3878
4040
  ],
3879
4041
  "properties": {
3880
4042
  "email": {
@@ -3884,7 +4046,7 @@
3884
4046
  },
3885
4047
  "password": {
3886
4048
  "type": "string",
3887
- "description": "Password of the portal user for confirmation"
4049
+ "description": "Password of the portal user for confirmation.\nRequired unless a `changeEmail` portal extension hook with `require_password_confirmation` disabled is configured for the portal.\n"
3888
4050
  }
3889
4051
  }
3890
4052
  }
@@ -3901,9 +4063,66 @@
3901
4063
  "properties": {
3902
4064
  "message": {
3903
4065
  "type": "string",
3904
- "enum": [
3905
- "You will receive a confirmation mail soon on your updated email address."
3906
- ]
4066
+ "description": "`You will receive a confirmation mail soon on your updated email address.` for the built-in flow,\nor `Your email change request has been received.` when a changeEmail portal extension hook handed\nthe change over to a third party.\n",
4067
+ "example": "You will receive a confirmation mail soon on your updated email address."
4068
+ }
4069
+ }
4070
+ }
4071
+ }
4072
+ }
4073
+ },
4074
+ "400": {
4075
+ "$ref": "#/components/responses/InvalidRequest"
4076
+ },
4077
+ "401": {
4078
+ "$ref": "#/components/responses/Unauthorized"
4079
+ },
4080
+ "500": {
4081
+ "$ref": "#/components/responses/InternalServerError"
4082
+ }
4083
+ }
4084
+ }
4085
+ },
4086
+ "/v2/portal/user/change/password": {
4087
+ "put": {
4088
+ "operationId": "changePortalUserPassword",
4089
+ "summary": "changePortalUserPassword",
4090
+ "description": "Hand over a password change to the third-party system configured via the `changePassword` portal extension hook.\nOnly available when such a hook is configured for the portal; the built-in password change flow does not use this endpoint.\n",
4091
+ "tags": [
4092
+ "ECP"
4093
+ ],
4094
+ "security": [
4095
+ {
4096
+ "PortalAuth": []
4097
+ }
4098
+ ],
4099
+ "requestBody": {
4100
+ "description": "Request payload",
4101
+ "required": false,
4102
+ "content": {
4103
+ "application/json": {
4104
+ "schema": {
4105
+ "type": "object",
4106
+ "properties": {
4107
+ "new_password": {
4108
+ "type": "string",
4109
+ "description": "New password chosen by the portal user.\nRequired when the configured `changePassword` hook has `require_new_password` enabled, ignored otherwise.\n"
4110
+ }
4111
+ }
4112
+ }
4113
+ }
4114
+ }
4115
+ },
4116
+ "responses": {
4117
+ "200": {
4118
+ "description": "The password change request was handed over to the third-party system.",
4119
+ "content": {
4120
+ "application/json": {
4121
+ "schema": {
4122
+ "type": "object",
4123
+ "properties": {
4124
+ "message": {
4125
+ "type": "string"
3907
4126
  }
3908
4127
  }
3909
4128
  }
@@ -3916,6 +4135,9 @@
3916
4135
  "401": {
3917
4136
  "$ref": "#/components/responses/Unauthorized"
3918
4137
  },
4138
+ "404": {
4139
+ "description": "No changePassword portal extension hook is configured for this portal."
4140
+ },
3919
4141
  "500": {
3920
4142
  "$ref": "#/components/responses/InternalServerError"
3921
4143
  }
@@ -7034,91 +7256,241 @@
7034
7256
  }
7035
7257
  }
7036
7258
  },
7037
- "/v2/portal/can-trigger-portal-flow": {
7259
+ "/v2/portal/entity/{slug}": {
7038
7260
  "post": {
7039
- "operationId": "canTriggerPortalFlow",
7040
- "summary": "canTriggerPortalFlow",
7041
- "description": "Returns whether the user can trigger a portal flow",
7261
+ "operationId": "createPortalUserEntity",
7262
+ "summary": "createPortalUserEntity",
7263
+ "description": "**EXPERIMENTAL do not rely on this endpoint.** It is unstable, currently limited to the `asset` schema, and may change or be removed without notice; third parties must not build on it yet.\nCreate a single entity on behalf of a portal user. The schema slug is passed in the path and must be one of the supported (experimental) schemas; field-level permissions are enforced by the caller's role grants. The request body is the entity to create (its attributes); the created entity is automatically related to the caller's contact.",
7264
+ "deprecated": true,
7042
7265
  "tags": [
7043
- "ECP Admin"
7266
+ "ECP"
7044
7267
  ],
7045
7268
  "security": [
7046
7269
  {
7047
- "EpilotAuth": []
7270
+ "PortalAuth": []
7048
7271
  }
7049
7272
  ],
7050
7273
  "parameters": [
7051
7274
  {
7052
- "in": "query",
7053
- "name": "origin",
7054
- "required": false,
7055
- "deprecated": true,
7056
- "schema": {
7057
- "$ref": "#/components/schemas/Origin"
7058
- },
7059
- "description": "Origin of the portal"
7060
- },
7061
- {
7062
- "in": "query",
7063
- "name": "portal_id",
7275
+ "in": "path",
7276
+ "name": "slug",
7277
+ "description": "Entity schema slug to create. Limited to the supported (experimental) schemas, currently `asset`.",
7064
7278
  "required": true,
7065
7279
  "schema": {
7066
7280
  "type": "string",
7067
- "description": "Portal ID",
7068
- "example": "123"
7281
+ "enum": [
7282
+ "asset"
7283
+ ],
7284
+ "example": "asset"
7069
7285
  }
7070
7286
  }
7071
7287
  ],
7072
7288
  "requestBody": {
7073
- "description": "Request of trigger portal flow",
7074
7289
  "required": true,
7075
7290
  "content": {
7076
7291
  "application/json": {
7077
7292
  "schema": {
7078
- "$ref": "#/components/schemas/TriggerPortalFlow"
7293
+ "$ref": "#/components/schemas/Entity"
7294
+ },
7295
+ "example": {
7296
+ "title": "PV Inverter",
7297
+ "manufacturer": "SMA",
7298
+ "external_id": "device-123"
7079
7299
  }
7080
7300
  }
7081
7301
  }
7082
7302
  },
7083
7303
  "responses": {
7084
- "200": {
7085
- "description": "Can Trigger Portal Flow",
7304
+ "201": {
7305
+ "description": "The entity has been created successfully for the portal user.",
7086
7306
  "content": {
7087
7307
  "application/json": {
7088
7308
  "schema": {
7089
- "type": "object",
7090
- "properties": {
7091
- "can_trigger": {
7092
- "type": "boolean",
7093
- "description": "Whether the flow can be triggered",
7094
- "example": true
7095
- }
7096
- }
7309
+ "$ref": "#/components/schemas/EntityResponse"
7097
7310
  }
7098
7311
  }
7099
7312
  }
7313
+ },
7314
+ "400": {
7315
+ "$ref": "#/components/responses/InvalidRequest"
7316
+ },
7317
+ "401": {
7318
+ "$ref": "#/components/responses/Unauthorized"
7319
+ },
7320
+ "403": {
7321
+ "$ref": "#/components/responses/Forbidden"
7322
+ },
7323
+ "500": {
7324
+ "$ref": "#/components/responses/InternalServerError"
7100
7325
  }
7101
7326
  }
7102
7327
  }
7103
7328
  },
7104
- "/v2/portal/automation-context": {
7105
- "get": {
7106
- "operationId": "getAutomationContext",
7107
- "summary": "getAutomationContext",
7108
- "description": "Retrieves the automation context.",
7329
+ "/v2/portal/entity/{slug}/{id}": {
7330
+ "patch": {
7331
+ "operationId": "patchPortalUserEntity",
7332
+ "summary": "patchPortalUserEntity",
7333
+ "description": "**EXPERIMENTAL — do not rely on this endpoint.** It is unstable, currently limited to the `asset` schema, and may change or be removed without notice; third parties must not build on it yet.\nPartially update a single entity on behalf of a portal user. The schema slug and entity id are passed in the path; the schema must be one of the supported (experimental) schemas. Field-level permissions are enforced by the caller's role grants (use null to clear a field, e.g. external_id). The target entity must already be owned by the caller's contact.",
7334
+ "deprecated": true,
7109
7335
  "tags": [
7110
7336
  "ECP"
7111
7337
  ],
7112
7338
  "security": [
7113
7339
  {
7114
- "EpilotAuth": []
7340
+ "PortalAuth": []
7115
7341
  }
7116
7342
  ],
7117
7343
  "parameters": [
7118
7344
  {
7119
- "in": "query",
7120
- "name": "activity_id",
7121
- "required": true,
7345
+ "in": "path",
7346
+ "name": "slug",
7347
+ "description": "Entity schema slug to update. Limited to the supported (experimental) schemas, currently `asset`.",
7348
+ "required": true,
7349
+ "schema": {
7350
+ "type": "string",
7351
+ "enum": [
7352
+ "asset"
7353
+ ],
7354
+ "example": "asset"
7355
+ }
7356
+ },
7357
+ {
7358
+ "in": "path",
7359
+ "name": "id",
7360
+ "description": "ID of the entity to update. Must already be owned by the caller's contact.",
7361
+ "required": true,
7362
+ "schema": {
7363
+ "$ref": "#/components/schemas/EntityId"
7364
+ }
7365
+ }
7366
+ ],
7367
+ "requestBody": {
7368
+ "required": true,
7369
+ "content": {
7370
+ "application/json": {
7371
+ "schema": {
7372
+ "$ref": "#/components/schemas/Entity"
7373
+ },
7374
+ "example": {
7375
+ "external_id": null
7376
+ }
7377
+ }
7378
+ }
7379
+ },
7380
+ "responses": {
7381
+ "200": {
7382
+ "description": "The entity has been updated successfully for the portal user.",
7383
+ "content": {
7384
+ "application/json": {
7385
+ "schema": {
7386
+ "$ref": "#/components/schemas/EntityResponse"
7387
+ }
7388
+ }
7389
+ }
7390
+ },
7391
+ "400": {
7392
+ "$ref": "#/components/responses/InvalidRequest"
7393
+ },
7394
+ "401": {
7395
+ "$ref": "#/components/responses/Unauthorized"
7396
+ },
7397
+ "403": {
7398
+ "$ref": "#/components/responses/Forbidden"
7399
+ },
7400
+ "404": {
7401
+ "$ref": "#/components/responses/NotFound"
7402
+ },
7403
+ "500": {
7404
+ "$ref": "#/components/responses/InternalServerError"
7405
+ }
7406
+ }
7407
+ }
7408
+ },
7409
+ "/v2/portal/can-trigger-portal-flow": {
7410
+ "post": {
7411
+ "operationId": "canTriggerPortalFlow",
7412
+ "summary": "canTriggerPortalFlow",
7413
+ "description": "Returns whether the user can trigger a portal flow",
7414
+ "tags": [
7415
+ "ECP Admin"
7416
+ ],
7417
+ "security": [
7418
+ {
7419
+ "EpilotAuth": []
7420
+ }
7421
+ ],
7422
+ "parameters": [
7423
+ {
7424
+ "in": "query",
7425
+ "name": "origin",
7426
+ "required": false,
7427
+ "deprecated": true,
7428
+ "schema": {
7429
+ "$ref": "#/components/schemas/Origin"
7430
+ },
7431
+ "description": "Origin of the portal"
7432
+ },
7433
+ {
7434
+ "in": "query",
7435
+ "name": "portal_id",
7436
+ "required": true,
7437
+ "schema": {
7438
+ "type": "string",
7439
+ "description": "Portal ID",
7440
+ "example": "123"
7441
+ }
7442
+ }
7443
+ ],
7444
+ "requestBody": {
7445
+ "description": "Request of trigger portal flow",
7446
+ "required": true,
7447
+ "content": {
7448
+ "application/json": {
7449
+ "schema": {
7450
+ "$ref": "#/components/schemas/TriggerPortalFlow"
7451
+ }
7452
+ }
7453
+ }
7454
+ },
7455
+ "responses": {
7456
+ "200": {
7457
+ "description": "Can Trigger Portal Flow",
7458
+ "content": {
7459
+ "application/json": {
7460
+ "schema": {
7461
+ "type": "object",
7462
+ "properties": {
7463
+ "can_trigger": {
7464
+ "type": "boolean",
7465
+ "description": "Whether the flow can be triggered",
7466
+ "example": true
7467
+ }
7468
+ }
7469
+ }
7470
+ }
7471
+ }
7472
+ }
7473
+ }
7474
+ }
7475
+ },
7476
+ "/v2/portal/automation-context": {
7477
+ "get": {
7478
+ "operationId": "getAutomationContext",
7479
+ "summary": "getAutomationContext",
7480
+ "description": "Retrieves the automation context.",
7481
+ "tags": [
7482
+ "ECP"
7483
+ ],
7484
+ "security": [
7485
+ {
7486
+ "EpilotAuth": []
7487
+ }
7488
+ ],
7489
+ "parameters": [
7490
+ {
7491
+ "in": "query",
7492
+ "name": "activity_id",
7493
+ "required": true,
7122
7494
  "schema": {
7123
7495
  "$ref": "#/components/schemas/ActivityId"
7124
7496
  },
@@ -9319,6 +9691,20 @@
9319
9691
  "example": "5da0a718-c822-403d-9f5d-20d4584e0528"
9320
9692
  },
9321
9693
  "description": "Portal ID (readonly UUID generated on portal creation)"
9694
+ },
9695
+ {
9696
+ "in": "query",
9697
+ "name": "page_upsert_mode",
9698
+ "required": false,
9699
+ "schema": {
9700
+ "type": "string",
9701
+ "enum": [
9702
+ "id",
9703
+ "slug"
9704
+ ],
9705
+ "default": "id"
9706
+ },
9707
+ "description": "Determines how pages are matched for upsert operations:\n- `id` (default): Match pages by their ID. Use this when page IDs are stable and known upfront.\n- `slug`: Match pages by their slug. When a request page has the same slug as an existing page, the existing page ID is adopted. Use this when page ids are unknown or when source page IDs differ from destination page IDs.\n"
9322
9708
  }
9323
9709
  ],
9324
9710
  "requestBody": {
@@ -10127,72 +10513,193 @@
10127
10513
  }
10128
10514
  }
10129
10515
  }
10130
- }
10131
- },
10132
- "components": {
10133
- "responses": {
10134
- "InvalidRequest": {
10135
- "description": "The request could not be validated",
10136
- "content": {
10137
- "application/json": {
10138
- "schema": {
10139
- "$ref": "#/components/schemas/ErrorResp"
10140
- }
10516
+ },
10517
+ "/v1/portal/mobile-config": {
10518
+ "get": {
10519
+ "operationId": "getMobileConfig",
10520
+ "summary": "getMobileConfig",
10521
+ "description": "Returns the portal's mobile app configuration. By default the response is build-ready (resolved): base info (display_name from the portal name, app_host from the domain, environment) and branding (logo from the portal images, colors from the design palette) are filled in. Pass raw=true to get only the stored mobile_config without resolution.",
10522
+ "tags": [
10523
+ "ECP Admin"
10524
+ ],
10525
+ "security": [
10526
+ {
10527
+ "EpilotAuth": []
10141
10528
  }
10142
- }
10143
- },
10144
- "Unauthorized": {
10145
- "description": "Could not authenticate the user",
10146
- "content": {
10147
- "application/json": {
10529
+ ],
10530
+ "parameters": [
10531
+ {
10532
+ "in": "query",
10533
+ "name": "portal_id",
10534
+ "required": true,
10148
10535
  "schema": {
10149
- "$ref": "#/components/schemas/ErrorResp"
10150
- }
10151
- }
10152
- }
10153
- },
10154
- "Forbidden": {
10155
- "description": "The user is not allowed to access this resource",
10156
- "content": {
10157
- "application/json": {
10536
+ "type": "string"
10537
+ },
10538
+ "description": "Portal ID"
10539
+ },
10540
+ {
10541
+ "in": "query",
10542
+ "name": "raw",
10543
+ "required": false,
10158
10544
  "schema": {
10159
- "$ref": "#/components/schemas/ErrorResp"
10160
- }
10545
+ "type": "boolean"
10546
+ },
10547
+ "description": "Return only the stored mobile_config without resolving base info/branding."
10161
10548
  }
10162
- }
10163
- },
10164
- "ForbiddenByRule": {
10165
- "description": "The user is not allowed to access this resource",
10166
- "content": {
10167
- "application/json": {
10168
- "schema": {
10169
- "oneOf": [
10170
- {
10171
- "$ref": "#/components/schemas/ErrorResp"
10172
- },
10173
- {
10174
- "$ref": "#/components/schemas/FailedRuleErrorResp"
10549
+ ],
10550
+ "responses": {
10551
+ "200": {
10552
+ "description": "Mobile config retrieved successfully.",
10553
+ "content": {
10554
+ "application/json": {
10555
+ "schema": {
10556
+ "$ref": "#/components/schemas/MobileConfig"
10175
10557
  }
10176
- ]
10558
+ }
10177
10559
  }
10560
+ },
10561
+ "401": {
10562
+ "$ref": "#/components/responses/Unauthorized"
10563
+ },
10564
+ "403": {
10565
+ "$ref": "#/components/responses/Forbidden"
10566
+ },
10567
+ "404": {
10568
+ "$ref": "#/components/responses/NotFound"
10569
+ },
10570
+ "500": {
10571
+ "$ref": "#/components/responses/InternalServerError"
10178
10572
  }
10179
10573
  }
10180
10574
  },
10181
- "Conflict": {
10182
- "description": "The request conflicts with the current state of the target resource.",
10183
- "content": {
10184
- "application/json": {
10185
- "schema": {
10186
- "$ref": "#/components/schemas/ErrorResp"
10187
- }
10575
+ "put": {
10576
+ "operationId": "putMobileConfig",
10577
+ "summary": "putMobileConfig",
10578
+ "description": "Merges the provided fields into the portal's mobile app configuration\n(deep merge). Only mobile_config is modified; all other portal settings\nare left untouched.\n",
10579
+ "tags": [
10580
+ "ECP Admin"
10581
+ ],
10582
+ "security": [
10583
+ {
10584
+ "EpilotAuth": []
10188
10585
  }
10189
- }
10190
- },
10191
- "ContractAssignmentConflict": {
10192
- "description": "Contract was found but is not assignable in its current state.",
10193
- "content": {
10194
- "application/json": {
10195
- "schema": {
10586
+ ],
10587
+ "parameters": [
10588
+ {
10589
+ "in": "query",
10590
+ "name": "portal_id",
10591
+ "required": true,
10592
+ "schema": {
10593
+ "type": "string"
10594
+ },
10595
+ "description": "Portal ID"
10596
+ }
10597
+ ],
10598
+ "requestBody": {
10599
+ "description": "Editable mobile fields to merge into the existing mobile_config. Only mobile-relevant settings + app branding are applied; other fields are ignored.",
10600
+ "required": true,
10601
+ "content": {
10602
+ "application/json": {
10603
+ "schema": {
10604
+ "$ref": "#/components/schemas/MobileConfigUpdate"
10605
+ }
10606
+ }
10607
+ }
10608
+ },
10609
+ "responses": {
10610
+ "200": {
10611
+ "description": "Mobile config updated successfully.",
10612
+ "content": {
10613
+ "application/json": {
10614
+ "schema": {
10615
+ "$ref": "#/components/schemas/MobileConfig"
10616
+ }
10617
+ }
10618
+ }
10619
+ },
10620
+ "400": {
10621
+ "$ref": "#/components/responses/InvalidRequest"
10622
+ },
10623
+ "401": {
10624
+ "$ref": "#/components/responses/Unauthorized"
10625
+ },
10626
+ "403": {
10627
+ "$ref": "#/components/responses/Forbidden"
10628
+ },
10629
+ "404": {
10630
+ "$ref": "#/components/responses/NotFound"
10631
+ },
10632
+ "500": {
10633
+ "$ref": "#/components/responses/InternalServerError"
10634
+ }
10635
+ }
10636
+ }
10637
+ }
10638
+ },
10639
+ "components": {
10640
+ "responses": {
10641
+ "InvalidRequest": {
10642
+ "description": "The request could not be validated",
10643
+ "content": {
10644
+ "application/json": {
10645
+ "schema": {
10646
+ "$ref": "#/components/schemas/ErrorResp"
10647
+ }
10648
+ }
10649
+ }
10650
+ },
10651
+ "Unauthorized": {
10652
+ "description": "Could not authenticate the user",
10653
+ "content": {
10654
+ "application/json": {
10655
+ "schema": {
10656
+ "$ref": "#/components/schemas/ErrorResp"
10657
+ }
10658
+ }
10659
+ }
10660
+ },
10661
+ "Forbidden": {
10662
+ "description": "The user is not allowed to access this resource",
10663
+ "content": {
10664
+ "application/json": {
10665
+ "schema": {
10666
+ "$ref": "#/components/schemas/ErrorResp"
10667
+ }
10668
+ }
10669
+ }
10670
+ },
10671
+ "ForbiddenByRule": {
10672
+ "description": "The user is not allowed to access this resource",
10673
+ "content": {
10674
+ "application/json": {
10675
+ "schema": {
10676
+ "oneOf": [
10677
+ {
10678
+ "$ref": "#/components/schemas/ErrorResp"
10679
+ },
10680
+ {
10681
+ "$ref": "#/components/schemas/FailedRuleErrorResp"
10682
+ }
10683
+ ]
10684
+ }
10685
+ }
10686
+ }
10687
+ },
10688
+ "Conflict": {
10689
+ "description": "The request conflicts with the current state of the target resource.",
10690
+ "content": {
10691
+ "application/json": {
10692
+ "schema": {
10693
+ "$ref": "#/components/schemas/ErrorResp"
10694
+ }
10695
+ }
10696
+ }
10697
+ },
10698
+ "ContractAssignmentConflict": {
10699
+ "description": "Contract was found but is not assignable in its current state.",
10700
+ "content": {
10701
+ "application/json": {
10702
+ "schema": {
10196
10703
  "allOf": [
10197
10704
  {
10198
10705
  "$ref": "#/components/schemas/ErrorResp"
@@ -10360,107 +10867,383 @@
10360
10867
  }
10361
10868
  },
10362
10869
  "schemas": {
10363
- "ContextEntity": {
10870
+ "MobileBuildStatus": {
10364
10871
  "type": "object",
10365
- "description": "An entity reference for context-aware operations",
10872
+ "description": "Latest build/upload status for a platform (system-written).",
10873
+ "additionalProperties": true,
10366
10874
  "properties": {
10367
- "entity_schema": {
10875
+ "version": {
10876
+ "type": "string"
10877
+ },
10878
+ "build_number": {
10879
+ "type": "integer"
10880
+ },
10881
+ "track": {
10368
10882
  "type": "string",
10369
- "description": "Entity schema",
10370
- "example": "contract"
10883
+ "description": "e.g. testflight | internal | beta"
10371
10884
  },
10372
- "entity_id": {
10885
+ "status": {
10373
10886
  "type": "string",
10374
- "format": "uuid",
10375
- "description": "Entity id",
10376
- "example": "5da0a718-c822-403d-9f5d-20d4584e0528"
10377
- }
10378
- },
10379
- "required": [
10380
- "entity_id",
10381
- "entity_schema"
10382
- ]
10383
- },
10384
- "ContextEntities": {
10385
- "type": "array",
10386
- "description": "Additional entities to include in the context for variable interpolation. Portal User and Contact entities are automatically part of the context.",
10387
- "items": {
10388
- "$ref": "#/components/schemas/ContextEntity"
10389
- },
10390
- "example": [
10391
- {
10392
- "entity_id": "5da0a718-c822-403d-9f5d-20d4584e0528",
10393
- "entity_schema": "contract"
10394
- }
10395
- ]
10396
- },
10397
- "ErrorResp": {
10398
- "type": "object",
10399
- "properties": {
10400
- "message": {
10887
+ "enum": [
10888
+ "building",
10889
+ "uploaded",
10890
+ "failed"
10891
+ ]
10892
+ },
10893
+ "updated_at": {
10401
10894
  "type": "string",
10402
- "description": "Error message"
10895
+ "format": "date-time"
10896
+ },
10897
+ "error": {
10898
+ "type": "string"
10403
10899
  }
10404
10900
  }
10405
10901
  },
10406
- "FailedRuleErrorResp": {
10902
+ "MobileBranding": {
10407
10903
  "type": "object",
10904
+ "additionalProperties": true,
10408
10905
  "properties": {
10409
- "message": {
10410
- "type": "string",
10411
- "description": "Error message"
10906
+ "app_icon": {
10907
+ "type": "string"
10412
10908
  },
10413
- "failed_rule": {
10414
- "type": "object",
10415
- "description": "Failed validation rule"
10909
+ "splash": {
10910
+ "type": "string"
10911
+ },
10912
+ "splash_dark": {
10913
+ "type": "string"
10914
+ },
10915
+ "icon_background_color": {
10916
+ "type": "string"
10917
+ },
10918
+ "splash_background_color": {
10919
+ "type": "string"
10920
+ },
10921
+ "splash_background_color_dark": {
10922
+ "type": "string"
10416
10923
  }
10417
10924
  }
10418
10925
  },
10419
- "EmailTemplates": {
10926
+ "MobileConfig": {
10420
10927
  "type": "object",
10421
- "description": "Email templates used for authentication and internal processes",
10928
+ "description": "Mobile app configuration for the portal. Stored inside the portal's config object. Identifiers/branding are non-secret; signing credentials live in a secure store, never here.",
10929
+ "additionalProperties": true,
10422
10930
  "properties": {
10423
- "confirmAccount": {
10424
- "$ref": "#/components/schemas/EntityId",
10425
- "nullable": true,
10426
- "description": "ID of the confirmation email template upon registration"
10931
+ "portal_id": {
10932
+ "type": "string",
10933
+ "description": "Portal id (response-only; ignored on write)."
10427
10934
  },
10428
- "advancedAuth": {
10429
- "$ref": "#/components/schemas/EntityId",
10430
- "nullable": true,
10431
- "description": "ID of the advanced Auth with login link and login code"
10935
+ "enabled": {
10936
+ "type": "boolean"
10432
10937
  },
10433
- "advancedMFA": {
10434
- "$ref": "#/components/schemas/EntityId",
10435
- "nullable": true,
10436
- "deprecated": true,
10437
- "description": "ID of the advanced MFA with login link and login code"
10938
+ "display_name": {
10939
+ "type": "string",
10940
+ "description": "App display name compiled into the binary."
10438
10941
  },
10439
- "journeySignUp": {
10440
- "$ref": "#/components/schemas/EntityId",
10441
- "nullable": true,
10442
- "description": "ID of the email template for signing up from Journeys"
10942
+ "app_host": {
10943
+ "type": "string",
10944
+ "description": "Host the mobile shell loads (defaults to the portal domain)."
10443
10945
  },
10444
- "journeySignInOneTimePassword": {
10445
- "$ref": "#/components/schemas/EntityId",
10446
- "nullable": true,
10447
- "description": "ID of the email template for OTP to sign in from Journeys"
10946
+ "environment": {
10947
+ "type": "string",
10948
+ "enum": [
10949
+ "prod",
10950
+ "staging",
10951
+ "dev"
10952
+ ]
10448
10953
  },
10449
- "journeyLoginOTP": {
10450
- "$ref": "#/components/schemas/EntityId",
10451
- "nullable": true,
10452
- "description": "ID of the email template for OTP to sign in from Journeys",
10453
- "deprecated": true
10954
+ "branding": {
10955
+ "$ref": "#/components/schemas/MobileBranding"
10454
10956
  },
10455
- "forgotPassword": {
10456
- "$ref": "#/components/schemas/EntityId",
10457
- "nullable": true,
10458
- "description": "ID of the email template for forgot password"
10957
+ "ios": {
10958
+ "type": "object",
10959
+ "additionalProperties": true,
10960
+ "properties": {
10961
+ "bundle_id": {
10962
+ "type": "string",
10963
+ "description": "iOS bundle id (matches the App Store Connect app)."
10964
+ },
10965
+ "team_id": {
10966
+ "type": "string",
10967
+ "description": "Apple Developer Team ID."
10968
+ },
10969
+ "credentials_status": {
10970
+ "type": "string",
10971
+ "enum": [
10972
+ "not_configured",
10973
+ "configured"
10974
+ ]
10975
+ },
10976
+ "app_store_id": {
10977
+ "type": "string",
10978
+ "description": "Numeric App Store id (system-written after first upload)."
10979
+ },
10980
+ "store_url": {
10981
+ "type": "string",
10982
+ "description": "System-written App Store URL."
10983
+ },
10984
+ "last_build": {
10985
+ "$ref": "#/components/schemas/MobileBuildStatus"
10986
+ }
10987
+ }
10459
10988
  },
10460
- "invitation": {
10461
- "$ref": "#/components/schemas/EntityId",
10462
- "nullable": true,
10463
- "description": "ID of the email template for invitation when the user is just invited to register on the portal."
10989
+ "android": {
10990
+ "type": "object",
10991
+ "additionalProperties": true,
10992
+ "properties": {
10993
+ "package_name": {
10994
+ "type": "string",
10995
+ "description": "Android package name (matches the Play Console app)."
10996
+ },
10997
+ "credentials_status": {
10998
+ "type": "string",
10999
+ "enum": [
11000
+ "not_configured",
11001
+ "configured"
11002
+ ]
11003
+ },
11004
+ "upload_key_status": {
11005
+ "type": "string",
11006
+ "description": "Play App Signing upload-key state.",
11007
+ "enum": [
11008
+ "not_configured",
11009
+ "generated",
11010
+ "enrolled"
11011
+ ]
11012
+ },
11013
+ "store_url": {
11014
+ "type": "string",
11015
+ "description": "System-written Play Store URL."
11016
+ },
11017
+ "last_build": {
11018
+ "$ref": "#/components/schemas/MobileBuildStatus"
11019
+ }
11020
+ }
11021
+ },
11022
+ "ota": {
11023
+ "$ref": "#/components/schemas/MobileOtaConfig"
11024
+ }
11025
+ }
11026
+ },
11027
+ "MobileConfigUpdate": {
11028
+ "type": "object",
11029
+ "description": "Editable mobile fields for PUT. Only mobile-relevant settings + app branding can be changed. Portal-derived values (display_name, app_host), the portal logo, and system-written fields (credentials_status, app_store_id, last_build, …) are ignored if sent.",
11030
+ "additionalProperties": true,
11031
+ "properties": {
11032
+ "enabled": {
11033
+ "type": "boolean"
11034
+ },
11035
+ "ios": {
11036
+ "type": "object",
11037
+ "additionalProperties": true,
11038
+ "properties": {
11039
+ "bundle_id": {
11040
+ "type": "string"
11041
+ },
11042
+ "team_id": {
11043
+ "type": "string"
11044
+ },
11045
+ "store_url": {
11046
+ "type": "string"
11047
+ },
11048
+ "app_store_id": {
11049
+ "type": "string"
11050
+ }
11051
+ }
11052
+ },
11053
+ "android": {
11054
+ "type": "object",
11055
+ "additionalProperties": true,
11056
+ "properties": {
11057
+ "package_name": {
11058
+ "type": "string"
11059
+ },
11060
+ "store_url": {
11061
+ "type": "string"
11062
+ }
11063
+ }
11064
+ },
11065
+ "branding": {
11066
+ "$ref": "#/components/schemas/MobileBranding"
11067
+ },
11068
+ "ota": {
11069
+ "$ref": "#/components/schemas/MobileOtaConfig"
11070
+ }
11071
+ }
11072
+ },
11073
+ "MobileOtaConfig": {
11074
+ "type": "object",
11075
+ "description": "OTA (over-the-air) update settings for the portal's mobile app. Drives the OTA build pipeline and the per-portal manifest. channel / update_strategy / min_native_version are epilot-internal controls.",
11076
+ "additionalProperties": true,
11077
+ "properties": {
11078
+ "enabled": {
11079
+ "type": "boolean",
11080
+ "description": "Whether OTA updates are enabled for this portal."
11081
+ },
11082
+ "channel": {
11083
+ "type": "string",
11084
+ "enum": [
11085
+ "canary",
11086
+ "stable"
11087
+ ],
11088
+ "description": "Release channel this portal follows."
11089
+ },
11090
+ "auto_update": {
11091
+ "type": "boolean",
11092
+ "description": "Whether the app auto-updates or prompts the user."
11093
+ },
11094
+ "update_strategy": {
11095
+ "type": "string",
11096
+ "enum": [
11097
+ "next-launch",
11098
+ "immediate"
11099
+ ],
11100
+ "description": "When to apply a downloaded bundle."
11101
+ },
11102
+ "min_native_version": {
11103
+ "type": "string",
11104
+ "description": "Minimum native app version required to load OTA bundles."
11105
+ }
11106
+ }
11107
+ },
11108
+ "OtaPortal": {
11109
+ "type": "object",
11110
+ "description": "A portal that has mobile OTA updates enabled.",
11111
+ "required": [
11112
+ "domain",
11113
+ "channel",
11114
+ "autoUpdate",
11115
+ "updateStrategy"
11116
+ ],
11117
+ "properties": {
11118
+ "domain": {
11119
+ "type": "string",
11120
+ "description": "Portal hostname — the OTA manifest filename ({domain}.json).",
11121
+ "example": "kundenportal.twl.de"
11122
+ },
11123
+ "channel": {
11124
+ "type": "string",
11125
+ "enum": [
11126
+ "canary",
11127
+ "stable"
11128
+ ]
11129
+ },
11130
+ "autoUpdate": {
11131
+ "type": "boolean"
11132
+ },
11133
+ "updateStrategy": {
11134
+ "type": "string",
11135
+ "enum": [
11136
+ "next-launch",
11137
+ "immediate"
11138
+ ]
11139
+ },
11140
+ "minNativeVersion": {
11141
+ "type": "string",
11142
+ "example": "1.0.0"
11143
+ }
11144
+ }
11145
+ },
11146
+ "ContextEntity": {
11147
+ "type": "object",
11148
+ "description": "An entity reference for context-aware operations",
11149
+ "properties": {
11150
+ "entity_schema": {
11151
+ "type": "string",
11152
+ "description": "Entity schema",
11153
+ "example": "contract"
11154
+ },
11155
+ "entity_id": {
11156
+ "type": "string",
11157
+ "format": "uuid",
11158
+ "description": "Entity id",
11159
+ "example": "5da0a718-c822-403d-9f5d-20d4584e0528"
11160
+ }
11161
+ },
11162
+ "required": [
11163
+ "entity_id",
11164
+ "entity_schema"
11165
+ ]
11166
+ },
11167
+ "ContextEntities": {
11168
+ "type": "array",
11169
+ "description": "Additional entities to include in the context for variable interpolation. Portal User and Contact entities are automatically part of the context.",
11170
+ "items": {
11171
+ "$ref": "#/components/schemas/ContextEntity"
11172
+ },
11173
+ "example": [
11174
+ {
11175
+ "entity_id": "5da0a718-c822-403d-9f5d-20d4584e0528",
11176
+ "entity_schema": "contract"
11177
+ }
11178
+ ]
11179
+ },
11180
+ "ErrorResp": {
11181
+ "type": "object",
11182
+ "properties": {
11183
+ "message": {
11184
+ "type": "string",
11185
+ "description": "Error message"
11186
+ }
11187
+ }
11188
+ },
11189
+ "FailedRuleErrorResp": {
11190
+ "type": "object",
11191
+ "properties": {
11192
+ "message": {
11193
+ "type": "string",
11194
+ "description": "Error message"
11195
+ },
11196
+ "failed_rule": {
11197
+ "type": "object",
11198
+ "description": "Failed validation rule"
11199
+ }
11200
+ }
11201
+ },
11202
+ "EmailTemplates": {
11203
+ "type": "object",
11204
+ "description": "Email templates used for authentication and internal processes",
11205
+ "properties": {
11206
+ "confirmAccount": {
11207
+ "$ref": "#/components/schemas/EntityId",
11208
+ "nullable": true,
11209
+ "description": "ID of the confirmation email template upon registration"
11210
+ },
11211
+ "advancedAuth": {
11212
+ "$ref": "#/components/schemas/EntityId",
11213
+ "nullable": true,
11214
+ "description": "ID of the advanced Auth with login link and login code"
11215
+ },
11216
+ "advancedMFA": {
11217
+ "$ref": "#/components/schemas/EntityId",
11218
+ "nullable": true,
11219
+ "deprecated": true,
11220
+ "description": "ID of the advanced MFA with login link and login code"
11221
+ },
11222
+ "journeySignUp": {
11223
+ "$ref": "#/components/schemas/EntityId",
11224
+ "nullable": true,
11225
+ "description": "ID of the email template for signing up from Journeys"
11226
+ },
11227
+ "journeySignInOneTimePassword": {
11228
+ "$ref": "#/components/schemas/EntityId",
11229
+ "nullable": true,
11230
+ "description": "ID of the email template for OTP to sign in from Journeys"
11231
+ },
11232
+ "journeyLoginOTP": {
11233
+ "$ref": "#/components/schemas/EntityId",
11234
+ "nullable": true,
11235
+ "description": "ID of the email template for OTP to sign in from Journeys",
11236
+ "deprecated": true
11237
+ },
11238
+ "forgotPassword": {
11239
+ "$ref": "#/components/schemas/EntityId",
11240
+ "nullable": true,
11241
+ "description": "ID of the email template for forgot password"
11242
+ },
11243
+ "invitation": {
11244
+ "$ref": "#/components/schemas/EntityId",
11245
+ "nullable": true,
11246
+ "description": "ID of the email template for invitation when the user is just invited to register on the portal."
10464
11247
  },
10465
11248
  "partnerInvitation": {
10466
11249
  "$ref": "#/components/schemas/EntityId",
@@ -10969,6 +11752,10 @@
10969
11752
  "auto_redirect_to_sso": {
10970
11753
  "type": "boolean",
10971
11754
  "description": "Decide whether to automatically redirect to the provider page during login, which would completely bypass showing the portal authentication page."
11755
+ },
11756
+ "prevent_user_enumeration": {
11757
+ "type": "boolean",
11758
+ "description": "Opt-in. When true, suppresses responses that reveal whether a user exists for public, pre-authentication actions (the login entry-point check and self-registration), at the expense of some UX. Already-authenticated actions are unaffected. Default false.\n"
10972
11759
  }
10973
11760
  }
10974
11761
  },
@@ -14282,49 +15069,154 @@
14282
15069
  "$ref": "#/components/schemas/PublicMeterReadingPlausibilityCheckDetails"
14283
15070
  }
14284
15071
  }
15072
+ },
15073
+ "changeEmail": {
15074
+ "type": "object",
15075
+ "properties": {
15076
+ "app": {
15077
+ "$ref": "#/components/schemas/PublicAppDetails"
15078
+ },
15079
+ "extension": {
15080
+ "$ref": "#/components/schemas/PublicExtensionDetails"
15081
+ },
15082
+ "hook": {
15083
+ "$ref": "#/components/schemas/PublicChangeEmailDetails"
15084
+ }
15085
+ }
15086
+ },
15087
+ "changePassword": {
15088
+ "type": "object",
15089
+ "properties": {
15090
+ "app": {
15091
+ "$ref": "#/components/schemas/PublicAppDetails"
15092
+ },
15093
+ "extension": {
15094
+ "$ref": "#/components/schemas/PublicExtensionDetails"
15095
+ },
15096
+ "hook": {
15097
+ "$ref": "#/components/schemas/PublicChangePasswordDetails"
15098
+ }
15099
+ }
15100
+ },
15101
+ "deleteAccount": {
15102
+ "type": "object",
15103
+ "properties": {
15104
+ "app": {
15105
+ "$ref": "#/components/schemas/PublicAppDetails"
15106
+ },
15107
+ "extension": {
15108
+ "$ref": "#/components/schemas/PublicExtensionDetails"
15109
+ },
15110
+ "hook": {
15111
+ "$ref": "#/components/schemas/PublicDeleteAccountDetails"
15112
+ }
15113
+ }
14285
15114
  }
14286
15115
  }
14287
15116
  },
14288
- "DataRetrievalItem": {
15117
+ "PublicSelfManagementExplanation": {
14289
15118
  "type": "object",
14290
15119
  "properties": {
14291
- "app": {
14292
- "$ref": "#/components/schemas/PublicAppDetails"
14293
- },
14294
- "extension": {
14295
- "$ref": "#/components/schemas/PublicExtensionDetails"
14296
- },
14297
- "hook": {
14298
- "$ref": "#/components/schemas/PublicDataRetrievalHookDetails"
15120
+ "en": {
15121
+ "type": "string",
15122
+ "description": "Explanation of the functionality shown to the end user."
14299
15123
  }
14300
- }
15124
+ },
15125
+ "additionalProperties": {
15126
+ "type": "string",
15127
+ "description": "Explanation of the functionality in language denoted by ISO 3166-1 alpha-2 code."
15128
+ },
15129
+ "required": [
15130
+ "en"
15131
+ ],
15132
+ "description": "Explanation of the hook."
14301
15133
  },
14302
- "PublicAppDetails": {
15134
+ "PublicChangeEmailDetails": {
14303
15135
  "type": "object",
14304
15136
  "properties": {
14305
- "app_id": {
15137
+ "id": {
14306
15138
  "type": "string",
14307
- "description": "Identifier of the app."
15139
+ "description": "Identifier of the hook."
14308
15140
  },
14309
- "name": {
14310
- "type": "object",
14311
- "properties": {
14312
- "en": {
14313
- "type": "string",
14314
- "description": "Name of the app in English."
14315
- }
14316
- },
14317
- "additionalProperties": {
14318
- "type": "string",
14319
- "description": "Name of the app in some other language denoted by ISO 3166-1 alpha-2 code."
14320
- },
14321
- "required": [
14322
- "en"
14323
- ]
15141
+ "require_password_confirmation": {
15142
+ "type": "boolean",
15143
+ "description": "Whether the portal user must confirm their current password before the email change is handed over to the third-party system.",
15144
+ "default": true
15145
+ },
15146
+ "explanation": {
15147
+ "$ref": "#/components/schemas/PublicSelfManagementExplanation"
14324
15148
  }
14325
15149
  }
14326
15150
  },
14327
- "PublicExtensionDetails": {
15151
+ "PublicChangePasswordDetails": {
15152
+ "type": "object",
15153
+ "properties": {
15154
+ "id": {
15155
+ "type": "string",
15156
+ "description": "Identifier of the hook."
15157
+ },
15158
+ "require_new_password": {
15159
+ "type": "boolean",
15160
+ "description": "Whether the portal user must provide a new password that is passed to the third-party system.",
15161
+ "default": false
15162
+ },
15163
+ "explanation": {
15164
+ "$ref": "#/components/schemas/PublicSelfManagementExplanation"
15165
+ }
15166
+ }
15167
+ },
15168
+ "PublicDeleteAccountDetails": {
15169
+ "type": "object",
15170
+ "properties": {
15171
+ "id": {
15172
+ "type": "string",
15173
+ "description": "Identifier of the hook."
15174
+ },
15175
+ "explanation": {
15176
+ "$ref": "#/components/schemas/PublicSelfManagementExplanation"
15177
+ }
15178
+ }
15179
+ },
15180
+ "DataRetrievalItem": {
15181
+ "type": "object",
15182
+ "properties": {
15183
+ "app": {
15184
+ "$ref": "#/components/schemas/PublicAppDetails"
15185
+ },
15186
+ "extension": {
15187
+ "$ref": "#/components/schemas/PublicExtensionDetails"
15188
+ },
15189
+ "hook": {
15190
+ "$ref": "#/components/schemas/PublicDataRetrievalHookDetails"
15191
+ }
15192
+ }
15193
+ },
15194
+ "PublicAppDetails": {
15195
+ "type": "object",
15196
+ "properties": {
15197
+ "app_id": {
15198
+ "type": "string",
15199
+ "description": "Identifier of the app."
15200
+ },
15201
+ "name": {
15202
+ "type": "object",
15203
+ "properties": {
15204
+ "en": {
15205
+ "type": "string",
15206
+ "description": "Name of the app in English."
15207
+ }
15208
+ },
15209
+ "additionalProperties": {
15210
+ "type": "string",
15211
+ "description": "Name of the app in some other language denoted by ISO 3166-1 alpha-2 code."
15212
+ },
15213
+ "required": [
15214
+ "en"
15215
+ ]
15216
+ }
15217
+ }
15218
+ },
15219
+ "PublicExtensionDetails": {
14328
15220
  "type": "object",
14329
15221
  "properties": {
14330
15222
  "id": {
@@ -14694,6 +15586,15 @@
14694
15586
  },
14695
15587
  {
14696
15588
  "$ref": "#/components/schemas/ExtensionHookVisualizationMetadata"
15589
+ },
15590
+ {
15591
+ "$ref": "#/components/schemas/ExtensionHookChangeEmail"
15592
+ },
15593
+ {
15594
+ "$ref": "#/components/schemas/ExtensionHookChangePassword"
15595
+ },
15596
+ {
15597
+ "$ref": "#/components/schemas/ExtensionHookDeleteAccount"
14697
15598
  }
14698
15599
  ]
14699
15600
  }
@@ -14973,15 +15874,313 @@
14973
15874
  "description": "Explanation of the functionality shown to the end user.",
14974
15875
  "example": "This process will give you access to the matching Contracts."
14975
15876
  }
14976
- },
14977
- "additionalProperties": {
14978
- "type": "string",
14979
- "description": "Explanation of the functionality in language denoted by ISO 3166-1 alpha-2 code."
14980
- },
14981
- "required": [
14982
- "en"
14983
- ],
14984
- "description": "Explanation of the hook."
15877
+ },
15878
+ "additionalProperties": {
15879
+ "type": "string",
15880
+ "description": "Explanation of the functionality in language denoted by ISO 3166-1 alpha-2 code."
15881
+ },
15882
+ "required": [
15883
+ "en"
15884
+ ],
15885
+ "description": "Explanation of the hook."
15886
+ },
15887
+ "use_static_ips": {
15888
+ "type": "boolean",
15889
+ "deprecated": true,
15890
+ "description": "Deprecated. Prefer `secure_proxy` instead.\nIf true, requests are made from a set of static IP addresses and only allow connections to a set of allowed IP addresses. Get in touch with us to add your IP addresses.\n",
15891
+ "default": false
15892
+ },
15893
+ "secure_proxy": {
15894
+ "$ref": "#/components/schemas/SecureProxyConfig"
15895
+ }
15896
+ },
15897
+ "required": [
15898
+ "type",
15899
+ "call"
15900
+ ]
15901
+ },
15902
+ "ExtensionHookMeterReadingPlausibilityCheck": {
15903
+ "description": "Hook that checks the plausibility of meter readings before they are saved. This hook makes a POST call whenever a user is trying to save a meter reading. The expected response to the call is:\n - 200:\n If meter reading is plausible, the response should contain:\n - valid: true\n If meter reading is not plausible, the response should contain:\n - valid: false\n",
15904
+ "type": "object",
15905
+ "properties": {
15906
+ "type": {
15907
+ "type": "string",
15908
+ "enum": [
15909
+ "meterReadingPlausibilityCheck"
15910
+ ]
15911
+ },
15912
+ "plausibility_mode": {
15913
+ "type": "string",
15914
+ "enum": [
15915
+ "check",
15916
+ "range"
15917
+ ],
15918
+ "default": "check",
15919
+ "description": "Mode for plausibility check:\n- \"check\": Validates meter reading and returns valid: boolean (used during submission)\n- \"range\": Returns min/max allowed values for each counter for validation before submission\n"
15920
+ },
15921
+ "auth": {
15922
+ "$ref": "#/components/schemas/ExtensionAuthBlock"
15923
+ },
15924
+ "call": {
15925
+ "type": "object",
15926
+ "properties": {
15927
+ "url": {
15928
+ "type": "string",
15929
+ "description": "URL to call. Supports variable interpolation."
15930
+ },
15931
+ "body": {
15932
+ "type": "object",
15933
+ "description": "JSON body to use for the call. Supports variable interpolation.",
15934
+ "additionalProperties": {
15935
+ "type": "string"
15936
+ },
15937
+ "default": {}
15938
+ },
15939
+ "headers": {
15940
+ "type": "object",
15941
+ "description": "Headers to use. Supports variable interpolation.",
15942
+ "additionalProperties": {
15943
+ "type": "string"
15944
+ },
15945
+ "default": {}
15946
+ }
15947
+ },
15948
+ "required": [
15949
+ "url",
15950
+ "headers",
15951
+ "body"
15952
+ ]
15953
+ },
15954
+ "resolved": {
15955
+ "type": "object",
15956
+ "description": "Response to the call",
15957
+ "properties": {
15958
+ "data_path": {
15959
+ "type": "string",
15960
+ "description": "Optional path to an array in the response. If specified and the path points to an array,\nthe hook will map over each item using 'Item' variable for interpolation.\nRelevant only if plausibility_mode is \"range\".\n",
15961
+ "example": "data.results"
15962
+ },
15963
+ "dataPath": {
15964
+ "type": "string",
15965
+ "deprecated": true,
15966
+ "description": "Deprecated. Use `data_path` instead."
15967
+ },
15968
+ "counter_identifiers": {
15969
+ "description": "Counter identifier(s) used to match against the meter's counters.\nCan be a string (counter ID) or an object with counter properties.\nThe backend resolves this to meter_counter_id in the final response.\nRelevant only if plausibility_mode is \"range\".\n",
15970
+ "oneOf": [
15971
+ {
15972
+ "type": "string",
15973
+ "example": "{{Item.counter_id}}"
15974
+ },
15975
+ {
15976
+ "type": "object",
15977
+ "additionalProperties": {
15978
+ "type": "string"
15979
+ },
15980
+ "example": {
15981
+ "obis_code": "{{Item.obis}}"
15982
+ }
15983
+ }
15984
+ ]
15985
+ },
15986
+ "valid": {
15987
+ "type": "string",
15988
+ "description": "Indicate whether the meter reading is plausible. Relevant only if plausibility_mode is \"check\".",
15989
+ "example": "{{CallResponse.data.valid}}"
15990
+ },
15991
+ "upper_limit": {
15992
+ "type": "string",
15993
+ "description": "Upper allowed limit of the meter reading",
15994
+ "example": "{{CallResponse.data.upper_limit}}"
15995
+ },
15996
+ "lower_limit": {
15997
+ "type": "string",
15998
+ "description": "Lower allowed limit of the meter reading",
15999
+ "example": "{{CallResponse.data.lower_limit}}"
16000
+ },
16001
+ "error_message_path": {
16002
+ "type": "string",
16003
+ "description": "Optional path to a human-readable error message in the third-party response body, used when the call fails (non-2xx status).\nIf specified and the path resolves to a string, that message is forwarded to the end user instead of a generic error.\n",
16004
+ "example": "error.message"
16005
+ }
16006
+ }
16007
+ },
16008
+ "use_static_ips": {
16009
+ "type": "boolean",
16010
+ "deprecated": true,
16011
+ "description": "Deprecated. Prefer `secure_proxy` instead.\nIf true, requests are made from a set of static IP addresses and only allow connections to a set of allowed IP addresses. Get in touch with us to add your IP addresses.\n",
16012
+ "default": false
16013
+ },
16014
+ "secure_proxy": {
16015
+ "$ref": "#/components/schemas/SecureProxyConfig"
16016
+ }
16017
+ },
16018
+ "required": [
16019
+ "type",
16020
+ "call",
16021
+ "resolved"
16022
+ ]
16023
+ },
16024
+ "ExtensionHookPriceDataRetrieval": {
16025
+ "description": "Hook that will allow using the specified source as data for price visualizations. This hook is triggered to fetch the data. Format of the request and response has to follow the following specification: TBD. The expected response to the call is:\n - 200 with the time series data\n",
16026
+ "type": "object",
16027
+ "properties": {
16028
+ "type": {
16029
+ "type": "string",
16030
+ "enum": [
16031
+ "priceDataRetrieval"
16032
+ ]
16033
+ },
16034
+ "auth": {
16035
+ "$ref": "#/components/schemas/ExtensionAuthBlock"
16036
+ },
16037
+ "call": {
16038
+ "type": "object",
16039
+ "properties": {
16040
+ "method": {
16041
+ "type": "string",
16042
+ "description": "HTTP method to use for the call",
16043
+ "default": "GET"
16044
+ },
16045
+ "url": {
16046
+ "type": "string",
16047
+ "description": "URL to call. Supports variable interpolation."
16048
+ },
16049
+ "params": {
16050
+ "type": "object",
16051
+ "description": "Parameters to append to the URL. Supports variable interpolation.",
16052
+ "additionalProperties": {
16053
+ "type": "string"
16054
+ },
16055
+ "default": {}
16056
+ },
16057
+ "headers": {
16058
+ "type": "object",
16059
+ "description": "Headers to use. Supports variable interpolation.",
16060
+ "additionalProperties": {
16061
+ "type": "string"
16062
+ },
16063
+ "default": {}
16064
+ },
16065
+ "body": {
16066
+ "type": "object",
16067
+ "description": "Request body to send. Supports variable interpolation. Content format is determined by Content-Type header.",
16068
+ "additionalProperties": {
16069
+ "type": "string"
16070
+ },
16071
+ "default": {}
16072
+ }
16073
+ },
16074
+ "required": [
16075
+ "url"
16076
+ ]
16077
+ },
16078
+ "resolved": {
16079
+ "type": "object",
16080
+ "properties": {
16081
+ "data_path": {
16082
+ "type": "string",
16083
+ "description": "Optional path to the data (array) in the response. If omitted, the data is assumed to be on the top level."
16084
+ },
16085
+ "dataPath": {
16086
+ "type": "string",
16087
+ "deprecated": true,
16088
+ "description": "Deprecated. Use `data_path` instead."
16089
+ },
16090
+ "error_message_path": {
16091
+ "type": "string",
16092
+ "description": "Optional path to a human-readable error message in the third-party response body, used when the call fails (non-2xx status).\nIf specified and the path resolves to a string, that message is forwarded to the end user instead of a generic error.\n",
16093
+ "example": "error.message"
16094
+ }
16095
+ }
16096
+ },
16097
+ "use_static_ips": {
16098
+ "type": "boolean",
16099
+ "deprecated": true,
16100
+ "description": "Deprecated. Prefer `secure_proxy` instead.\nIf true, requests are made from a set of static IP addresses and only allow connections to a set of allowed IP addresses. Get in touch with us to add your IP addresses.\n",
16101
+ "default": false
16102
+ },
16103
+ "secure_proxy": {
16104
+ "$ref": "#/components/schemas/SecureProxyConfig"
16105
+ }
16106
+ },
16107
+ "required": [
16108
+ "type",
16109
+ "call"
16110
+ ]
16111
+ },
16112
+ "ExtensionHookConsumptionDataRetrieval": {
16113
+ "description": "Hook that will allow using the specified source as data for consumption visualizations. This hook is triggered to fetch the data. Format of the request and response has to follow the following specification: TBD. The expected response to the call is:\n - 200 with the time series data\n",
16114
+ "type": "object",
16115
+ "properties": {
16116
+ "type": {
16117
+ "type": "string",
16118
+ "enum": [
16119
+ "consumptionDataRetrieval"
16120
+ ]
16121
+ },
16122
+ "auth": {
16123
+ "$ref": "#/components/schemas/ExtensionAuthBlock"
16124
+ },
16125
+ "call": {
16126
+ "type": "object",
16127
+ "properties": {
16128
+ "method": {
16129
+ "type": "string",
16130
+ "description": "HTTP method to use for the call",
16131
+ "default": "GET"
16132
+ },
16133
+ "url": {
16134
+ "type": "string",
16135
+ "description": "URL to call. Supports variable interpolation."
16136
+ },
16137
+ "params": {
16138
+ "type": "object",
16139
+ "description": "Parameters to append to the URL. Supports variable interpolation.",
16140
+ "additionalProperties": {
16141
+ "type": "string"
16142
+ },
16143
+ "default": {}
16144
+ },
16145
+ "headers": {
16146
+ "type": "object",
16147
+ "description": "Headers to use. Supports variable interpolation.",
16148
+ "additionalProperties": {
16149
+ "type": "string"
16150
+ },
16151
+ "default": {}
16152
+ },
16153
+ "body": {
16154
+ "type": "object",
16155
+ "description": "Request body to send. Supports variable interpolation. Content format is determined by Content-Type header.",
16156
+ "additionalProperties": {
16157
+ "type": "string"
16158
+ },
16159
+ "default": {}
16160
+ }
16161
+ },
16162
+ "required": [
16163
+ "url"
16164
+ ]
16165
+ },
16166
+ "resolved": {
16167
+ "type": "object",
16168
+ "properties": {
16169
+ "data_path": {
16170
+ "type": "string",
16171
+ "description": "Optional path to the data (array) in the response. If omitted, the data is assumed to be on the top level."
16172
+ },
16173
+ "dataPath": {
16174
+ "type": "string",
16175
+ "deprecated": true,
16176
+ "description": "Deprecated. Use `data_path` instead."
16177
+ },
16178
+ "error_message_path": {
16179
+ "type": "string",
16180
+ "description": "Optional path to a human-readable error message in the third-party response body, used when the call fails (non-2xx status).\nIf specified and the path resolves to a string, that message is forwarded to the end user instead of a generic error.\n",
16181
+ "example": "error.message"
16182
+ }
16183
+ }
14985
16184
  },
14986
16185
  "use_static_ips": {
14987
16186
  "type": "boolean",
@@ -14998,24 +16197,22 @@
14998
16197
  "call"
14999
16198
  ]
15000
16199
  },
15001
- "ExtensionHookMeterReadingPlausibilityCheck": {
15002
- "description": "Hook that checks the plausibility of meter readings before they are saved. This hook makes a POST call whenever a user is trying to save a meter reading. The expected response to the call is:\n - 200:\n If meter reading is plausible, the response should contain:\n - valid: true\n If meter reading is not plausible, the response should contain:\n - valid: false\n",
16200
+ "ExtensionHookDataExport": {
16201
+ "description": "Generic data export hook. When configured on a visualization block, the portal delegates the export action (e.g. CSV/Excel/PDF download) to the configured external source instead of generating the file itself. Can be used by any block that supports export consumption charts, dynamic tariff charts, etc. The expected response to the call is:\n - 200 with a JSON body describing the exported file (download_url, optional filename, content_type, expires_at)\n",
15003
16202
  "type": "object",
15004
16203
  "properties": {
15005
16204
  "type": {
15006
16205
  "type": "string",
15007
16206
  "enum": [
15008
- "meterReadingPlausibilityCheck"
16207
+ "dataExport"
15009
16208
  ]
15010
16209
  },
15011
- "plausibility_mode": {
15012
- "type": "string",
15013
- "enum": [
15014
- "check",
15015
- "range"
15016
- ],
15017
- "default": "check",
15018
- "description": "Mode for plausibility check:\n- \"check\": Validates meter reading and returns valid: boolean (used during submission)\n- \"range\": Returns min/max allowed values for each counter for validation before submission\n"
16210
+ "block_types": {
16211
+ "type": "array",
16212
+ "description": "Optional list of portal block types this hook supports. If omitted,\nthe hook is usable on any export-capable block. Allowed values match\nthe block type identifiers used by the portal builder\n(e.g. `consumption_visualization`, `dynamic_tariff`).\n",
16213
+ "items": {
16214
+ "type": "string"
16215
+ }
15019
16216
  },
15020
16217
  "auth": {
15021
16218
  "$ref": "#/components/schemas/ExtensionAuthBlock"
@@ -15023,13 +16220,18 @@
15023
16220
  "call": {
15024
16221
  "type": "object",
15025
16222
  "properties": {
16223
+ "method": {
16224
+ "type": "string",
16225
+ "description": "HTTP method to use for the call",
16226
+ "default": "GET"
16227
+ },
15026
16228
  "url": {
15027
16229
  "type": "string",
15028
16230
  "description": "URL to call. Supports variable interpolation."
15029
16231
  },
15030
- "body": {
16232
+ "params": {
15031
16233
  "type": "object",
15032
- "description": "JSON body to use for the call. Supports variable interpolation.",
16234
+ "description": "Parameters to append to the URL. Supports variable interpolation.",
15033
16235
  "additionalProperties": {
15034
16236
  "type": "string"
15035
16237
  },
@@ -15042,61 +16244,23 @@
15042
16244
  "type": "string"
15043
16245
  },
15044
16246
  "default": {}
16247
+ },
16248
+ "body": {
16249
+ "type": "object",
16250
+ "description": "Request body to send. Supports variable interpolation. Content format is determined by Content-Type header.",
16251
+ "additionalProperties": {
16252
+ "type": "string"
16253
+ },
16254
+ "default": {}
15045
16255
  }
15046
16256
  },
15047
16257
  "required": [
15048
- "url",
15049
- "headers",
15050
- "body"
16258
+ "url"
15051
16259
  ]
15052
16260
  },
15053
16261
  "resolved": {
15054
16262
  "type": "object",
15055
- "description": "Response to the call",
15056
16263
  "properties": {
15057
- "data_path": {
15058
- "type": "string",
15059
- "description": "Optional path to an array in the response. If specified and the path points to an array,\nthe hook will map over each item using 'Item' variable for interpolation.\nRelevant only if plausibility_mode is \"range\".\n",
15060
- "example": "data.results"
15061
- },
15062
- "dataPath": {
15063
- "type": "string",
15064
- "deprecated": true,
15065
- "description": "Deprecated. Use `data_path` instead."
15066
- },
15067
- "counter_identifiers": {
15068
- "description": "Counter identifier(s) used to match against the meter's counters.\nCan be a string (counter ID) or an object with counter properties.\nThe backend resolves this to meter_counter_id in the final response.\nRelevant only if plausibility_mode is \"range\".\n",
15069
- "oneOf": [
15070
- {
15071
- "type": "string",
15072
- "example": "{{Item.counter_id}}"
15073
- },
15074
- {
15075
- "type": "object",
15076
- "additionalProperties": {
15077
- "type": "string"
15078
- },
15079
- "example": {
15080
- "obis_code": "{{Item.obis}}"
15081
- }
15082
- }
15083
- ]
15084
- },
15085
- "valid": {
15086
- "type": "string",
15087
- "description": "Indicate whether the meter reading is plausible. Relevant only if plausibility_mode is \"check\".",
15088
- "example": "{{CallResponse.data.valid}}"
15089
- },
15090
- "upper_limit": {
15091
- "type": "string",
15092
- "description": "Upper allowed limit of the meter reading",
15093
- "example": "{{CallResponse.data.upper_limit}}"
15094
- },
15095
- "lower_limit": {
15096
- "type": "string",
15097
- "description": "Lower allowed limit of the meter reading",
15098
- "example": "{{CallResponse.data.lower_limit}}"
15099
- },
15100
16264
  "error_message_path": {
15101
16265
  "type": "string",
15102
16266
  "description": "Optional path to a human-readable error message in the third-party response body, used when the call fails (non-2xx status).\nIf specified and the path resolves to a string, that message is forwarded to the end user instead of a generic error.\n",
@@ -15116,18 +16280,17 @@
15116
16280
  },
15117
16281
  "required": [
15118
16282
  "type",
15119
- "call",
15120
- "resolved"
16283
+ "call"
15121
16284
  ]
15122
16285
  },
15123
- "ExtensionHookPriceDataRetrieval": {
15124
- "description": "Hook that will allow using the specified source as data for price visualizations. This hook is triggered to fetch the data. Format of the request and response has to follow the following specification: TBD. The expected response to the call is:\n - 200 with the time series data\n",
16286
+ "ExtensionHookVisualizationMetadata": {
16287
+ "description": "Hook that returns runtime metadata describing how a visualization should be rendered for a given portal context. Invoked by the portal before fetching data, with the same context the data hook receives.\n",
15125
16288
  "type": "object",
15126
16289
  "properties": {
15127
16290
  "type": {
15128
16291
  "type": "string",
15129
16292
  "enum": [
15130
- "priceDataRetrieval"
16293
+ "visualizationMetadata"
15131
16294
  ]
15132
16295
  },
15133
16296
  "auth": {
@@ -15179,7 +16342,7 @@
15179
16342
  "properties": {
15180
16343
  "data_path": {
15181
16344
  "type": "string",
15182
- "description": "Optional path to the data (array) in the response. If omitted, the data is assumed to be on the top level."
16345
+ "description": "Optional path to the metadata object in the response. If omitted, the metadata is assumed to be on the top level."
15183
16346
  },
15184
16347
  "dataPath": {
15185
16348
  "type": "string",
@@ -15208,14 +16371,14 @@
15208
16371
  "call"
15209
16372
  ]
15210
16373
  },
15211
- "ExtensionHookConsumptionDataRetrieval": {
16374
+ "ExtensionHookCostDataRetrieval": {
15212
16375
  "description": "Hook that will allow using the specified source as data for consumption visualizations. This hook is triggered to fetch the data. Format of the request and response has to follow the following specification: TBD. The expected response to the call is:\n - 200 with the time series data\n",
15213
16376
  "type": "object",
15214
16377
  "properties": {
15215
16378
  "type": {
15216
16379
  "type": "string",
15217
16380
  "enum": [
15218
- "consumptionDataRetrieval"
16381
+ "costDataRetrieval"
15219
16382
  ]
15220
16383
  },
15221
16384
  "auth": {
@@ -15296,22 +16459,38 @@
15296
16459
  "call"
15297
16460
  ]
15298
16461
  },
15299
- "ExtensionHookDataExport": {
15300
- "description": "Generic data export hook. When configured on a visualization block, the portal delegates the export action (e.g. CSV/Excel/PDF download) to the configured external source instead of generating the file itself. Can be used by any block that supports export consumption charts, dynamic tariff charts, etc. The expected response to the call is:\n - 200 with a JSON body describing the exported file (download_url, optional filename, content_type, expires_at)\n",
16462
+ "ExtensionHookChangeEmail": {
16463
+ "description": "Hook that replaces the built-in change email functionality for portal users. When configured, the portal does not change the user's login email itself. Instead, this hook makes an HTTP call to the third-party system, which is expected to handle the email change (most likely by sending the user instructions to confirm the new email address).\nThe expected response http status code to the call is:\n - 2xx if the request was accepted\n - non-2xx if the request failed (optionally with a human-readable message resolved via `resolved.error_message_path`)\n",
15301
16464
  "type": "object",
15302
16465
  "properties": {
15303
16466
  "type": {
15304
16467
  "type": "string",
15305
16468
  "enum": [
15306
- "dataExport"
16469
+ "changeEmail"
15307
16470
  ]
15308
16471
  },
15309
- "block_types": {
15310
- "type": "array",
15311
- "description": "Optional list of portal block types this hook supports. If omitted,\nthe hook is usable on any export-capable block. Allowed values match\nthe block type identifiers used by the portal builder\n(e.g. `consumption_visualization`, `dynamic_tariff`).\n",
15312
- "items": {
15313
- "type": "string"
15314
- }
16472
+ "require_password_confirmation": {
16473
+ "type": "boolean",
16474
+ "description": "Whether the portal user must confirm their current password before the change email request is handed over to the third-party system. When true, the portal collects and verifies the current password before calling the hook.\n",
16475
+ "default": true
16476
+ },
16477
+ "explanation": {
16478
+ "type": "object",
16479
+ "properties": {
16480
+ "en": {
16481
+ "type": "string",
16482
+ "description": "Explanation of the functionality shown to the end user.",
16483
+ "example": "You will receive an email with instructions to confirm your new email address."
16484
+ }
16485
+ },
16486
+ "additionalProperties": {
16487
+ "type": "string",
16488
+ "description": "Explanation of the functionality in language denoted by ISO 3166-1 alpha-2 code."
16489
+ },
16490
+ "required": [
16491
+ "en"
16492
+ ],
16493
+ "description": "Optional explanation shown to the user in the change email confirmation dialog."
15315
16494
  },
15316
16495
  "auth": {
15317
16496
  "$ref": "#/components/schemas/ExtensionAuthBlock"
@@ -15322,7 +16501,7 @@
15322
16501
  "method": {
15323
16502
  "type": "string",
15324
16503
  "description": "HTTP method to use for the call",
15325
- "default": "GET"
16504
+ "default": "POST"
15326
16505
  },
15327
16506
  "url": {
15328
16507
  "type": "string",
@@ -15346,15 +16525,12 @@
15346
16525
  },
15347
16526
  "body": {
15348
16527
  "type": "object",
15349
- "description": "Request body to send. Supports variable interpolation. Content format is determined by Content-Type header.",
15350
- "additionalProperties": {
15351
- "type": "string"
15352
- },
15353
- "default": {}
16528
+ "description": "Optional JSON body to use for the call. Defaults to an object with the requested new email and portal user context. The requested new email is available as `{{Input.new_email}}` and the current account email as `{{Input.old_email}}`. Supports variable interpolation."
15354
16529
  }
15355
16530
  },
15356
16531
  "required": [
15357
- "url"
16532
+ "url",
16533
+ "headers"
15358
16534
  ]
15359
16535
  },
15360
16536
  "resolved": {
@@ -15367,12 +16543,6 @@
15367
16543
  }
15368
16544
  }
15369
16545
  },
15370
- "use_static_ips": {
15371
- "type": "boolean",
15372
- "deprecated": true,
15373
- "description": "Deprecated. Prefer `secure_proxy` instead.\nIf true, requests are made from a set of static IP addresses and only allow connections to a set of allowed IP addresses. Get in touch with us to add your IP addresses.\n",
15374
- "default": false
15375
- },
15376
16546
  "secure_proxy": {
15377
16547
  "$ref": "#/components/schemas/SecureProxyConfig"
15378
16548
  }
@@ -15382,16 +16552,39 @@
15382
16552
  "call"
15383
16553
  ]
15384
16554
  },
15385
- "ExtensionHookVisualizationMetadata": {
15386
- "description": "Hook that returns runtime metadata describing how a visualization should be rendered for a given portal context. Invoked by the portal before fetching data, with the same context the data hook receives.\n",
16555
+ "ExtensionHookChangePassword": {
16556
+ "description": "Hook that replaces the built-in change password functionality for portal users. When configured, the portal does not change the user's password itself. Instead, this hook makes an HTTP call to the third-party system, which is expected to handle the password change (most likely by sending the user instructions to complete the process).\nThe expected response http status code to the call is:\n - 2xx if the request was accepted\n - non-2xx if the request failed (optionally with a human-readable message resolved via `resolved.error_message_path`)\n",
15387
16557
  "type": "object",
15388
16558
  "properties": {
15389
16559
  "type": {
15390
16560
  "type": "string",
15391
16561
  "enum": [
15392
- "visualizationMetadata"
16562
+ "changePassword"
15393
16563
  ]
15394
16564
  },
16565
+ "require_new_password": {
16566
+ "type": "boolean",
16567
+ "description": "Whether the portal user must provide a new password. When false, the portal only asks the user to confirm (showing the configured explanation) and no new password is collected; the third-party system is expected to handle the password change. When true, the portal collects a new password and passes it to the third-party system as `{{Input.new_password}}`.\n",
16568
+ "default": false
16569
+ },
16570
+ "explanation": {
16571
+ "type": "object",
16572
+ "properties": {
16573
+ "en": {
16574
+ "type": "string",
16575
+ "description": "Explanation of the functionality shown to the end user.",
16576
+ "example": "You will receive an email with instructions to reset your password."
16577
+ }
16578
+ },
16579
+ "additionalProperties": {
16580
+ "type": "string",
16581
+ "description": "Explanation of the functionality in language denoted by ISO 3166-1 alpha-2 code."
16582
+ },
16583
+ "required": [
16584
+ "en"
16585
+ ],
16586
+ "description": "Optional explanation shown to the user in the change password confirmation dialog."
16587
+ },
15395
16588
  "auth": {
15396
16589
  "$ref": "#/components/schemas/ExtensionAuthBlock"
15397
16590
  },
@@ -15401,7 +16594,7 @@
15401
16594
  "method": {
15402
16595
  "type": "string",
15403
16596
  "description": "HTTP method to use for the call",
15404
- "default": "GET"
16597
+ "default": "POST"
15405
16598
  },
15406
16599
  "url": {
15407
16600
  "type": "string",
@@ -15425,29 +16618,17 @@
15425
16618
  },
15426
16619
  "body": {
15427
16620
  "type": "object",
15428
- "description": "Request body to send. Supports variable interpolation. Content format is determined by Content-Type header.",
15429
- "additionalProperties": {
15430
- "type": "string"
15431
- },
15432
- "default": {}
16621
+ "description": "Optional JSON body to use for the call. Defaults to an object with portal user context (and the new password as `{{Input.new_password}}` when `require_new_password` is true). Supports variable interpolation."
15433
16622
  }
15434
16623
  },
15435
16624
  "required": [
15436
- "url"
16625
+ "url",
16626
+ "headers"
15437
16627
  ]
15438
16628
  },
15439
16629
  "resolved": {
15440
16630
  "type": "object",
15441
16631
  "properties": {
15442
- "data_path": {
15443
- "type": "string",
15444
- "description": "Optional path to the metadata object in the response. If omitted, the metadata is assumed to be on the top level."
15445
- },
15446
- "dataPath": {
15447
- "type": "string",
15448
- "deprecated": true,
15449
- "description": "Deprecated. Use `data_path` instead."
15450
- },
15451
16632
  "error_message_path": {
15452
16633
  "type": "string",
15453
16634
  "description": "Optional path to a human-readable error message in the third-party response body, used when the call fails (non-2xx status).\nIf specified and the path resolves to a string, that message is forwarded to the end user instead of a generic error.\n",
@@ -15455,12 +16636,6 @@
15455
16636
  }
15456
16637
  }
15457
16638
  },
15458
- "use_static_ips": {
15459
- "type": "boolean",
15460
- "deprecated": true,
15461
- "description": "Deprecated. Prefer `secure_proxy` instead.\nIf true, requests are made from a set of static IP addresses and only allow connections to a set of allowed IP addresses. Get in touch with us to add your IP addresses.\n",
15462
- "default": false
15463
- },
15464
16639
  "secure_proxy": {
15465
16640
  "$ref": "#/components/schemas/SecureProxyConfig"
15466
16641
  }
@@ -15470,16 +16645,43 @@
15470
16645
  "call"
15471
16646
  ]
15472
16647
  },
15473
- "ExtensionHookCostDataRetrieval": {
15474
- "description": "Hook that will allow using the specified source as data for consumption visualizations. This hook is triggered to fetch the data. Format of the request and response has to follow the following specification: TBD. The expected response to the call is:\n - 200 with the time series data\n",
16648
+ "ExtensionHookDeleteAccount": {
16649
+ "description": "Hook that replaces the built-in delete account functionality for portal users. When configured, the portal does not delete the user itself. Instead, this hook makes an HTTP call to the third-party system, which is expected to handle the deletion.\nThe `deletion_mode` controls what the portal does after the call:\n - `synchronous`: The third-party system deletes the user immediately. The portal waits for a successful (2xx) response and then also deletes the epilot user.\n - `asynchronous`: The third-party system handles deletion out-of-band. The portal does not delete anything immediately; cleanup is expected to happen later (e.g. via the user deletion API or webhooks).\n\nThe expected response http status code to the call is:\n - 2xx if the request was accepted\n - non-2xx if the request failed (optionally with a human-readable message resolved via `resolved.error_message_path`)\n",
15475
16650
  "type": "object",
15476
16651
  "properties": {
15477
16652
  "type": {
15478
16653
  "type": "string",
15479
16654
  "enum": [
15480
- "costDataRetrieval"
16655
+ "deleteAccount"
15481
16656
  ]
15482
16657
  },
16658
+ "deletion_mode": {
16659
+ "type": "string",
16660
+ "enum": [
16661
+ "synchronous",
16662
+ "asynchronous"
16663
+ ],
16664
+ "description": "Controls how the account deletion is handled. `synchronous` waits for the third-party system to respond and then also deletes the epilot user. `asynchronous` hands the deletion over entirely to the third-party system and the portal does not delete anything immediately.\n",
16665
+ "default": "synchronous"
16666
+ },
16667
+ "explanation": {
16668
+ "type": "object",
16669
+ "properties": {
16670
+ "en": {
16671
+ "type": "string",
16672
+ "description": "Explanation of the functionality shown to the end user.",
16673
+ "example": "Your account deletion will be processed by our system. This may take a few days."
16674
+ }
16675
+ },
16676
+ "additionalProperties": {
16677
+ "type": "string",
16678
+ "description": "Explanation of the functionality in language denoted by ISO 3166-1 alpha-2 code."
16679
+ },
16680
+ "required": [
16681
+ "en"
16682
+ ],
16683
+ "description": "Optional explanation shown to the user in the delete account confirmation dialog."
16684
+ },
15483
16685
  "auth": {
15484
16686
  "$ref": "#/components/schemas/ExtensionAuthBlock"
15485
16687
  },
@@ -15489,7 +16691,7 @@
15489
16691
  "method": {
15490
16692
  "type": "string",
15491
16693
  "description": "HTTP method to use for the call",
15492
- "default": "GET"
16694
+ "default": "POST"
15493
16695
  },
15494
16696
  "url": {
15495
16697
  "type": "string",
@@ -15513,29 +16715,17 @@
15513
16715
  },
15514
16716
  "body": {
15515
16717
  "type": "object",
15516
- "description": "Request body to send. Supports variable interpolation. Content format is determined by Content-Type header.",
15517
- "additionalProperties": {
15518
- "type": "string"
15519
- },
15520
- "default": {}
16718
+ "description": "Optional JSON body to use for the call. Defaults to an object with portal user context, e.g. `{\"portal_user_id\": \"...\", \"email\": \"...\"}`. Supports variable interpolation."
15521
16719
  }
15522
16720
  },
15523
16721
  "required": [
15524
- "url"
16722
+ "url",
16723
+ "headers"
15525
16724
  ]
15526
16725
  },
15527
16726
  "resolved": {
15528
16727
  "type": "object",
15529
16728
  "properties": {
15530
- "data_path": {
15531
- "type": "string",
15532
- "description": "Optional path to the data (array) in the response. If omitted, the data is assumed to be on the top level."
15533
- },
15534
- "dataPath": {
15535
- "type": "string",
15536
- "deprecated": true,
15537
- "description": "Deprecated. Use `data_path` instead."
15538
- },
15539
16729
  "error_message_path": {
15540
16730
  "type": "string",
15541
16731
  "description": "Optional path to a human-readable error message in the third-party response body, used when the call fails (non-2xx status).\nIf specified and the path resolves to a string, that message is forwarded to the end user instead of a generic error.\n",
@@ -15543,12 +16733,6 @@
15543
16733
  }
15544
16734
  }
15545
16735
  },
15546
- "use_static_ips": {
15547
- "type": "boolean",
15548
- "deprecated": true,
15549
- "description": "Deprecated. Prefer `secure_proxy` instead.\nIf true, requests are made from a set of static IP addresses and only allow connections to a set of allowed IP addresses. Get in touch with us to add your IP addresses.\n",
15550
- "default": false
15551
- },
15552
16736
  "secure_proxy": {
15553
16737
  "$ref": "#/components/schemas/SecureProxyConfig"
15554
16738
  }
@@ -15886,6 +17070,11 @@
15886
17070
  },
15887
17071
  "mobile_oidc_config": {
15888
17072
  "$ref": "#/components/schemas/MoblieOIDCConfig"
17073
+ },
17074
+ "expose_client_secret": {
17075
+ "type": "boolean",
17076
+ "description": "Allow the resolved `client_secret` to be returned through the\npublic single-provider endpoint at SSO initiation. Only set this\nfor OIDC flows that require a public client secret in the\nbrowser (e.g. some PKCE-less authorization-code variants). When\nunset (default), the secret is kept server-side and only used at\nthe token exchange.\n",
17077
+ "example": false
15889
17078
  }
15890
17079
  },
15891
17080
  "required": [
@@ -16748,6 +17937,10 @@
16748
17937
  "auto_redirect_to_sso": {
16749
17938
  "type": "boolean",
16750
17939
  "description": "Decide whether to automatically redirect to the provider page during login, which would completely bypass showing the portal authentication page."
17940
+ },
17941
+ "prevent_user_enumeration": {
17942
+ "type": "boolean",
17943
+ "description": "Opt-in. When true, suppresses responses that reveal whether a user exists for public, pre-authentication actions (the login entry-point check and self-registration), at the expense of some UX. Already-authenticated actions are unaffected. Default false.\n"
16751
17944
  }
16752
17945
  }
16753
17946
  },