@epilot/cli 0.1.160 → 0.1.162
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.
- package/README.md +1 -1
- package/definitions/customer-portal.json +1102 -106
- package/dist/{access-token-WWE6BDJH.js → access-token-3AXUK6WE.js} +2 -1
- package/dist/{add-component-YSPAIR3D.js → add-component-5F5BWNLG.js} +2 -1
- package/dist/{add-function-6VYBWMRD.js → add-function-RK5AO3SP.js} +2 -1
- package/dist/{address-EH3C4CVB.js → address-KVQC366U.js} +2 -1
- package/dist/{address-suggestions-RRSLOBFW.js → address-suggestions-6FL2ISTG.js} +2 -1
- package/dist/{ai-agents-53M2KHPI.js → ai-agents-P6IRWDQS.js} +2 -1
- package/dist/{api-HKW3N3TV.js → api-J6BRSNPK.js} +2 -1
- package/dist/app-PM77ZCZD.js +26 -0
- package/dist/{audit-logs-YFRK3EFU.js → audit-logs-5E2V4AKK.js} +2 -1
- package/dist/{automation-4DEE3TUI.js → automation-EDPKHTEL.js} +2 -1
- package/dist/{billing-XX4VVOPI.js → billing-C3SSZYOR.js} +2 -1
- package/dist/bin/epilot.js +57 -57
- package/dist/{blueprint-manifest-V5MJ3GCA.js → blueprint-manifest-7MYLZZIP.js} +2 -1
- package/dist/{calendar-IHVASAEY.js → calendar-WFHS5BVO.js} +2 -1
- package/dist/{chunk-6HMS3A5C.js → chunk-5O46CUYA.js} +5 -1
- package/dist/chunk-7DPEBEAH.js +8 -0
- package/dist/{chunk-5ZFPCFKV.js → chunk-RVMXITS4.js} +4 -0
- package/dist/{chunk-PZQM3BWY.js → chunk-SWPN6I32.js} +7 -1
- package/dist/{completion-6CAE47TY.js → completion-I2DE6NUV.js} +1 -1
- package/dist/{configuration-hub-ZSYHJZXF.js → configuration-hub-WCBZZCLC.js} +2 -1
- package/dist/{consent-QICGZLMO.js → consent-3JLTRTJL.js} +2 -1
- package/dist/{customer-portal-UCDUZO6G.js → customer-portal-SG7CAMPB.js} +2 -1
- package/dist/{dashboard-YL5C562L.js → dashboard-WG6LVIMP.js} +2 -1
- package/dist/{data-governance-4VMCRLDZ.js → data-governance-Y4FFP6LP.js} +2 -1
- package/dist/{deduplication-RKRS2COL.js → deduplication-IJUWU73X.js} +2 -1
- package/dist/{deploy-3HGRSDVZ.js → deploy-U57GD35K.js} +2 -1
- package/dist/{design-CQWCY2IS.js → design-F6LOO3ZU.js} +2 -1
- package/dist/{dev-IYGZMQ5U.js → dev-IRBFS5LD.js} +2 -1
- package/dist/{document-JYWQBMSI.js → document-YPNERDN2.js} +2 -1
- package/dist/{email-settings-NG4J6IM6.js → email-settings-ISCVLGYS.js} +2 -1
- package/dist/{email-template-GVI34ZOC.js → email-template-PSLJDNOQ.js} +2 -1
- package/dist/{entity-AZTQCTNG.js → entity-TOBJDE3N.js} +2 -1
- package/dist/{entity-mapping-NN32XUJX.js → entity-mapping-FNAIZRJB.js} +2 -1
- package/dist/{environments-3ASNXU73.js → environments-IUN3DYCK.js} +2 -1
- package/dist/{event-catalog-JU7XOJLT.js → event-catalog-I4BVILKS.js} +2 -1
- package/dist/{export-TDFYIMNN.js → export-T3JQAI2B.js} +2 -1
- package/dist/{file-YDLNXKZR.js → file-ZLW3SXTF.js} +2 -1
- package/dist/{iban-7HDVKBUS.js → iban-4RPIPYDJ.js} +2 -1
- package/dist/{init-5K7TXE74.js → init-KPK3SHXV.js} +2 -1
- package/dist/{integration-toolkit-WBDJB76S.js → integration-toolkit-XK76FHPN.js} +2 -1
- package/dist/{journey-RZ7UI5ZA.js → journey-OJ62O6V4.js} +2 -1
- package/dist/{kanban-7CZGNU52.js → kanban-D4OHRHOH.js} +2 -1
- package/dist/{message-BFAKIAPX.js → message-JZOHDFE2.js} +2 -1
- package/dist/{metering-R4HXARRF.js → metering-4WS2G4AR.js} +2 -1
- package/dist/{notes-MHH5OZ2E.js → notes-XLRWLDCP.js} +2 -1
- package/dist/{notification-RPBIG3FC.js → notification-WM43FVUX.js} +2 -1
- package/dist/{organization-AUBBICVD.js → organization-T6C7ASGR.js} +2 -1
- package/dist/{partner-directory-FYZJUNAW.js → partner-directory-NJTFKC5M.js} +2 -1
- package/dist/{permissions-4TQQYZXO.js → permissions-OIQD2UP2.js} +2 -1
- package/dist/{pricing-SPMVLPRL.js → pricing-HSBVP5R4.js} +2 -1
- package/dist/{pricing-tier-7LVSXXMJ.js → pricing-tier-JWMMOO3B.js} +2 -1
- package/dist/{purpose-GVCQT7YJ.js → purpose-H7X66WEX.js} +2 -1
- package/dist/{query-5QIXDE32.js → query-CGI5CP4W.js} +2 -1
- package/dist/{remove-component-4XFMVBFW.js → remove-component-M3DYWZC3.js} +2 -1
- package/dist/{review-BD2ZOXCZ.js → review-LM4RC2YK.js} +2 -1
- package/dist/{sandbox-Q3XHXMFY.js → sandbox-AY5YBXVG.js} +2 -1
- package/dist/{sharing-SQJ355YL.js → sharing-2T2Y3RMD.js} +2 -1
- package/dist/{snapshot-AHEAQ3CU.js → snapshot-AFPML4RZ.js} +2 -1
- package/dist/{submission-WJUI3BYW.js → submission-TYX67QAH.js} +2 -1
- package/dist/{targeting-6QHPH3GP.js → targeting-I65D7X32.js} +2 -1
- package/dist/{template-variables-WVV7Y6HO.js → template-variables-KPXDO2VC.js} +2 -1
- package/dist/{upgrade-B6ZDZ7ZL.js → upgrade-EUM2GHS6.js} +1 -1
- package/dist/{user-TB64MVPS.js → user-YXRE2BDR.js} +2 -1
- package/dist/{validate-B5MJWBXP.js → validate-CCBSCMV6.js} +2 -1
- package/dist/{validation-rules-I3MUNH5H.js → validation-rules-MZIJRFR2.js} +2 -1
- package/dist/{versions-KZG3AZCT.js → versions-4BSJVGYQ.js} +2 -1
- package/dist/{webhooks-R2ZJHOWQ.js → webhooks-TKYGZHS3.js} +2 -1
- package/dist/{workflow-NBX7EDI7.js → workflow-PE5OUBFQ.js} +2 -1
- package/dist/{workflow-definition-R6VB7HO3.js → workflow-definition-PKKY5NHV.js} +2 -1
- package/package.json +1 -1
- package/dist/app-TCMEJ6XJ.js +0 -26
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"openapi": "3.0.3",
|
|
3
3
|
"info": {
|
|
4
4
|
"title": "Portal API",
|
|
5
|
-
"description": "Backend for epilot portals - End Customer Portal & Installer Portal",
|
|
5
|
+
"description": "Backend for epilot portals - End Customer Portal & Installer Portal\n",
|
|
6
6
|
"version": "1.0.0"
|
|
7
7
|
},
|
|
8
8
|
"tags": [
|
|
@@ -281,6 +281,7 @@
|
|
|
281
281
|
"PortalAuth": []
|
|
282
282
|
}
|
|
283
283
|
],
|
|
284
|
+
"x-contact-identification-token": true,
|
|
284
285
|
"responses": {
|
|
285
286
|
"204": {
|
|
286
287
|
"description": "Tokens is valid for the given organization."
|
|
@@ -2152,7 +2153,23 @@
|
|
|
2152
2153
|
"content": {
|
|
2153
2154
|
"application/json": {
|
|
2154
2155
|
"schema": {
|
|
2155
|
-
"
|
|
2156
|
+
"allOf": [
|
|
2157
|
+
{
|
|
2158
|
+
"$ref": "#/components/schemas/PortalConfig"
|
|
2159
|
+
},
|
|
2160
|
+
{
|
|
2161
|
+
"type": "object",
|
|
2162
|
+
"properties": {
|
|
2163
|
+
"identity_providers": {
|
|
2164
|
+
"type": "array",
|
|
2165
|
+
"description": "SSO identity providers configured for the portal, reduced to the\nfields needed to render provider login buttons. Omitted when the\nportal has no providers.\n",
|
|
2166
|
+
"items": {
|
|
2167
|
+
"$ref": "#/components/schemas/PublicIdentityProvider"
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
2172
|
+
]
|
|
2156
2173
|
}
|
|
2157
2174
|
}
|
|
2158
2175
|
}
|
|
@@ -3315,6 +3332,7 @@
|
|
|
3315
3332
|
"operationId": "getSchemas",
|
|
3316
3333
|
"summary": "getSchemas",
|
|
3317
3334
|
"description": "Retrieves the schemas. Only schemas usable in the private part of the portal are returned.",
|
|
3335
|
+
"x-contact-identification-token": true,
|
|
3318
3336
|
"tags": [
|
|
3319
3337
|
"ECP"
|
|
3320
3338
|
],
|
|
@@ -3685,6 +3703,7 @@
|
|
|
3685
3703
|
"operationId": "getContact",
|
|
3686
3704
|
"summary": "getContact",
|
|
3687
3705
|
"description": "Retrieves the contact of the logged in user.",
|
|
3706
|
+
"x-contact-identification-token": true,
|
|
3688
3707
|
"tags": [
|
|
3689
3708
|
"ECP"
|
|
3690
3709
|
],
|
|
@@ -3915,6 +3934,9 @@
|
|
|
3915
3934
|
"404": {
|
|
3916
3935
|
"$ref": "#/components/responses/NotFound"
|
|
3917
3936
|
},
|
|
3937
|
+
"429": {
|
|
3938
|
+
"$ref": "#/components/responses/TooManyRequests"
|
|
3939
|
+
},
|
|
3918
3940
|
"500": {
|
|
3919
3941
|
"$ref": "#/components/responses/InternalServerError"
|
|
3920
3942
|
}
|
|
@@ -4001,6 +4023,78 @@
|
|
|
4001
4023
|
"404": {
|
|
4002
4024
|
"$ref": "#/components/responses/NotFound"
|
|
4003
4025
|
},
|
|
4026
|
+
"429": {
|
|
4027
|
+
"$ref": "#/components/responses/TooManyRequests"
|
|
4028
|
+
},
|
|
4029
|
+
"500": {
|
|
4030
|
+
"$ref": "#/components/responses/InternalServerError"
|
|
4031
|
+
}
|
|
4032
|
+
}
|
|
4033
|
+
}
|
|
4034
|
+
},
|
|
4035
|
+
"/v3/portal/public/contact/identify": {
|
|
4036
|
+
"post": {
|
|
4037
|
+
"operationId": "identifyContact",
|
|
4038
|
+
"summary": "identifyContact",
|
|
4039
|
+
"description": "Identify a contact by the portal's configured registration identifiers and, on a match,\nissue a short-lived bearer token that acts as that contact.\n\nResolution is identical to `checkContactExistsV3`. The token does not grant a portal\nsession; it is accepted only on the operations listed in `allowed_operations`, and\nexpires at `expires_at`. Requires `surface_id` to name a surface of the portal whose\n`authentication` is `registration_identifiers`; returns 403 otherwise. The token is\nconfined to that surface's data access. Requests may be rate limited (429).\n",
|
|
4040
|
+
"security": [],
|
|
4041
|
+
"tags": [
|
|
4042
|
+
"Public"
|
|
4043
|
+
],
|
|
4044
|
+
"parameters": [
|
|
4045
|
+
{
|
|
4046
|
+
"in": "query",
|
|
4047
|
+
"name": "portal_id",
|
|
4048
|
+
"required": false,
|
|
4049
|
+
"schema": {
|
|
4050
|
+
"$ref": "#/components/schemas/PortalId"
|
|
4051
|
+
},
|
|
4052
|
+
"description": "PortalId of the portal (required if domain is not provided)"
|
|
4053
|
+
},
|
|
4054
|
+
{
|
|
4055
|
+
"in": "query",
|
|
4056
|
+
"name": "domain",
|
|
4057
|
+
"required": false,
|
|
4058
|
+
"schema": {
|
|
4059
|
+
"type": "string"
|
|
4060
|
+
},
|
|
4061
|
+
"description": "Portal domain for identification (alternative to portal_id)"
|
|
4062
|
+
}
|
|
4063
|
+
],
|
|
4064
|
+
"requestBody": {
|
|
4065
|
+
"description": "Request payload",
|
|
4066
|
+
"required": true,
|
|
4067
|
+
"content": {
|
|
4068
|
+
"application/json": {
|
|
4069
|
+
"schema": {
|
|
4070
|
+
"$ref": "#/components/schemas/ContactIdentifyRequest"
|
|
4071
|
+
}
|
|
4072
|
+
}
|
|
4073
|
+
}
|
|
4074
|
+
},
|
|
4075
|
+
"responses": {
|
|
4076
|
+
"200": {
|
|
4077
|
+
"description": "The identification result. On a match, `contact_id` and `token` are present. When no\ncontact matched, only `reason` is present - the response is deliberately shaped the same\nway for every negative outcome so it cannot be used to enumerate contacts.\n",
|
|
4078
|
+
"content": {
|
|
4079
|
+
"application/json": {
|
|
4080
|
+
"schema": {
|
|
4081
|
+
"$ref": "#/components/schemas/ContactIdentifyResponse"
|
|
4082
|
+
}
|
|
4083
|
+
}
|
|
4084
|
+
}
|
|
4085
|
+
},
|
|
4086
|
+
"400": {
|
|
4087
|
+
"$ref": "#/components/responses/InvalidRequest"
|
|
4088
|
+
},
|
|
4089
|
+
"403": {
|
|
4090
|
+
"$ref": "#/components/responses/Forbidden"
|
|
4091
|
+
},
|
|
4092
|
+
"404": {
|
|
4093
|
+
"$ref": "#/components/responses/NotFound"
|
|
4094
|
+
},
|
|
4095
|
+
"429": {
|
|
4096
|
+
"$ref": "#/components/responses/TooManyRequests"
|
|
4097
|
+
},
|
|
4004
4098
|
"500": {
|
|
4005
4099
|
"$ref": "#/components/responses/InternalServerError"
|
|
4006
4100
|
}
|
|
@@ -4075,6 +4169,9 @@
|
|
|
4075
4169
|
"404": {
|
|
4076
4170
|
"$ref": "#/components/responses/NotFound"
|
|
4077
4171
|
},
|
|
4172
|
+
"429": {
|
|
4173
|
+
"$ref": "#/components/responses/TooManyRequests"
|
|
4174
|
+
},
|
|
4078
4175
|
"500": {
|
|
4079
4176
|
"$ref": "#/components/responses/InternalServerError"
|
|
4080
4177
|
}
|
|
@@ -7588,6 +7685,7 @@
|
|
|
7588
7685
|
"PortalAuth": []
|
|
7589
7686
|
}
|
|
7590
7687
|
],
|
|
7688
|
+
"x-contact-identification-token": true,
|
|
7591
7689
|
"requestBody": {
|
|
7592
7690
|
"content": {
|
|
7593
7691
|
"application/json": {
|
|
@@ -7628,6 +7726,7 @@
|
|
|
7628
7726
|
"operationId": "searchPortalUserEntities",
|
|
7629
7727
|
"summary": "searchPortalUserEntities",
|
|
7630
7728
|
"description": "Search all entities of a portal user",
|
|
7729
|
+
"x-contact-identification-token": true,
|
|
7631
7730
|
"tags": [
|
|
7632
7731
|
"ECP"
|
|
7633
7732
|
],
|
|
@@ -8507,6 +8606,7 @@
|
|
|
8507
8606
|
"post": {
|
|
8508
8607
|
"operationId": "getMeterReadings",
|
|
8509
8608
|
"summary": "getMeterReadings",
|
|
8609
|
+
"x-contact-identification-token": true,
|
|
8510
8610
|
"description": "Fetches meter readings for a counter and optionally resolves Handlebars\ntemplate strings against each reading object using @epilot/variables.\n",
|
|
8511
8611
|
"tags": [
|
|
8512
8612
|
"ECP"
|
|
@@ -8711,6 +8811,68 @@
|
|
|
8711
8811
|
}
|
|
8712
8812
|
}
|
|
8713
8813
|
},
|
|
8814
|
+
"/v3/portal/public/sso/providers/{provider_slug}": {
|
|
8815
|
+
"get": {
|
|
8816
|
+
"operationId": "getPublicSSOProviderV3",
|
|
8817
|
+
"summary": "getPublicSSOProviderV3",
|
|
8818
|
+
"description": "Returns the public configuration of a single SSO identity provider with env var\nplaceholders (incl. secrets) already resolved against the organization's environment.\n\nPortal-scoped variant of getSSOProvider: the portal is identified by `org_id` +\n`portal_id` only, so callers without a portal domain (e.g. standalone journeys)\ncan resolve the provider.\nOnly the web OIDC configuration is returned: `mobile_oidc_config` is omitted\nentirely, and the web `client_secret` and `metadata.test_auth_*` credentials are\nnever returned — they are used server-side by the SSO callback to exchange the\nauthorization code for tokens. `oidc_config.has_client_secret` is set when either\nexists, so clients route the exchange through the callback.\n",
|
|
8819
|
+
"security": [],
|
|
8820
|
+
"tags": [
|
|
8821
|
+
"Public"
|
|
8822
|
+
],
|
|
8823
|
+
"parameters": [
|
|
8824
|
+
{
|
|
8825
|
+
"in": "path",
|
|
8826
|
+
"name": "provider_slug",
|
|
8827
|
+
"required": true,
|
|
8828
|
+
"description": "Provider slug (organization-unique)",
|
|
8829
|
+
"schema": {
|
|
8830
|
+
"$ref": "#/components/schemas/ProviderSlug"
|
|
8831
|
+
}
|
|
8832
|
+
},
|
|
8833
|
+
{
|
|
8834
|
+
"in": "query",
|
|
8835
|
+
"name": "org_id",
|
|
8836
|
+
"required": true,
|
|
8837
|
+
"description": "epilot organization id",
|
|
8838
|
+
"schema": {
|
|
8839
|
+
"type": "string",
|
|
8840
|
+
"example": 123
|
|
8841
|
+
}
|
|
8842
|
+
},
|
|
8843
|
+
{
|
|
8844
|
+
"in": "query",
|
|
8845
|
+
"name": "portal_id",
|
|
8846
|
+
"required": true,
|
|
8847
|
+
"description": "ID of the Portal",
|
|
8848
|
+
"schema": {
|
|
8849
|
+
"$ref": "#/components/schemas/PortalId"
|
|
8850
|
+
}
|
|
8851
|
+
}
|
|
8852
|
+
],
|
|
8853
|
+
"responses": {
|
|
8854
|
+
"200": {
|
|
8855
|
+
"description": "Resolved public SSO provider configuration, reduced to the web OIDC flow. `mobile_oidc_config` is omitted; `oidc_config.client_secret` and the `metadata.test_auth_*` credentials are stripped (kept server-side for the token exchange).",
|
|
8856
|
+
"content": {
|
|
8857
|
+
"application/json": {
|
|
8858
|
+
"schema": {
|
|
8859
|
+
"$ref": "#/components/schemas/ProviderPublicConfigV3"
|
|
8860
|
+
}
|
|
8861
|
+
}
|
|
8862
|
+
}
|
|
8863
|
+
},
|
|
8864
|
+
"400": {
|
|
8865
|
+
"$ref": "#/components/responses/InvalidRequest"
|
|
8866
|
+
},
|
|
8867
|
+
"404": {
|
|
8868
|
+
"$ref": "#/components/responses/NotFound"
|
|
8869
|
+
},
|
|
8870
|
+
"500": {
|
|
8871
|
+
"$ref": "#/components/responses/InternalServerError"
|
|
8872
|
+
}
|
|
8873
|
+
}
|
|
8874
|
+
}
|
|
8875
|
+
},
|
|
8714
8876
|
"/v2/portal/public/sso/login": {
|
|
8715
8877
|
"post": {
|
|
8716
8878
|
"operationId": "ssoLogin",
|
|
@@ -8856,6 +9018,11 @@
|
|
|
8856
9018
|
"properties": {
|
|
8857
9019
|
"provider_slug": {
|
|
8858
9020
|
"$ref": "#/components/schemas/ProviderSlug"
|
|
9021
|
+
},
|
|
9022
|
+
"login_only": {
|
|
9023
|
+
"type": "boolean",
|
|
9024
|
+
"default": false,
|
|
9025
|
+
"description": "Authenticate existing identities only. When no portal user and no contact matches the identity, the login fails with a 400 response carrying `reason: PORTAL_ACCOUNT_NOT_FOUND` instead of registering a new portal user. A matched contact is still provisioned a portal user; contacts and accounts are never created."
|
|
8859
9026
|
}
|
|
8860
9027
|
}
|
|
8861
9028
|
}
|
|
@@ -8882,6 +9049,9 @@
|
|
|
8882
9049
|
}
|
|
8883
9050
|
}
|
|
8884
9051
|
}
|
|
9052
|
+
},
|
|
9053
|
+
"400": {
|
|
9054
|
+
"$ref": "#/components/responses/InvalidRequest"
|
|
8885
9055
|
}
|
|
8886
9056
|
}
|
|
8887
9057
|
}
|
|
@@ -9153,6 +9323,7 @@
|
|
|
9153
9323
|
"EitherAuth": []
|
|
9154
9324
|
}
|
|
9155
9325
|
],
|
|
9326
|
+
"x-contact-identification-token": true,
|
|
9156
9327
|
"parameters": [
|
|
9157
9328
|
{
|
|
9158
9329
|
"in": "query",
|
|
@@ -10617,106 +10788,50 @@
|
|
|
10617
10788
|
}
|
|
10618
10789
|
}
|
|
10619
10790
|
},
|
|
10620
|
-
"/v3/portal/
|
|
10621
|
-
"get": {
|
|
10622
|
-
"operationId": "listAllPortalConfigs",
|
|
10623
|
-
"summary": "listAllPortalConfigs",
|
|
10624
|
-
"description": "Retrieves all portal configurations.",
|
|
10625
|
-
"tags": [
|
|
10626
|
-
"ECP Admin"
|
|
10627
|
-
],
|
|
10628
|
-
"security": [
|
|
10629
|
-
{
|
|
10630
|
-
"EpilotAuth": []
|
|
10631
|
-
}
|
|
10632
|
-
],
|
|
10633
|
-
"responses": {
|
|
10634
|
-
"200": {
|
|
10635
|
-
"description": "All portal configs retrieved successfully.",
|
|
10636
|
-
"content": {
|
|
10637
|
-
"application/json": {
|
|
10638
|
-
"schema": {
|
|
10639
|
-
"type": "object",
|
|
10640
|
-
"properties": {
|
|
10641
|
-
"data": {
|
|
10642
|
-
"type": "array",
|
|
10643
|
-
"items": {
|
|
10644
|
-
"$ref": "#/components/schemas/PortalConfigV3"
|
|
10645
|
-
}
|
|
10646
|
-
}
|
|
10647
|
-
}
|
|
10648
|
-
}
|
|
10649
|
-
}
|
|
10650
|
-
}
|
|
10651
|
-
},
|
|
10652
|
-
"401": {
|
|
10653
|
-
"$ref": "#/components/responses/Unauthorized"
|
|
10654
|
-
},
|
|
10655
|
-
"403": {
|
|
10656
|
-
"$ref": "#/components/responses/Forbidden"
|
|
10657
|
-
},
|
|
10658
|
-
"500": {
|
|
10659
|
-
"$ref": "#/components/responses/InternalServerError"
|
|
10660
|
-
}
|
|
10661
|
-
}
|
|
10662
|
-
}
|
|
10663
|
-
},
|
|
10664
|
-
"/v3/portal/config/swap": {
|
|
10791
|
+
"/v3/portal/config/{portal_id}/revisions": {
|
|
10665
10792
|
"post": {
|
|
10666
|
-
"operationId": "
|
|
10667
|
-
"summary": "
|
|
10668
|
-
"description": "
|
|
10793
|
+
"operationId": "createPortalRevision",
|
|
10794
|
+
"summary": "createPortalRevision",
|
|
10795
|
+
"description": "Creates a new revision — a complete, immutable snapshot of the portal's configuration, pages and email templates. Nothing about the live portal changes; the snapshot only becomes live when it is published.\n\nThe payload must be COMPLETE. This endpoint does not merge against live or against the previous revision: publishing a revision deletes every live page the revision does not contain. The server validates structure only — `pages` present, each page carrying `id`, `slug`, `order` and `blocks`, and no two pages sharing an `id` or a `slug`. Semantic completeness of the config is a promise the caller makes, and a partial payload is honoured rather than rejected.\n\n`email_templates` is optional, and absence means \"keep the portal's current templates\", never \"no templates\". A present but partial map is taken verbatim.\n",
|
|
10669
10796
|
"tags": [
|
|
10670
10797
|
"ECP Admin"
|
|
10671
10798
|
],
|
|
10672
|
-
"
|
|
10799
|
+
"parameters": [
|
|
10673
10800
|
{
|
|
10674
|
-
"
|
|
10801
|
+
"in": "path",
|
|
10802
|
+
"name": "portal_id",
|
|
10803
|
+
"required": true,
|
|
10804
|
+
"schema": {
|
|
10805
|
+
"type": "string",
|
|
10806
|
+
"format": "uuid",
|
|
10807
|
+
"example": "5da0a718-c822-403d-9f5d-20d4584e0528"
|
|
10808
|
+
},
|
|
10809
|
+
"description": "Portal ID (readonly UUID generated on portal creation)"
|
|
10675
10810
|
}
|
|
10676
10811
|
],
|
|
10677
10812
|
"requestBody": {
|
|
10678
|
-
"description": "
|
|
10813
|
+
"description": "Complete portal configuration snapshot",
|
|
10679
10814
|
"required": true,
|
|
10680
10815
|
"content": {
|
|
10681
10816
|
"application/json": {
|
|
10682
10817
|
"schema": {
|
|
10683
|
-
"
|
|
10684
|
-
"required": [
|
|
10685
|
-
"source_portal_id",
|
|
10686
|
-
"target_portal_id"
|
|
10687
|
-
],
|
|
10688
|
-
"properties": {
|
|
10689
|
-
"source_portal_id": {
|
|
10690
|
-
"$ref": "#/components/schemas/PortalId"
|
|
10691
|
-
},
|
|
10692
|
-
"target_portal_id": {
|
|
10693
|
-
"$ref": "#/components/schemas/PortalId"
|
|
10694
|
-
},
|
|
10695
|
-
"items_to_swap": {
|
|
10696
|
-
"type": "array",
|
|
10697
|
-
"items": {
|
|
10698
|
-
"$ref": "#/components/schemas/SwappableConfig"
|
|
10699
|
-
},
|
|
10700
|
-
"description": "Optional, opt-in configuration items to additionally swap on top of the always-swapped pages and functional config. Defaults to an empty list (nothing extra swapped). Domain and access/security settings can never be swapped."
|
|
10701
|
-
}
|
|
10702
|
-
}
|
|
10818
|
+
"$ref": "#/components/schemas/PortalRevisionRequest"
|
|
10703
10819
|
}
|
|
10704
10820
|
}
|
|
10705
10821
|
}
|
|
10706
10822
|
},
|
|
10823
|
+
"security": [
|
|
10824
|
+
{
|
|
10825
|
+
"EpilotAuth": []
|
|
10826
|
+
}
|
|
10827
|
+
],
|
|
10707
10828
|
"responses": {
|
|
10708
|
-
"
|
|
10709
|
-
"description": "
|
|
10829
|
+
"201": {
|
|
10830
|
+
"description": "Revision created successfully.",
|
|
10710
10831
|
"content": {
|
|
10711
10832
|
"application/json": {
|
|
10712
10833
|
"schema": {
|
|
10713
|
-
"
|
|
10714
|
-
"properties": {
|
|
10715
|
-
"message": {
|
|
10716
|
-
"type": "string",
|
|
10717
|
-
"example": "Domain and users swapped successfully."
|
|
10718
|
-
}
|
|
10719
|
-
}
|
|
10834
|
+
"$ref": "#/components/schemas/PortalRevisionCreated"
|
|
10720
10835
|
}
|
|
10721
10836
|
}
|
|
10722
10837
|
}
|
|
@@ -10733,28 +10848,367 @@
|
|
|
10733
10848
|
"404": {
|
|
10734
10849
|
"$ref": "#/components/responses/NotFound"
|
|
10735
10850
|
},
|
|
10851
|
+
"409": {
|
|
10852
|
+
"$ref": "#/components/responses/Conflict"
|
|
10853
|
+
},
|
|
10736
10854
|
"500": {
|
|
10737
10855
|
"$ref": "#/components/responses/InternalServerError"
|
|
10738
10856
|
}
|
|
10739
10857
|
}
|
|
10740
|
-
}
|
|
10741
|
-
|
|
10742
|
-
|
|
10743
|
-
|
|
10744
|
-
"
|
|
10745
|
-
"summary": "clonePortalConfig",
|
|
10746
|
-
"description": "Creates a new portal by cloning configuration and pages from an existing portal. The new portal gets its own domain, users, email templates, and authentication settings.",
|
|
10858
|
+
},
|
|
10859
|
+
"get": {
|
|
10860
|
+
"operationId": "listPortalRevisions",
|
|
10861
|
+
"summary": "listPortalRevisions",
|
|
10862
|
+
"description": "Lists a portal's revision history, newest first. Metadata only — no config blob, no page bodies. `is_published` says whether that revision is the one currently live, which is a different question from `published_at`, which records the last time it was published.\n",
|
|
10747
10863
|
"tags": [
|
|
10748
10864
|
"ECP Admin"
|
|
10749
10865
|
],
|
|
10750
|
-
"
|
|
10866
|
+
"parameters": [
|
|
10751
10867
|
{
|
|
10752
|
-
"
|
|
10753
|
-
|
|
10754
|
-
|
|
10755
|
-
|
|
10756
|
-
|
|
10757
|
-
|
|
10868
|
+
"in": "path",
|
|
10869
|
+
"name": "portal_id",
|
|
10870
|
+
"required": true,
|
|
10871
|
+
"schema": {
|
|
10872
|
+
"type": "string",
|
|
10873
|
+
"format": "uuid",
|
|
10874
|
+
"example": "5da0a718-c822-403d-9f5d-20d4584e0528"
|
|
10875
|
+
},
|
|
10876
|
+
"description": "Portal ID (readonly UUID generated on portal creation)"
|
|
10877
|
+
},
|
|
10878
|
+
{
|
|
10879
|
+
"in": "query",
|
|
10880
|
+
"name": "limit",
|
|
10881
|
+
"required": false,
|
|
10882
|
+
"schema": {
|
|
10883
|
+
"type": "integer",
|
|
10884
|
+
"minimum": 1,
|
|
10885
|
+
"maximum": 100,
|
|
10886
|
+
"default": 25
|
|
10887
|
+
},
|
|
10888
|
+
"description": "Maximum number of revisions to return"
|
|
10889
|
+
},
|
|
10890
|
+
{
|
|
10891
|
+
"in": "query",
|
|
10892
|
+
"name": "cursor",
|
|
10893
|
+
"required": false,
|
|
10894
|
+
"schema": {
|
|
10895
|
+
"type": "string"
|
|
10896
|
+
},
|
|
10897
|
+
"description": "Opaque pagination cursor, taken from a previous response's `next_cursor`"
|
|
10898
|
+
}
|
|
10899
|
+
],
|
|
10900
|
+
"security": [
|
|
10901
|
+
{
|
|
10902
|
+
"EpilotAuth": []
|
|
10903
|
+
}
|
|
10904
|
+
],
|
|
10905
|
+
"responses": {
|
|
10906
|
+
"200": {
|
|
10907
|
+
"description": "Revision history retrieved successfully.",
|
|
10908
|
+
"content": {
|
|
10909
|
+
"application/json": {
|
|
10910
|
+
"schema": {
|
|
10911
|
+
"$ref": "#/components/schemas/PortalRevisionList"
|
|
10912
|
+
}
|
|
10913
|
+
}
|
|
10914
|
+
}
|
|
10915
|
+
},
|
|
10916
|
+
"400": {
|
|
10917
|
+
"$ref": "#/components/responses/InvalidRequest"
|
|
10918
|
+
},
|
|
10919
|
+
"401": {
|
|
10920
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
10921
|
+
},
|
|
10922
|
+
"403": {
|
|
10923
|
+
"$ref": "#/components/responses/Forbidden"
|
|
10924
|
+
},
|
|
10925
|
+
"404": {
|
|
10926
|
+
"$ref": "#/components/responses/NotFound"
|
|
10927
|
+
},
|
|
10928
|
+
"500": {
|
|
10929
|
+
"$ref": "#/components/responses/InternalServerError"
|
|
10930
|
+
}
|
|
10931
|
+
}
|
|
10932
|
+
}
|
|
10933
|
+
},
|
|
10934
|
+
"/v3/portal/config/{portal_id}/revisions/{revision_id}": {
|
|
10935
|
+
"get": {
|
|
10936
|
+
"operationId": "getPortalRevision",
|
|
10937
|
+
"summary": "getPortalRevision",
|
|
10938
|
+
"description": "Returns the full content of one revision: the snapshotted config, its pages (in the live `Page` shape), email templates and identity providers with secrets redacted.\n\nThis is a pure read. The server records nothing about it: no \"loaded\" marker, no audit entry, and no change to which revision is live. Revision content re-enters the system only as a new `POST .../revisions`.\n\nSecret-typed extension option values are removed from `config` entirely, not masked.\n\nReturns `409` when the revision exists but its stored config or email templates row cannot be read, rather than a partial snapshot. Saving again produces a complete revision.\n",
|
|
10939
|
+
"tags": [
|
|
10940
|
+
"ECP Admin"
|
|
10941
|
+
],
|
|
10942
|
+
"parameters": [
|
|
10943
|
+
{
|
|
10944
|
+
"in": "path",
|
|
10945
|
+
"name": "portal_id",
|
|
10946
|
+
"required": true,
|
|
10947
|
+
"schema": {
|
|
10948
|
+
"type": "string",
|
|
10949
|
+
"format": "uuid",
|
|
10950
|
+
"example": "5da0a718-c822-403d-9f5d-20d4584e0528"
|
|
10951
|
+
},
|
|
10952
|
+
"description": "Portal ID (readonly UUID generated on portal creation)"
|
|
10953
|
+
},
|
|
10954
|
+
{
|
|
10955
|
+
"in": "path",
|
|
10956
|
+
"name": "revision_id",
|
|
10957
|
+
"required": true,
|
|
10958
|
+
"schema": {
|
|
10959
|
+
"type": "string",
|
|
10960
|
+
"example": "2026-08-25T14:03:11.482Z-a7f3c1d9"
|
|
10961
|
+
},
|
|
10962
|
+
"description": "Revision ID. Contains `:` characters — percent-encode it."
|
|
10963
|
+
}
|
|
10964
|
+
],
|
|
10965
|
+
"security": [
|
|
10966
|
+
{
|
|
10967
|
+
"EpilotAuth": []
|
|
10968
|
+
}
|
|
10969
|
+
],
|
|
10970
|
+
"responses": {
|
|
10971
|
+
"200": {
|
|
10972
|
+
"description": "Revision retrieved successfully.",
|
|
10973
|
+
"content": {
|
|
10974
|
+
"application/json": {
|
|
10975
|
+
"schema": {
|
|
10976
|
+
"$ref": "#/components/schemas/PortalRevision"
|
|
10977
|
+
}
|
|
10978
|
+
}
|
|
10979
|
+
}
|
|
10980
|
+
},
|
|
10981
|
+
"400": {
|
|
10982
|
+
"$ref": "#/components/responses/InvalidRequest"
|
|
10983
|
+
},
|
|
10984
|
+
"401": {
|
|
10985
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
10986
|
+
},
|
|
10987
|
+
"403": {
|
|
10988
|
+
"$ref": "#/components/responses/Forbidden"
|
|
10989
|
+
},
|
|
10990
|
+
"404": {
|
|
10991
|
+
"$ref": "#/components/responses/NotFound"
|
|
10992
|
+
},
|
|
10993
|
+
"409": {
|
|
10994
|
+
"$ref": "#/components/responses/Conflict"
|
|
10995
|
+
},
|
|
10996
|
+
"500": {
|
|
10997
|
+
"$ref": "#/components/responses/InternalServerError"
|
|
10998
|
+
}
|
|
10999
|
+
}
|
|
11000
|
+
}
|
|
11001
|
+
},
|
|
11002
|
+
"/v3/portal/config/{portal_id}/publish": {
|
|
11003
|
+
"post": {
|
|
11004
|
+
"operationId": "publishPortalRevision",
|
|
11005
|
+
"summary": "publishPortalRevision",
|
|
11006
|
+
"description": "Makes one revision the portal's live configuration, atomically: either everything below takes effect or nothing does. Publish is a full-snapshot replace: it writes config, every page and email templates to match the revision exactly, and deletes every live page the revision does not contain.\n\nAccepts any valid `revision_id` for the portal, old or new. There is no separate rollback endpoint and none is needed.\n\nThe same atomic publish stamps `name`, `description` and `published_at` onto the revision being published — permanently, on that revision, and not on any later one that supersedes it as live. `name` is generated server-side when the request omits it, so every published revision carries one.\n\nConflict detection covers the live config only: publish is rejected with `409` when the live config changed after publish read it. Live pages, their redirect routes and the email templates are replaced outright — a concurrent edit to a live page is not detected and is overwritten. `409` is also returned when the revision's stored config or email templates cannot be read, or when two of its pages would resolve to the same slug. A domain the revision claims that another portal already owns is a `400`.\n",
|
|
11007
|
+
"tags": [
|
|
11008
|
+
"ECP Admin"
|
|
11009
|
+
],
|
|
11010
|
+
"parameters": [
|
|
11011
|
+
{
|
|
11012
|
+
"in": "path",
|
|
11013
|
+
"name": "portal_id",
|
|
11014
|
+
"required": true,
|
|
11015
|
+
"schema": {
|
|
11016
|
+
"type": "string",
|
|
11017
|
+
"format": "uuid",
|
|
11018
|
+
"example": "5da0a718-c822-403d-9f5d-20d4584e0528"
|
|
11019
|
+
},
|
|
11020
|
+
"description": "Portal ID (readonly UUID generated on portal creation)"
|
|
11021
|
+
}
|
|
11022
|
+
],
|
|
11023
|
+
"requestBody": {
|
|
11024
|
+
"description": "The revision to publish",
|
|
11025
|
+
"required": true,
|
|
11026
|
+
"content": {
|
|
11027
|
+
"application/json": {
|
|
11028
|
+
"schema": {
|
|
11029
|
+
"$ref": "#/components/schemas/PublishRevisionRequest"
|
|
11030
|
+
}
|
|
11031
|
+
}
|
|
11032
|
+
}
|
|
11033
|
+
},
|
|
11034
|
+
"security": [
|
|
11035
|
+
{
|
|
11036
|
+
"EpilotAuth": []
|
|
11037
|
+
}
|
|
11038
|
+
],
|
|
11039
|
+
"responses": {
|
|
11040
|
+
"200": {
|
|
11041
|
+
"description": "Revision published successfully.",
|
|
11042
|
+
"content": {
|
|
11043
|
+
"application/json": {
|
|
11044
|
+
"schema": {
|
|
11045
|
+
"$ref": "#/components/schemas/PublishResult"
|
|
11046
|
+
}
|
|
11047
|
+
}
|
|
11048
|
+
}
|
|
11049
|
+
},
|
|
11050
|
+
"400": {
|
|
11051
|
+
"$ref": "#/components/responses/InvalidRequest"
|
|
11052
|
+
},
|
|
11053
|
+
"401": {
|
|
11054
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
11055
|
+
},
|
|
11056
|
+
"403": {
|
|
11057
|
+
"$ref": "#/components/responses/Forbidden"
|
|
11058
|
+
},
|
|
11059
|
+
"404": {
|
|
11060
|
+
"$ref": "#/components/responses/NotFound"
|
|
11061
|
+
},
|
|
11062
|
+
"409": {
|
|
11063
|
+
"$ref": "#/components/responses/Conflict"
|
|
11064
|
+
},
|
|
11065
|
+
"500": {
|
|
11066
|
+
"$ref": "#/components/responses/InternalServerError"
|
|
11067
|
+
},
|
|
11068
|
+
"503": {
|
|
11069
|
+
"$ref": "#/components/responses/ServiceUnavailable"
|
|
11070
|
+
}
|
|
11071
|
+
}
|
|
11072
|
+
}
|
|
11073
|
+
},
|
|
11074
|
+
"/v3/portal/configs": {
|
|
11075
|
+
"get": {
|
|
11076
|
+
"operationId": "listAllPortalConfigs",
|
|
11077
|
+
"summary": "listAllPortalConfigs",
|
|
11078
|
+
"description": "Retrieves all portal configurations.",
|
|
11079
|
+
"tags": [
|
|
11080
|
+
"ECP Admin"
|
|
11081
|
+
],
|
|
11082
|
+
"security": [
|
|
11083
|
+
{
|
|
11084
|
+
"EpilotAuth": []
|
|
11085
|
+
}
|
|
11086
|
+
],
|
|
11087
|
+
"responses": {
|
|
11088
|
+
"200": {
|
|
11089
|
+
"description": "All portal configs retrieved successfully.",
|
|
11090
|
+
"content": {
|
|
11091
|
+
"application/json": {
|
|
11092
|
+
"schema": {
|
|
11093
|
+
"type": "object",
|
|
11094
|
+
"properties": {
|
|
11095
|
+
"data": {
|
|
11096
|
+
"type": "array",
|
|
11097
|
+
"items": {
|
|
11098
|
+
"$ref": "#/components/schemas/PortalConfigV3"
|
|
11099
|
+
}
|
|
11100
|
+
}
|
|
11101
|
+
}
|
|
11102
|
+
}
|
|
11103
|
+
}
|
|
11104
|
+
}
|
|
11105
|
+
},
|
|
11106
|
+
"401": {
|
|
11107
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
11108
|
+
},
|
|
11109
|
+
"403": {
|
|
11110
|
+
"$ref": "#/components/responses/Forbidden"
|
|
11111
|
+
},
|
|
11112
|
+
"500": {
|
|
11113
|
+
"$ref": "#/components/responses/InternalServerError"
|
|
11114
|
+
}
|
|
11115
|
+
}
|
|
11116
|
+
}
|
|
11117
|
+
},
|
|
11118
|
+
"/v3/portal/config/swap": {
|
|
11119
|
+
"post": {
|
|
11120
|
+
"operationId": "swapPortalConfig",
|
|
11121
|
+
"summary": "swapPortalConfig",
|
|
11122
|
+
"description": "Swaps the portal configuration of two portals.",
|
|
11123
|
+
"tags": [
|
|
11124
|
+
"ECP Admin"
|
|
11125
|
+
],
|
|
11126
|
+
"security": [
|
|
11127
|
+
{
|
|
11128
|
+
"EpilotAuth": []
|
|
11129
|
+
}
|
|
11130
|
+
],
|
|
11131
|
+
"requestBody": {
|
|
11132
|
+
"description": "Source and target portal IDs",
|
|
11133
|
+
"required": true,
|
|
11134
|
+
"content": {
|
|
11135
|
+
"application/json": {
|
|
11136
|
+
"schema": {
|
|
11137
|
+
"type": "object",
|
|
11138
|
+
"required": [
|
|
11139
|
+
"source_portal_id",
|
|
11140
|
+
"target_portal_id"
|
|
11141
|
+
],
|
|
11142
|
+
"properties": {
|
|
11143
|
+
"source_portal_id": {
|
|
11144
|
+
"$ref": "#/components/schemas/PortalId"
|
|
11145
|
+
},
|
|
11146
|
+
"target_portal_id": {
|
|
11147
|
+
"$ref": "#/components/schemas/PortalId"
|
|
11148
|
+
},
|
|
11149
|
+
"items_to_swap": {
|
|
11150
|
+
"type": "array",
|
|
11151
|
+
"items": {
|
|
11152
|
+
"$ref": "#/components/schemas/SwappableConfig"
|
|
11153
|
+
},
|
|
11154
|
+
"description": "Optional, opt-in configuration items to additionally swap on top of the always-swapped pages and functional config. Defaults to an empty list (nothing extra swapped). Domain and access/security settings can never be swapped."
|
|
11155
|
+
}
|
|
11156
|
+
}
|
|
11157
|
+
}
|
|
11158
|
+
}
|
|
11159
|
+
}
|
|
11160
|
+
},
|
|
11161
|
+
"responses": {
|
|
11162
|
+
"200": {
|
|
11163
|
+
"description": "Domain and users swapped successfully.",
|
|
11164
|
+
"content": {
|
|
11165
|
+
"application/json": {
|
|
11166
|
+
"schema": {
|
|
11167
|
+
"type": "object",
|
|
11168
|
+
"properties": {
|
|
11169
|
+
"message": {
|
|
11170
|
+
"type": "string",
|
|
11171
|
+
"example": "Domain and users swapped successfully."
|
|
11172
|
+
}
|
|
11173
|
+
}
|
|
11174
|
+
}
|
|
11175
|
+
}
|
|
11176
|
+
}
|
|
11177
|
+
},
|
|
11178
|
+
"400": {
|
|
11179
|
+
"$ref": "#/components/responses/InvalidRequest"
|
|
11180
|
+
},
|
|
11181
|
+
"401": {
|
|
11182
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
11183
|
+
},
|
|
11184
|
+
"403": {
|
|
11185
|
+
"$ref": "#/components/responses/Forbidden"
|
|
11186
|
+
},
|
|
11187
|
+
"404": {
|
|
11188
|
+
"$ref": "#/components/responses/NotFound"
|
|
11189
|
+
},
|
|
11190
|
+
"500": {
|
|
11191
|
+
"$ref": "#/components/responses/InternalServerError"
|
|
11192
|
+
}
|
|
11193
|
+
}
|
|
11194
|
+
}
|
|
11195
|
+
},
|
|
11196
|
+
"/v3/portal/config/clone": {
|
|
11197
|
+
"post": {
|
|
11198
|
+
"operationId": "clonePortalConfig",
|
|
11199
|
+
"summary": "clonePortalConfig",
|
|
11200
|
+
"description": "Creates a new portal by cloning configuration and pages from an existing portal. The new portal gets its own domain, users, email templates, and authentication settings.",
|
|
11201
|
+
"tags": [
|
|
11202
|
+
"ECP Admin"
|
|
11203
|
+
],
|
|
11204
|
+
"security": [
|
|
11205
|
+
{
|
|
11206
|
+
"EpilotAuth": []
|
|
11207
|
+
}
|
|
11208
|
+
],
|
|
11209
|
+
"requestBody": {
|
|
11210
|
+
"description": "Source portal ID and optional name for the cloned portal",
|
|
11211
|
+
"required": true,
|
|
10758
11212
|
"content": {
|
|
10759
11213
|
"application/json": {
|
|
10760
11214
|
"schema": {
|
|
@@ -12939,6 +13393,13 @@
|
|
|
12939
13393
|
}
|
|
12940
13394
|
]
|
|
12941
13395
|
},
|
|
13396
|
+
"surfaces": {
|
|
13397
|
+
"type": "array",
|
|
13398
|
+
"description": "Surfaces this portal's data is reached from besides the portal UI itself (public journeys on the website, chat). Configured under Security > Surfaces. Each surface defines how a caller authenticates and what data access applies on it; the portal UI is the implicit default surface (login, default scope) and is not listed here. A surface with `authentication: registration_identifiers` is what makes `identifyContact` issue tokens for this portal: without one, `identifyContact` returns 403.\n",
|
|
13399
|
+
"items": {
|
|
13400
|
+
"$ref": "#/components/schemas/PortalSurface"
|
|
13401
|
+
}
|
|
13402
|
+
},
|
|
12942
13403
|
"contact_identifiers_for_account": {
|
|
12943
13404
|
"type": "array",
|
|
12944
13405
|
"description": "Account-mode only. Identifiers on the contact entity of the primarily\nidentified account. Used to pick an existing related contact within the\nresolved account; if none matches, the values are written onto the new\ncontact that is created and linked to the account.\n",
|
|
@@ -13088,6 +13549,12 @@
|
|
|
13088
13549
|
"type": "boolean",
|
|
13089
13550
|
"description": "Whether this is a v3 portal configuration"
|
|
13090
13551
|
},
|
|
13552
|
+
"published_revision_id": {
|
|
13553
|
+
"type": "string",
|
|
13554
|
+
"readOnly": true,
|
|
13555
|
+
"description": "The revision currently live on this portal. Absent until the first publish.",
|
|
13556
|
+
"example": "2026-08-25T14:03:11.482Z-a7f3c1d9"
|
|
13557
|
+
},
|
|
13091
13558
|
"portal_id": {
|
|
13092
13559
|
"$ref": "#/components/schemas/PortalId"
|
|
13093
13560
|
},
|
|
@@ -13636,6 +14103,79 @@
|
|
|
13636
14103
|
}
|
|
13637
14104
|
}
|
|
13638
14105
|
},
|
|
14106
|
+
"ContactIdentifyRequest": {
|
|
14107
|
+
"description": "ContactExistsRequest plus the surface the token is requested for.\n",
|
|
14108
|
+
"allOf": [
|
|
14109
|
+
{
|
|
14110
|
+
"$ref": "#/components/schemas/ContactExistsRequest"
|
|
14111
|
+
},
|
|
14112
|
+
{
|
|
14113
|
+
"type": "object",
|
|
14114
|
+
"required": [
|
|
14115
|
+
"surface_id"
|
|
14116
|
+
],
|
|
14117
|
+
"properties": {
|
|
14118
|
+
"surface_id": {
|
|
14119
|
+
"type": "string",
|
|
14120
|
+
"description": "Id of the portal surface (see `surfaces` on the portal config) this token is\nfor. The surface must exist and use `authentication: registration_identifiers`;\notherwise the call returns 403. The issued token is bound to this surface and\nconfined to its data access settings.\n",
|
|
14121
|
+
"example": "website-journeys"
|
|
14122
|
+
}
|
|
14123
|
+
}
|
|
14124
|
+
}
|
|
14125
|
+
]
|
|
14126
|
+
},
|
|
14127
|
+
"ContactIdentifyResponse": {
|
|
14128
|
+
"type": "object",
|
|
14129
|
+
"properties": {
|
|
14130
|
+
"contact_id": {
|
|
14131
|
+
"$ref": "#/components/schemas/EntityId",
|
|
14132
|
+
"description": "ID of the identified contact. Present only on a match."
|
|
14133
|
+
},
|
|
14134
|
+
"account_id": {
|
|
14135
|
+
"$ref": "#/components/schemas/EntityId",
|
|
14136
|
+
"description": "ID of the resolved account when the portal is configured for account-based\nregistration. Present only on a match.\n"
|
|
14137
|
+
},
|
|
14138
|
+
"token": {
|
|
14139
|
+
"type": "string",
|
|
14140
|
+
"description": "One-time bearer token scoped to the identified contact, to be sent as\n`Authorization: Bearer <token>` against the portal APIs. Present only on a match.\n",
|
|
14141
|
+
"example": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
|
|
14142
|
+
},
|
|
14143
|
+
"token_type": {
|
|
14144
|
+
"type": "string",
|
|
14145
|
+
"enum": [
|
|
14146
|
+
"contact_identification"
|
|
14147
|
+
],
|
|
14148
|
+
"description": "Type of the issued token, matching its own `token_type` claim and the token type in access-token-api. Present only on a match."
|
|
14149
|
+
},
|
|
14150
|
+
"surface_id": {
|
|
14151
|
+
"type": "string",
|
|
14152
|
+
"description": "The surface the token is bound to; echoes the request. Present only on a match.",
|
|
14153
|
+
"example": "website-journeys"
|
|
14154
|
+
},
|
|
14155
|
+
"expires_at": {
|
|
14156
|
+
"type": "string",
|
|
14157
|
+
"format": "date-time",
|
|
14158
|
+
"description": "When the issued token stops being accepted. Present only on a match.",
|
|
14159
|
+
"example": "2026-08-11T10:35:00.000Z"
|
|
14160
|
+
},
|
|
14161
|
+
"allowed_operations": {
|
|
14162
|
+
"type": "array",
|
|
14163
|
+
"description": "The operationIds the issued token may call. Every other operation rejects the token\nwith 401, including read operations. Echoed so a caller does not have to infer the\nsurface from this specification, and so a change to the allowlist is visible at\nruntime. Present only on a match.\n",
|
|
14164
|
+
"items": {
|
|
14165
|
+
"type": "string"
|
|
14166
|
+
},
|
|
14167
|
+
"example": []
|
|
14168
|
+
},
|
|
14169
|
+
"reason": {
|
|
14170
|
+
"type": "string",
|
|
14171
|
+
"enum": [
|
|
14172
|
+
"TIMEOUT",
|
|
14173
|
+
"NOT_FOUND"
|
|
14174
|
+
],
|
|
14175
|
+
"description": "Present only when no token was issued. NOT_FOUND means the given identifiers did not\nmatch any contact (definitive - the client should not retry). TIMEOUT means the contact\nwas not found within the processing window but may still be ingesting; the client may\nretry (ideally with trigger_identifiers_check=false).\n"
|
|
14176
|
+
}
|
|
14177
|
+
}
|
|
14178
|
+
},
|
|
13639
14179
|
"AccountExistsRequest": {
|
|
13640
14180
|
"type": "object",
|
|
13641
14181
|
"required": [
|
|
@@ -15121,6 +15661,94 @@
|
|
|
15121
15661
|
}
|
|
15122
15662
|
}
|
|
15123
15663
|
},
|
|
15664
|
+
"PortalSurface": {
|
|
15665
|
+
"type": "object",
|
|
15666
|
+
"description": "One surface a portal's data is reached from (see `surfaces` on the portal config).\n\nA surface names how callers authenticate on it and what they may reach once they\nhave. Data access is always a subset of the portal's own: a schema not in\n`allowed_portal_entities` cannot be opened up by a surface, and the portal's\ncontact-relation rules still apply underneath the surface's own.\n\nTokens minted for a surface (currently: contact identification tokens for\n`registration_identifiers` surfaces) carry the surface id. The surface's data\naccess is resolved from the portal config on every request, not baked into the\ntoken, so tightening a surface applies to tokens already in circulation.\n",
|
|
15667
|
+
"required": [
|
|
15668
|
+
"id",
|
|
15669
|
+
"name",
|
|
15670
|
+
"authentication"
|
|
15671
|
+
],
|
|
15672
|
+
"properties": {
|
|
15673
|
+
"id": {
|
|
15674
|
+
"type": "string",
|
|
15675
|
+
"minLength": 1,
|
|
15676
|
+
"maxLength": 64,
|
|
15677
|
+
"pattern": "^[a-z0-9][a-z0-9_-]*$",
|
|
15678
|
+
"description": "Stable identifier, unique within the portal. Referenced by `identifyContact` and carried in issued tokens.",
|
|
15679
|
+
"example": "website-journeys"
|
|
15680
|
+
},
|
|
15681
|
+
"name": {
|
|
15682
|
+
"type": "string",
|
|
15683
|
+
"minLength": 1,
|
|
15684
|
+
"maxLength": 120,
|
|
15685
|
+
"example": "Website journeys"
|
|
15686
|
+
},
|
|
15687
|
+
"description": {
|
|
15688
|
+
"type": "string",
|
|
15689
|
+
"maxLength": 500,
|
|
15690
|
+
"description": "Free text for the configuring user, e.g. where this surface is embedded."
|
|
15691
|
+
},
|
|
15692
|
+
"authentication": {
|
|
15693
|
+
"type": "string",
|
|
15694
|
+
"enum": [
|
|
15695
|
+
"login",
|
|
15696
|
+
"registration_identifiers"
|
|
15697
|
+
],
|
|
15698
|
+
"description": "How a caller on this surface proves who they are.\n\n- `login`: a logged-in portal user token (the portal's own authentication).\n- `registration_identifiers`: the caller supplies the portal's\n `registration_identifiers` to `identifyContact` and receives a short-lived\n contact identification token. Anyone who knows or guesses those values can\n use this surface, so its data access should be as narrow as the use case allows.\n\n`anonymous` (no proof of identity at all) is planned and not accepted yet.\n"
|
|
15699
|
+
},
|
|
15700
|
+
"token_ttl_seconds": {
|
|
15701
|
+
"type": "integer",
|
|
15702
|
+
"format": "int32",
|
|
15703
|
+
"default": 300,
|
|
15704
|
+
"minimum": 60,
|
|
15705
|
+
"maximum": 900,
|
|
15706
|
+
"description": "Lifetime of tokens minted for this surface. Only used with `registration_identifiers`."
|
|
15707
|
+
},
|
|
15708
|
+
"data_access": {
|
|
15709
|
+
"$ref": "#/components/schemas/PortalSurfaceDataAccess"
|
|
15710
|
+
}
|
|
15711
|
+
}
|
|
15712
|
+
},
|
|
15713
|
+
"PortalSurfaceDataAccess": {
|
|
15714
|
+
"type": "object",
|
|
15715
|
+
"description": "What a caller on a surface may reach. Every property is optional; omitting all of them is the portal's default scope.",
|
|
15716
|
+
"properties": {
|
|
15717
|
+
"entities": {
|
|
15718
|
+
"type": "array",
|
|
15719
|
+
"description": "Schemas reachable on this surface, each with the targets that always apply to\nit. Must be a subset of the portal's `allowed_portal_entities`; anything else\nis ignored. Empty or omitted means the surface reaches nothing - data access is\nopted into per schema.\n",
|
|
15720
|
+
"items": {
|
|
15721
|
+
"$ref": "#/components/schemas/PortalSurfaceEntityAccess"
|
|
15722
|
+
}
|
|
15723
|
+
},
|
|
15724
|
+
"role_id": {
|
|
15725
|
+
"type": "string",
|
|
15726
|
+
"description": "360 role whose grants apply on this surface (`<org_id>:<slug>`), giving\nvertical permissions - which attributes and actions are permitted. Omitted\nmeans the portal's default role.\n",
|
|
15727
|
+
"example": "728:public_journeys_readonly"
|
|
15728
|
+
}
|
|
15729
|
+
}
|
|
15730
|
+
},
|
|
15731
|
+
"PortalSurfaceEntityAccess": {
|
|
15732
|
+
"type": "object",
|
|
15733
|
+
"description": "One schema this surface reaches, with the targets that always apply to it.",
|
|
15734
|
+
"properties": {
|
|
15735
|
+
"schema": {
|
|
15736
|
+
"type": "string",
|
|
15737
|
+
"description": "Schema slug, from the portal's `allowed_portal_entities`.",
|
|
15738
|
+
"example": "contract"
|
|
15739
|
+
},
|
|
15740
|
+
"target_ids": {
|
|
15741
|
+
"type": "array",
|
|
15742
|
+
"description": "Targets (see the Targeting API) whose filters always apply to reads of this\nschema on this surface, giving horizontal permissions - which rows are\nreachable. They are applied in addition to any targets the caller passes. A\ntarget matches one schema, which is why they are configured per schema here.\n",
|
|
15743
|
+
"items": {
|
|
15744
|
+
"type": "string"
|
|
15745
|
+
}
|
|
15746
|
+
}
|
|
15747
|
+
},
|
|
15748
|
+
"required": [
|
|
15749
|
+
"schema"
|
|
15750
|
+
]
|
|
15751
|
+
},
|
|
15124
15752
|
"Grant": {
|
|
15125
15753
|
"type": "object",
|
|
15126
15754
|
"properties": {
|
|
@@ -15481,21 +16109,64 @@
|
|
|
15481
16109
|
"type": "boolean",
|
|
15482
16110
|
"description": "True when the linearized path reached the end of the execution graph. False when an\nunevaluated decision task was encountered, meaning additional tasks may appear once\nthe decision is resolved (the returned tasks are still the guaranteed active path).\nAlways true for V1 executions.\n"
|
|
15483
16111
|
},
|
|
15484
|
-
"tasks": {
|
|
15485
|
-
"type": "array",
|
|
15486
|
-
"description": "Portal-visible tasks in linear (timeline) order",
|
|
15487
|
-
"items": {
|
|
15488
|
-
"$ref": "#/components/schemas/PortalWorkflowTask"
|
|
15489
|
-
}
|
|
16112
|
+
"tasks": {
|
|
16113
|
+
"type": "array",
|
|
16114
|
+
"description": "Portal-visible tasks in linear (timeline) order",
|
|
16115
|
+
"items": {
|
|
16116
|
+
"$ref": "#/components/schemas/PortalWorkflowTask"
|
|
16117
|
+
}
|
|
16118
|
+
},
|
|
16119
|
+
"stages": {
|
|
16120
|
+
"type": "array",
|
|
16121
|
+
"description": "Customer-facing stages of the execution in strict order, each with a\nprogress status derived by the Workflows API. Present only for V2 flow\nexecutions whose template defines stages and whose boundaries resolve\ncleanly; omitted otherwise, so consumers must fall back to the flat\ntask timeline.\n",
|
|
16122
|
+
"items": {
|
|
16123
|
+
"$ref": "#/components/schemas/PortalWorkflowStage"
|
|
16124
|
+
}
|
|
16125
|
+
}
|
|
16126
|
+
},
|
|
16127
|
+
"required": [
|
|
16128
|
+
"id",
|
|
16129
|
+
"name",
|
|
16130
|
+
"status",
|
|
16131
|
+
"version",
|
|
16132
|
+
"is_path_complete",
|
|
16133
|
+
"tasks"
|
|
16134
|
+
]
|
|
16135
|
+
},
|
|
16136
|
+
"PortalWorkflowStage": {
|
|
16137
|
+
"type": "object",
|
|
16138
|
+
"description": "A customer-facing stage of a flow execution, with a progress status derived\nfrom the execution's tasks by the Workflows API. Stages form a strict total\norder; the array order is the stage order.\n",
|
|
16139
|
+
"properties": {
|
|
16140
|
+
"id": {
|
|
16141
|
+
"type": "string",
|
|
16142
|
+
"description": "Stable unique identifier for the stage"
|
|
16143
|
+
},
|
|
16144
|
+
"name": {
|
|
16145
|
+
"type": "string",
|
|
16146
|
+
"description": "User-facing stage title"
|
|
16147
|
+
},
|
|
16148
|
+
"description": {
|
|
16149
|
+
"type": "string",
|
|
16150
|
+
"description": "Customer-facing description of what happens in this stage"
|
|
16151
|
+
},
|
|
16152
|
+
"status": {
|
|
16153
|
+
"type": "string",
|
|
16154
|
+
"enum": [
|
|
16155
|
+
"COMPLETED",
|
|
16156
|
+
"IN_PROGRESS",
|
|
16157
|
+
"UPCOMING"
|
|
16158
|
+
],
|
|
16159
|
+
"description": "Derived progress status:\n- COMPLETED: every task of the stage is done, and the flow has moved past it\n- IN_PROGRESS: the flow's current work is inside this stage\n- UPCOMING: the flow has not reached this stage yet\n"
|
|
16160
|
+
},
|
|
16161
|
+
"completed_at": {
|
|
16162
|
+
"type": "string",
|
|
16163
|
+
"description": "Latest completion timestamp among the stage's tasks; set only when the\nstage is COMPLETED and at least one of its tasks recorded one\n"
|
|
15490
16164
|
}
|
|
15491
16165
|
},
|
|
15492
16166
|
"required": [
|
|
15493
16167
|
"id",
|
|
15494
16168
|
"name",
|
|
15495
|
-
"status"
|
|
15496
|
-
"version",
|
|
15497
|
-
"is_path_complete",
|
|
15498
|
-
"tasks"
|
|
16169
|
+
"status"
|
|
15499
16170
|
]
|
|
15500
16171
|
},
|
|
15501
16172
|
"PortalWorkflowTask": {
|
|
@@ -15567,6 +16238,10 @@
|
|
|
15567
16238
|
"type": "string",
|
|
15568
16239
|
"description": "Name of the phase the underlying task belongs to, if any (V2 only)"
|
|
15569
16240
|
},
|
|
16241
|
+
"stage_id": {
|
|
16242
|
+
"type": "string",
|
|
16243
|
+
"description": "Id of the stage the underlying task belongs to, when the execution\ncarries stages (derived by the Workflows API at read time)\n"
|
|
16244
|
+
},
|
|
15570
16245
|
"completed_at": {
|
|
15571
16246
|
"type": "string",
|
|
15572
16247
|
"description": "Timestamp when the task was completed or skipped"
|
|
@@ -18500,6 +19175,41 @@
|
|
|
18500
19175
|
"display_name"
|
|
18501
19176
|
]
|
|
18502
19177
|
},
|
|
19178
|
+
"ProviderPublicConfigV3": {
|
|
19179
|
+
"type": "object",
|
|
19180
|
+
"description": "Web-only public provider configuration served by `getPublicSSOProviderV3`.\nUnlike `ProviderPublicConfig` there is no `mobile_oidc_config`, and\n`oidc_config` never carries `client_secret` or the `metadata.test_auth_*`\ncredentials — `oidc_config.has_client_secret` signals their presence so\nclients route the token exchange through the backend SSO callback.\n",
|
|
19181
|
+
"properties": {
|
|
19182
|
+
"slug": {
|
|
19183
|
+
"$ref": "#/components/schemas/ProviderSlug"
|
|
19184
|
+
},
|
|
19185
|
+
"display_name": {
|
|
19186
|
+
"$ref": "#/components/schemas/ProviderDisplayName"
|
|
19187
|
+
},
|
|
19188
|
+
"oidc_config": {
|
|
19189
|
+
"$ref": "#/components/schemas/OIDCProviderConfig"
|
|
19190
|
+
}
|
|
19191
|
+
},
|
|
19192
|
+
"required": [
|
|
19193
|
+
"slug",
|
|
19194
|
+
"display_name"
|
|
19195
|
+
]
|
|
19196
|
+
},
|
|
19197
|
+
"PublicIdentityProvider": {
|
|
19198
|
+
"type": "object",
|
|
19199
|
+
"description": "Minimal public identity provider info — enough to render a provider login button.",
|
|
19200
|
+
"properties": {
|
|
19201
|
+
"slug": {
|
|
19202
|
+
"$ref": "#/components/schemas/ProviderSlug"
|
|
19203
|
+
},
|
|
19204
|
+
"display_name": {
|
|
19205
|
+
"$ref": "#/components/schemas/ProviderDisplayName"
|
|
19206
|
+
}
|
|
19207
|
+
},
|
|
19208
|
+
"required": [
|
|
19209
|
+
"slug",
|
|
19210
|
+
"display_name"
|
|
19211
|
+
]
|
|
19212
|
+
},
|
|
18503
19213
|
"AttributeMappingConfig": {
|
|
18504
19214
|
"type": "object",
|
|
18505
19215
|
"description": "Dictionary of epilot user attributes to claims",
|
|
@@ -19599,6 +20309,13 @@
|
|
|
19599
20309
|
}
|
|
19600
20310
|
]
|
|
19601
20311
|
},
|
|
20312
|
+
"surfaces": {
|
|
20313
|
+
"type": "array",
|
|
20314
|
+
"description": "Surfaces this portal's data is reached from besides the portal UI itself (public journeys on the website, chat). Configured under Security > Surfaces. Each surface defines how a caller authenticates and what data access applies on it; the portal UI is the implicit default surface (login, default scope) and is not listed here. A surface with `authentication: registration_identifiers` is what makes `identifyContact` issue tokens for this portal: without one, `identifyContact` returns 403.\n",
|
|
20315
|
+
"items": {
|
|
20316
|
+
"$ref": "#/components/schemas/PortalSurface"
|
|
20317
|
+
}
|
|
20318
|
+
},
|
|
19602
20319
|
"contact_identifiers_for_account": {
|
|
19603
20320
|
"type": "array",
|
|
19604
20321
|
"description": "Account-mode only. Identifiers on the contact entity of the primarily\nidentified account. Used to pick an existing related contact within the\nresolved account; if none matches, the values are written onto the new\ncontact that is created and linked to the account.\n",
|
|
@@ -19748,6 +20465,12 @@
|
|
|
19748
20465
|
"type": "boolean",
|
|
19749
20466
|
"description": "Whether this is a v3 portal configuration"
|
|
19750
20467
|
},
|
|
20468
|
+
"published_revision_id": {
|
|
20469
|
+
"type": "string",
|
|
20470
|
+
"readOnly": true,
|
|
20471
|
+
"description": "The revision currently live on this portal. Absent until the first publish.",
|
|
20472
|
+
"example": "2026-08-25T14:03:11.482Z-a7f3c1d9"
|
|
20473
|
+
},
|
|
19751
20474
|
"portal_id": {
|
|
19752
20475
|
"$ref": "#/components/schemas/PortalId"
|
|
19753
20476
|
},
|
|
@@ -19973,6 +20696,279 @@
|
|
|
19973
20696
|
}
|
|
19974
20697
|
}
|
|
19975
20698
|
},
|
|
20699
|
+
"RevisionPageRequest": {
|
|
20700
|
+
"type": "object",
|
|
20701
|
+
"additionalProperties": true,
|
|
20702
|
+
"description": "A page inside a revision snapshot. `additionalProperties` is true on purpose — a page carries fields this schema does not name and they must survive into the snapshot and back onto the live page when it is published.\n",
|
|
20703
|
+
"required": [
|
|
20704
|
+
"id",
|
|
20705
|
+
"slug",
|
|
20706
|
+
"order",
|
|
20707
|
+
"blocks"
|
|
20708
|
+
],
|
|
20709
|
+
"properties": {
|
|
20710
|
+
"id": {
|
|
20711
|
+
"type": "string",
|
|
20712
|
+
"format": "uuid",
|
|
20713
|
+
"description": "Stable page identity. Required because a revision diff correlates pages by id, not by slug.\n`format: uuid` is enforced, so a non-UUID id is rejected as a `400`.\n",
|
|
20714
|
+
"example": "c495fef9-eeca-4019-a989-8390dcd9825b"
|
|
20715
|
+
},
|
|
20716
|
+
"slug": {
|
|
20717
|
+
"type": "string",
|
|
20718
|
+
"example": "dashboard"
|
|
20719
|
+
},
|
|
20720
|
+
"order": {
|
|
20721
|
+
"type": "number",
|
|
20722
|
+
"example": 0
|
|
20723
|
+
},
|
|
20724
|
+
"blocks": {
|
|
20725
|
+
"type": "object",
|
|
20726
|
+
"additionalProperties": true,
|
|
20727
|
+
"description": "Required on purpose. A live save preserves an omitted `blocks` from the stored page; publish replaces each live page wholesale with no such guard, so a revision snapshotted without blocks would wipe them on publish. `{}` is a valid value — the field simply has to be present.\n"
|
|
20728
|
+
}
|
|
20729
|
+
}
|
|
20730
|
+
},
|
|
20731
|
+
"PortalRevisionRequest": {
|
|
20732
|
+
"allOf": [
|
|
20733
|
+
{
|
|
20734
|
+
"$ref": "#/components/schemas/UpdateOnlyPortalConfigAttributes"
|
|
20735
|
+
},
|
|
20736
|
+
{
|
|
20737
|
+
"$ref": "#/components/schemas/CommonConfigAttributesV3"
|
|
20738
|
+
},
|
|
20739
|
+
{
|
|
20740
|
+
"type": "object",
|
|
20741
|
+
"additionalProperties": true,
|
|
20742
|
+
"required": [
|
|
20743
|
+
"pages"
|
|
20744
|
+
],
|
|
20745
|
+
"properties": {
|
|
20746
|
+
"email_templates": {
|
|
20747
|
+
"$ref": "#/components/schemas/EmailTemplates"
|
|
20748
|
+
},
|
|
20749
|
+
"identity_providers": {
|
|
20750
|
+
"type": "array",
|
|
20751
|
+
"items": {
|
|
20752
|
+
"$ref": "#/components/schemas/ProviderConfig"
|
|
20753
|
+
}
|
|
20754
|
+
},
|
|
20755
|
+
"based_on_revision_id": {
|
|
20756
|
+
"type": "string"
|
|
20757
|
+
},
|
|
20758
|
+
"pages": {
|
|
20759
|
+
"type": "array",
|
|
20760
|
+
"items": {
|
|
20761
|
+
"$ref": "#/components/schemas/RevisionPageRequest"
|
|
20762
|
+
}
|
|
20763
|
+
}
|
|
20764
|
+
}
|
|
20765
|
+
}
|
|
20766
|
+
]
|
|
20767
|
+
},
|
|
20768
|
+
"PublishRevisionRequest": {
|
|
20769
|
+
"type": "object",
|
|
20770
|
+
"required": [
|
|
20771
|
+
"revision_id"
|
|
20772
|
+
],
|
|
20773
|
+
"properties": {
|
|
20774
|
+
"revision_id": {
|
|
20775
|
+
"type": "string",
|
|
20776
|
+
"example": "2026-08-25T14:03:11.482Z-a7f3c1d9"
|
|
20777
|
+
},
|
|
20778
|
+
"name": {
|
|
20779
|
+
"type": "string",
|
|
20780
|
+
"maxLength": 255,
|
|
20781
|
+
"description": "Label stamped onto the revision being published, permanently and atomically with the publish itself. Optional: when omitted the revision keeps whatever name it already has, possibly none; the server never generates one. An explicit name replaces any previous one. Clients derive a display label for unnamed revisions from `published_at` / `created_at`.\n",
|
|
20782
|
+
"example": "FAQ page launch"
|
|
20783
|
+
},
|
|
20784
|
+
"description": {
|
|
20785
|
+
"type": "string",
|
|
20786
|
+
"maxLength": 8000,
|
|
20787
|
+
"description": "Optional description stamped onto the revision at publish time"
|
|
20788
|
+
}
|
|
20789
|
+
}
|
|
20790
|
+
},
|
|
20791
|
+
"PortalRevisionSummary": {
|
|
20792
|
+
"type": "object",
|
|
20793
|
+
"required": [
|
|
20794
|
+
"revision_id",
|
|
20795
|
+
"created_at",
|
|
20796
|
+
"page_count",
|
|
20797
|
+
"is_published"
|
|
20798
|
+
],
|
|
20799
|
+
"properties": {
|
|
20800
|
+
"revision_id": {
|
|
20801
|
+
"type": "string",
|
|
20802
|
+
"example": "2026-08-25T14:03:11.482Z-a7f3c1d9"
|
|
20803
|
+
},
|
|
20804
|
+
"created_at": {
|
|
20805
|
+
"type": "string",
|
|
20806
|
+
"format": "date-time"
|
|
20807
|
+
},
|
|
20808
|
+
"created_by": {
|
|
20809
|
+
"type": "string",
|
|
20810
|
+
"description": "May be absent — an internal-auth caller carries no user id."
|
|
20811
|
+
},
|
|
20812
|
+
"name": {
|
|
20813
|
+
"type": "string",
|
|
20814
|
+
"description": "Set at publish time only, and only when the publish request carried one. A revision can have been published and still have no name; `published_at` is the signal that a revision was live at some point, not this field.\n",
|
|
20815
|
+
"example": "FAQ page launch"
|
|
20816
|
+
},
|
|
20817
|
+
"description": {
|
|
20818
|
+
"type": "string"
|
|
20819
|
+
},
|
|
20820
|
+
"page_count": {
|
|
20821
|
+
"type": "number"
|
|
20822
|
+
},
|
|
20823
|
+
"published_at": {
|
|
20824
|
+
"type": "string",
|
|
20825
|
+
"format": "date-time",
|
|
20826
|
+
"description": "The last time this revision was published. NOT the same question as `is_published`: a revision that was live yesterday still carries a `published_at`.\n"
|
|
20827
|
+
},
|
|
20828
|
+
"published_by": {
|
|
20829
|
+
"type": "string",
|
|
20830
|
+
"description": "Who performed the last publish of this revision."
|
|
20831
|
+
},
|
|
20832
|
+
"is_published": {
|
|
20833
|
+
"type": "boolean",
|
|
20834
|
+
"description": "Whether this revision is the one currently live."
|
|
20835
|
+
}
|
|
20836
|
+
}
|
|
20837
|
+
},
|
|
20838
|
+
"PortalRevisionList": {
|
|
20839
|
+
"type": "object",
|
|
20840
|
+
"required": [
|
|
20841
|
+
"results"
|
|
20842
|
+
],
|
|
20843
|
+
"properties": {
|
|
20844
|
+
"results": {
|
|
20845
|
+
"type": "array",
|
|
20846
|
+
"items": {
|
|
20847
|
+
"$ref": "#/components/schemas/PortalRevisionSummary"
|
|
20848
|
+
}
|
|
20849
|
+
},
|
|
20850
|
+
"next_cursor": {
|
|
20851
|
+
"type": "string",
|
|
20852
|
+
"description": "Opaque cursor to pass back as `cursor` to fetch the next page. When `next_cursor` is absent, the client has reached the end of the dataset.\n"
|
|
20853
|
+
}
|
|
20854
|
+
}
|
|
20855
|
+
},
|
|
20856
|
+
"PortalRevisionCreated": {
|
|
20857
|
+
"allOf": [
|
|
20858
|
+
{
|
|
20859
|
+
"$ref": "#/components/schemas/PortalRevisionSummary"
|
|
20860
|
+
},
|
|
20861
|
+
{
|
|
20862
|
+
"type": "object",
|
|
20863
|
+
"properties": {
|
|
20864
|
+
"identity_providers": {
|
|
20865
|
+
"type": "array",
|
|
20866
|
+
"description": "The SSO identity providers just captured into this revision, with `client_secret` REDACTED exactly as the admin portal-config GET redacts it. Returned only here — not on list items or elsewhere — because the caller who just submitted providers is the one reader who needs to see what got captured, e.g. to warn that the revision was saved without a client secret.\n",
|
|
20867
|
+
"items": {
|
|
20868
|
+
"$ref": "#/components/schemas/ProviderConfig"
|
|
20869
|
+
}
|
|
20870
|
+
}
|
|
20871
|
+
}
|
|
20872
|
+
}
|
|
20873
|
+
]
|
|
20874
|
+
},
|
|
20875
|
+
"RevisionPage": {
|
|
20876
|
+
"description": "A page from a revision snapshot. Same shape as a live `Page`, without the server-managed fields (`past_routes`, `_created_at`, `_updated_at`, `is_deleted`), which publish re-derives from the live portal.\n",
|
|
20877
|
+
"allOf": [
|
|
20878
|
+
{
|
|
20879
|
+
"$ref": "#/components/schemas/Page"
|
|
20880
|
+
},
|
|
20881
|
+
{
|
|
20882
|
+
"type": "object",
|
|
20883
|
+
"required": [
|
|
20884
|
+
"id",
|
|
20885
|
+
"blocks"
|
|
20886
|
+
],
|
|
20887
|
+
"properties": {
|
|
20888
|
+
"org_id": {
|
|
20889
|
+
"type": "string"
|
|
20890
|
+
}
|
|
20891
|
+
}
|
|
20892
|
+
}
|
|
20893
|
+
]
|
|
20894
|
+
},
|
|
20895
|
+
"PortalRevision": {
|
|
20896
|
+
"allOf": [
|
|
20897
|
+
{
|
|
20898
|
+
"$ref": "#/components/schemas/PortalRevisionSummary"
|
|
20899
|
+
},
|
|
20900
|
+
{
|
|
20901
|
+
"type": "object",
|
|
20902
|
+
"required": [
|
|
20903
|
+
"config",
|
|
20904
|
+
"pages"
|
|
20905
|
+
],
|
|
20906
|
+
"properties": {
|
|
20907
|
+
"config": {
|
|
20908
|
+
"type": "object",
|
|
20909
|
+
"additionalProperties": true,
|
|
20910
|
+
"description": "The snapshotted portal configuration. Secret-typed extension option values are removed entirely — not masked — before the response is assembled.\n"
|
|
20911
|
+
},
|
|
20912
|
+
"pages": {
|
|
20913
|
+
"type": "array",
|
|
20914
|
+
"items": {
|
|
20915
|
+
"$ref": "#/components/schemas/RevisionPage"
|
|
20916
|
+
}
|
|
20917
|
+
},
|
|
20918
|
+
"identity_providers": {
|
|
20919
|
+
"type": "array",
|
|
20920
|
+
"description": "The SSO identity providers captured in this revision, with `client_secret` REDACTED exactly as the admin portal-config GET redacts it (no read path returns a stored secret).\nPublishing this revision replaces every live provider on this portal's origin, not just this portal's own, with this set. Identity providers key on `IDP#{origin}#{slug}`, a partition shared by every portal on that origin, so any other portal sharing it (an ADDITIONAL_PORTAL clone, most commonly) is affected too. A client can diff this against the live providers (by `slug`, and on `oidc_config.oidc_issuer` / `oidc_config.client_id` for a changed provider) to warn that a publish would also change SSO configuration, for this portal and any others sharing its origin.\nABSENT and `[]` are different answers. `[]` means the revision has no providers and publishing it removes the live ones; absent means the revision was saved before provider versioning, carries no SSO opinion, and publishing it changes no provider.\n",
|
|
20921
|
+
"items": {
|
|
20922
|
+
"$ref": "#/components/schemas/ProviderConfig"
|
|
20923
|
+
}
|
|
20924
|
+
},
|
|
20925
|
+
"email_templates": {
|
|
20926
|
+
"$ref": "#/components/schemas/EmailTemplates"
|
|
20927
|
+
},
|
|
20928
|
+
"email_template_settings": {
|
|
20929
|
+
"type": "object",
|
|
20930
|
+
"additionalProperties": true,
|
|
20931
|
+
"description": "The portal's email template settings as they were live when the revision was saved. Read-only: this field cannot be set through the revision request and is always captured from live.\n"
|
|
20932
|
+
}
|
|
20933
|
+
}
|
|
20934
|
+
}
|
|
20935
|
+
]
|
|
20936
|
+
},
|
|
20937
|
+
"PublishResult": {
|
|
20938
|
+
"type": "object",
|
|
20939
|
+
"required": [
|
|
20940
|
+
"revision_id",
|
|
20941
|
+
"published_at",
|
|
20942
|
+
"post_publish_warnings"
|
|
20943
|
+
],
|
|
20944
|
+
"properties": {
|
|
20945
|
+
"revision_id": {
|
|
20946
|
+
"type": "string"
|
|
20947
|
+
},
|
|
20948
|
+
"published_at": {
|
|
20949
|
+
"type": "string",
|
|
20950
|
+
"format": "date-time"
|
|
20951
|
+
},
|
|
20952
|
+
"published_by": {
|
|
20953
|
+
"type": "string"
|
|
20954
|
+
},
|
|
20955
|
+
"name": {
|
|
20956
|
+
"type": "string",
|
|
20957
|
+
"description": "The name now on the revision: the one sent in the request, or the one a previous publish set when this request omitted `name`. Absent when neither exists.\n",
|
|
20958
|
+
"example": "FAQ page launch"
|
|
20959
|
+
},
|
|
20960
|
+
"post_publish_warnings": {
|
|
20961
|
+
"type": "array",
|
|
20962
|
+
"description": "Stable, machine-readable codes for best-effort post-publish side effects that exhausted their retries. Publishing itself succeeded; these are not error messages and not meant for display as-is. Clients map each code to their own localized text.\nKnown codes: `allowed_entities_change` (portal entity grants and detail pages did not sync), `dns_records_clear` (the previous domain's DNS records were not cleared). New codes may be added without a client update, so treat an unrecognized code as a generic \"a follow-up step did not finish\" case rather than an error.\n",
|
|
20963
|
+
"items": {
|
|
20964
|
+
"type": "string"
|
|
20965
|
+
},
|
|
20966
|
+
"example": [
|
|
20967
|
+
"allowed_entities_change"
|
|
20968
|
+
]
|
|
20969
|
+
}
|
|
20970
|
+
}
|
|
20971
|
+
},
|
|
19976
20972
|
"SwappableConfig": {
|
|
19977
20973
|
"type": "string",
|
|
19978
20974
|
"description": "Optional configuration item that a portal swap can additionally include. The swap always transfers the pages and the functional experience config that keep the portal working. These items are opt-in on top of that and are OFF by default. Domain and access/security settings (domain, cognito_details, auth_settings) can never be swapped and are therefore not part of this enum.",
|