@helpai/elements 0.13.1 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/schema.json CHANGED
@@ -165,9 +165,9 @@
165
165
  "minLength": 1,
166
166
  "maxLength": 2048
167
167
  },
168
- "intake": {
169
- "default": "/ai/agent/intake",
170
- "description": "Pre-chat intake submission endpoint. The widget POSTs the captured lead values here on completion (fire-and-forget), keyed by the same visitorId + sessionId as the conversation. Optional — a 404 is ignored.",
168
+ "submitForm": {
169
+ "default": "/ai/agent/submit-form",
170
+ "description": "Form submission endpoint. The widget POSTs each completed form's values here (fire-and-forget), keyed by the same visitorId + sessionId as the conversation; the payload carries `formId` + `trigger` so the backend can route. Optional — a 404 is ignored.",
171
171
  "type": "string",
172
172
  "minLength": 1,
173
173
  "maxLength": 2048
@@ -176,7 +176,7 @@
176
176
  "required": [
177
177
  "upload",
178
178
  "transcribe",
179
- "intake"
179
+ "submitForm"
180
180
  ],
181
181
  "additionalProperties": {},
182
182
  "description": "HTTP endpoint overrides. Paths are appended to baseUrl; absolute URLs are accepted too.",
@@ -189,7 +189,7 @@
189
189
  "upload": "https://help.ai/uploads/presign"
190
190
  },
191
191
  {
192
- "intake": "https://crm.acme.com/leads/intake"
192
+ "submitForm": "https://crm.acme.com/leads"
193
193
  }
194
194
  ]
195
195
  },
@@ -1354,229 +1354,362 @@
1354
1354
  }
1355
1355
  ]
1356
1356
  },
1357
- "intake": {
1358
- "type": "object",
1359
- "properties": {
1360
- "enabled": {
1361
- "default": false,
1362
- "description": "Turn the pre-chat intake gate on. Off by default.",
1363
- "type": "boolean"
1364
- },
1365
- "title": {
1366
- "description": "Form heading (e.g. 'Before we start').",
1367
- "type": "string",
1368
- "maxLength": 120
1369
- },
1370
- "description": {
1371
- "description": "Sub-heading under the title.",
1372
- "type": "string",
1373
- "maxLength": 500
1374
- },
1375
- "fields": {
1376
- "default": [],
1377
- "description": "The questions to ask, in order. The gate is only active when there is ≥1 field.",
1378
- "maxItems": 20,
1379
- "type": "array",
1380
- "items": {
1381
- "type": "object",
1382
- "properties": {
1383
- "name": {
1384
- "type": "string",
1385
- "minLength": 1,
1386
- "maxLength": 80,
1387
- "description": "Stable key the answer is recorded under (e.g. `email`). Sent to the agent as captured context."
1388
- },
1389
- "label": {
1390
- "type": "string",
1391
- "minLength": 1,
1392
- "maxLength": 200,
1393
- "description": "Visible field label."
1394
- },
1395
- "type": {
1357
+ "forms": {
1358
+ "maxItems": 20,
1359
+ "type": "array",
1360
+ "items": {
1361
+ "type": "object",
1362
+ "properties": {
1363
+ "id": {
1364
+ "type": "string",
1365
+ "minLength": 1,
1366
+ "maxLength": 80,
1367
+ "description": "Stable id — persistence dedupe + `manual` openForm(id)."
1368
+ },
1369
+ "on": {
1370
+ "anyOf": [
1371
+ {
1396
1372
  "type": "string",
1397
- "enum": [
1398
- "text",
1399
- "email",
1400
- "tel",
1401
- "url",
1402
- "number",
1403
- "textarea",
1404
- "select",
1405
- "radio",
1406
- "checkbox",
1407
- "multiselect"
1408
- ],
1409
- "description": "Field renderer + built-in validation (`email`/`tel`/`url`/`number` are checked)."
1373
+ "pattern": "^(pre-chat|conversation-closed|panel-close|manual|after-messages:\\d+|idle:\\d+|page-area:.+)$",
1374
+ "description": "When the form fires: `pre-chat`, `after-messages:N`, `conversation-closed`, `panel-close`, `idle:Nseconds`, `page-area:<area>`, or `manual`."
1410
1375
  },
1411
- "placeholder": {
1412
- "type": "string",
1413
- "maxLength": 200
1376
+ {
1377
+ "minItems": 1,
1378
+ "maxItems": 8,
1379
+ "type": "array",
1380
+ "items": {
1381
+ "type": "string",
1382
+ "pattern": "^(pre-chat|conversation-closed|panel-close|manual|after-messages:\\d+|idle:\\d+|page-area:.+)$",
1383
+ "description": "When the form fires: `pre-chat`, `after-messages:N`, `conversation-closed`, `panel-close`, `idle:Nseconds`, `page-area:<area>`, or `manual`."
1384
+ }
1385
+ }
1386
+ ],
1387
+ "description": "Trigger(s) that surface this form."
1388
+ },
1389
+ "when": {
1390
+ "description": "Extra eligibility predicate evaluated when a trigger fires.",
1391
+ "type": "object",
1392
+ "properties": {
1393
+ "missingContext": {
1394
+ "description": "Only show if `userContext` lacks ALL of these keys (progressive profiling).",
1395
+ "maxItems": 20,
1396
+ "type": "array",
1397
+ "items": {
1398
+ "type": "string",
1399
+ "maxLength": 80
1400
+ }
1414
1401
  },
1415
- "required": {
1416
- "default": true,
1417
- "description": "Must be answered to submit. Set `false` to allow skipping.",
1418
- "type": "boolean"
1402
+ "pageArea": {
1403
+ "description": "Only on these `pageContext.area` values.",
1404
+ "anyOf": [
1405
+ {
1406
+ "type": "string",
1407
+ "maxLength": 64
1408
+ },
1409
+ {
1410
+ "maxItems": 20,
1411
+ "type": "array",
1412
+ "items": {
1413
+ "type": "string",
1414
+ "maxLength": 64
1415
+ }
1416
+ }
1417
+ ]
1419
1418
  },
1420
- "defaultValue": {
1421
- "description": "Pre-filled value (comma-join for `multiselect`).",
1422
- "type": "string",
1423
- "maxLength": 2000
1419
+ "minMessages": {
1420
+ "type": "integer",
1421
+ "minimum": 0,
1422
+ "maximum": 1000
1424
1423
  },
1425
- "options": {
1426
- "description": "Choices for `select` / `radio` / `multiselect`.",
1427
- "maxItems": 50,
1428
- "type": "array",
1429
- "items": {
1424
+ "maxMessages": {
1425
+ "type": "integer",
1426
+ "minimum": 0,
1427
+ "maximum": 1000
1428
+ }
1429
+ },
1430
+ "additionalProperties": {}
1431
+ },
1432
+ "fields": {
1433
+ "default": [],
1434
+ "description": "The questions, in order. ≥1 required to show.",
1435
+ "maxItems": 20,
1436
+ "type": "array",
1437
+ "items": {
1438
+ "type": "object",
1439
+ "properties": {
1440
+ "name": {
1441
+ "type": "string",
1442
+ "minLength": 1,
1443
+ "maxLength": 80,
1444
+ "description": "Stable key the answer is recorded under (e.g. `email`)."
1445
+ },
1446
+ "label": {
1447
+ "type": "string",
1448
+ "minLength": 1,
1449
+ "maxLength": 200,
1450
+ "description": "Visible field label."
1451
+ },
1452
+ "type": {
1453
+ "type": "string",
1454
+ "enum": [
1455
+ "text",
1456
+ "email",
1457
+ "tel",
1458
+ "url",
1459
+ "number",
1460
+ "date",
1461
+ "time",
1462
+ "textarea",
1463
+ "select",
1464
+ "radio",
1465
+ "checkbox",
1466
+ "multiselect"
1467
+ ],
1468
+ "description": "Field renderer + built-in validation (email/tel/url/number/date/time checked)."
1469
+ },
1470
+ "placeholder": {
1471
+ "type": "string",
1472
+ "maxLength": 200
1473
+ },
1474
+ "required": {
1475
+ "default": true,
1476
+ "description": "Must be answered to submit. `false` allows skipping.",
1477
+ "type": "boolean"
1478
+ },
1479
+ "defaultValue": {
1480
+ "description": "Pre-filled value (comma-join for `multiselect`).",
1481
+ "type": "string",
1482
+ "maxLength": 2000
1483
+ },
1484
+ "options": {
1485
+ "description": "Choices for `select` / `radio` / `multiselect`.",
1486
+ "maxItems": 50,
1487
+ "type": "array",
1488
+ "items": {
1489
+ "type": "object",
1490
+ "properties": {
1491
+ "value": {
1492
+ "type": "string",
1493
+ "minLength": 1,
1494
+ "maxLength": 200,
1495
+ "description": "The value recorded when this choice is picked."
1496
+ },
1497
+ "label": {
1498
+ "description": "Display text; falls back to `value`.",
1499
+ "type": "string",
1500
+ "minLength": 1,
1501
+ "maxLength": 200
1502
+ },
1503
+ "description": {
1504
+ "description": "Optional sub-label under the choice.",
1505
+ "type": "string",
1506
+ "maxLength": 280
1507
+ }
1508
+ },
1509
+ "required": [
1510
+ "value"
1511
+ ],
1512
+ "additionalProperties": {}
1513
+ }
1514
+ },
1515
+ "validation": {
1430
1516
  "type": "object",
1431
1517
  "properties": {
1432
- "value": {
1518
+ "pattern": {
1519
+ "description": "Regex source string — text-like fields (e.g. `^\\d{5}$`).",
1433
1520
  "type": "string",
1434
- "minLength": 1,
1435
- "maxLength": 200,
1436
- "description": "The value recorded when this choice is picked."
1521
+ "maxLength": 500
1437
1522
  },
1438
- "label": {
1439
- "description": "Display text; falls back to `value`.",
1440
- "type": "string",
1441
- "minLength": 1,
1442
- "maxLength": 200
1523
+ "minLength": {
1524
+ "type": "integer",
1525
+ "minimum": 0,
1526
+ "maximum": 10000
1443
1527
  },
1444
- "description": {
1445
- "description": "Optional sub-label under the choice.",
1446
- "type": "string",
1447
- "maxLength": 280
1528
+ "maxLength": {
1529
+ "type": "integer",
1530
+ "minimum": 1,
1531
+ "maximum": 10000
1532
+ },
1533
+ "min": {
1534
+ "description": "Minimum — `number` fields.",
1535
+ "type": "number"
1536
+ },
1537
+ "max": {
1538
+ "description": "Maximum — `number` fields.",
1539
+ "type": "number"
1540
+ },
1541
+ "minSelections": {
1542
+ "description": "Minimum picks — `multiselect`.",
1543
+ "type": "integer",
1544
+ "minimum": 0,
1545
+ "maximum": 50
1546
+ },
1547
+ "maxSelections": {
1548
+ "description": "Maximum picks — `multiselect`.",
1549
+ "type": "integer",
1550
+ "minimum": 1,
1551
+ "maximum": 50
1448
1552
  }
1449
1553
  },
1450
- "required": [
1451
- "value"
1452
- ],
1453
1554
  "additionalProperties": {}
1555
+ },
1556
+ "allowOther": {
1557
+ "description": "`select`/`radio`/`multiselect` — allow a free-text 'Other' answer.",
1558
+ "type": "boolean"
1559
+ },
1560
+ "validationHint": {
1561
+ "description": "Small helper line under the field.",
1562
+ "type": "string",
1563
+ "maxLength": 280
1454
1564
  }
1455
1565
  },
1456
- "validation": {
1457
- "type": "object",
1458
- "properties": {
1459
- "pattern": {
1460
- "description": "Regex source string — text-like fields (e.g. `^\\d{5}$`).",
1461
- "type": "string",
1462
- "maxLength": 500
1463
- },
1464
- "minLength": {
1465
- "type": "integer",
1466
- "minimum": 0,
1467
- "maximum": 10000
1468
- },
1469
- "maxLength": {
1470
- "type": "integer",
1471
- "minimum": 1,
1472
- "maximum": 10000
1473
- },
1474
- "min": {
1475
- "description": "Minimum — `number` fields.",
1476
- "type": "number"
1477
- },
1478
- "max": {
1479
- "description": "Maximum `number` fields.",
1480
- "type": "number"
1566
+ "required": [
1567
+ "name",
1568
+ "label",
1569
+ "type",
1570
+ "required"
1571
+ ],
1572
+ "additionalProperties": {}
1573
+ }
1574
+ },
1575
+ "title": {
1576
+ "type": "string",
1577
+ "maxLength": 120
1578
+ },
1579
+ "description": {
1580
+ "type": "string",
1581
+ "maxLength": 500
1582
+ },
1583
+ "submitLabel": {
1584
+ "type": "string",
1585
+ "maxLength": 60
1586
+ },
1587
+ "skippable": {
1588
+ "default": false,
1589
+ "description": "Show a Skip control.",
1590
+ "type": "boolean"
1591
+ },
1592
+ "blocking": {
1593
+ "default": false,
1594
+ "description": "Gate the composer (honored only for `pre-chat` / `after-messages`); otherwise renders inline.",
1595
+ "type": "boolean"
1596
+ },
1597
+ "frequency": {
1598
+ "default": "once",
1599
+ "description": "`once` (persisted per-device), `session`, or `always`.",
1600
+ "type": "string",
1601
+ "enum": [
1602
+ "once",
1603
+ "session",
1604
+ "always"
1605
+ ]
1606
+ },
1607
+ "mirrorToContext": {
1608
+ "default": true,
1609
+ "description": "Mirror the answers into `userContext` so they ride every request. Set `false` for PII (DOB, policy #, symptoms) — those then go only to the submit endpoint.",
1610
+ "type": "boolean"
1611
+ }
1612
+ },
1613
+ "required": [
1614
+ "id",
1615
+ "on",
1616
+ "fields",
1617
+ "skippable",
1618
+ "blocking",
1619
+ "frequency",
1620
+ "mirrorToContext"
1621
+ ],
1622
+ "additionalProperties": {}
1623
+ },
1624
+ "description": "Event-driven forms — an ordered list (order = priority). Each binds to trigger event(s), shows blocking or inline, dedupes by frequency, and records via `endpoints.forms`. Generic across verticals.",
1625
+ "examples": [
1626
+ [
1627
+ {
1628
+ "id": "intake",
1629
+ "on": "pre-chat",
1630
+ "blocking": true,
1631
+ "fields": [
1632
+ {
1633
+ "name": "email",
1634
+ "label": "Email",
1635
+ "type": "email"
1636
+ }
1637
+ ]
1638
+ }
1639
+ ],
1640
+ [
1641
+ {
1642
+ "id": "lead",
1643
+ "on": "pre-chat",
1644
+ "blocking": true,
1645
+ "title": "Before we start",
1646
+ "fields": [
1647
+ {
1648
+ "name": "name",
1649
+ "label": "Your name",
1650
+ "type": "text"
1651
+ },
1652
+ {
1653
+ "name": "email",
1654
+ "label": "Work email",
1655
+ "type": "email"
1656
+ }
1657
+ ]
1658
+ },
1659
+ {
1660
+ "id": "csat",
1661
+ "on": "conversation-closed",
1662
+ "frequency": "always",
1663
+ "fields": [
1664
+ {
1665
+ "name": "rating",
1666
+ "label": "How did we do?",
1667
+ "type": "radio",
1668
+ "options": [
1669
+ {
1670
+ "value": "good"
1481
1671
  },
1482
- "minSelections": {
1483
- "description": "Minimum picks — `multiselect`.",
1484
- "type": "integer",
1485
- "minimum": 0,
1486
- "maximum": 50
1672
+ {
1673
+ "value": "bad"
1674
+ }
1675
+ ]
1676
+ }
1677
+ ]
1678
+ },
1679
+ {
1680
+ "id": "appointment",
1681
+ "on": [
1682
+ "page-area:booking",
1683
+ "manual"
1684
+ ],
1685
+ "mirrorToContext": false,
1686
+ "fields": [
1687
+ {
1688
+ "name": "department",
1689
+ "label": "Department",
1690
+ "type": "select",
1691
+ "options": [
1692
+ {
1693
+ "value": "dermatology"
1487
1694
  },
1488
- "maxSelections": {
1489
- "description": "Maximum picks — `multiselect`.",
1490
- "type": "integer",
1491
- "minimum": 1,
1492
- "maximum": 50
1695
+ {
1696
+ "value": "cardiology"
1493
1697
  }
1494
- },
1495
- "additionalProperties": {}
1698
+ ]
1496
1699
  },
1497
- "allowOther": {
1498
- "description": "`select`/`radio`/`multiselect` — allow a free-text 'Other' answer.",
1499
- "type": "boolean"
1700
+ {
1701
+ "name": "date",
1702
+ "label": "Preferred date",
1703
+ "type": "date"
1500
1704
  },
1501
- "validationHint": {
1502
- "description": "Small helper line under the field.",
1503
- "type": "string",
1504
- "maxLength": 280
1705
+ {
1706
+ "name": "consent",
1707
+ "label": "I consent to be contacted",
1708
+ "type": "checkbox"
1505
1709
  }
1506
- },
1507
- "required": [
1508
- "name",
1509
- "label",
1510
- "type",
1511
- "required"
1512
- ],
1513
- "additionalProperties": {}
1710
+ ]
1514
1711
  }
1515
- },
1516
- "submitLabel": {
1517
- "description": "Submit button label (default 'Start chat').",
1518
- "type": "string",
1519
- "maxLength": 60
1520
- },
1521
- "skippable": {
1522
- "default": false,
1523
- "description": "Show a Skip control to dismiss the whole form.",
1524
- "type": "boolean"
1525
- }
1526
- },
1527
- "required": [
1528
- "enabled",
1529
- "fields",
1530
- "skippable"
1531
- ],
1532
- "additionalProperties": {},
1533
- "description": "Pre-chat intake form — a blocking lead/sales-capture gate of typed, validated fields. The same field shape powers the AI ask-input tool. Off by default.",
1534
- "examples": [
1535
- {
1536
- "enabled": false
1537
- },
1538
- {
1539
- "enabled": true,
1540
- "title": "Before we start",
1541
- "description": "Tell us who you are so we can help faster.",
1542
- "submitLabel": "Start chat",
1543
- "fields": [
1544
- {
1545
- "name": "name",
1546
- "label": "Your name",
1547
- "type": "text",
1548
- "required": true
1549
- },
1550
- {
1551
- "name": "email",
1552
- "label": "Work email",
1553
- "type": "email",
1554
- "required": true
1555
- },
1556
- {
1557
- "name": "phone",
1558
- "label": "Phone",
1559
- "type": "tel",
1560
- "required": false
1561
- },
1562
- {
1563
- "name": "topic",
1564
- "label": "What can we help with?",
1565
- "type": "select",
1566
- "required": true,
1567
- "options": [
1568
- {
1569
- "value": "sales",
1570
- "label": "Talk to sales"
1571
- },
1572
- {
1573
- "value": "support",
1574
- "label": "Get support"
1575
- }
1576
- ]
1577
- }
1578
- ]
1579
- }
1712
+ ]
1580
1713
  ]
1581
1714
  },
1582
1715
  "modules": {
@@ -2982,229 +3115,362 @@
2982
3115
  }
2983
3116
  ]
2984
3117
  },
2985
- "intake": {
2986
- "type": "object",
2987
- "properties": {
2988
- "enabled": {
2989
- "default": false,
2990
- "description": "Turn the pre-chat intake gate on. Off by default.",
2991
- "type": "boolean"
2992
- },
2993
- "title": {
2994
- "description": "Form heading (e.g. 'Before we start').",
2995
- "type": "string",
2996
- "maxLength": 120
2997
- },
2998
- "description": {
2999
- "description": "Sub-heading under the title.",
3000
- "type": "string",
3001
- "maxLength": 500
3002
- },
3003
- "fields": {
3004
- "default": [],
3005
- "description": "The questions to ask, in order. The gate is only active when there is ≥1 field.",
3006
- "maxItems": 20,
3007
- "type": "array",
3008
- "items": {
3009
- "type": "object",
3010
- "properties": {
3011
- "name": {
3012
- "type": "string",
3013
- "minLength": 1,
3014
- "maxLength": 80,
3015
- "description": "Stable key the answer is recorded under (e.g. `email`). Sent to the agent as captured context."
3016
- },
3017
- "label": {
3018
- "type": "string",
3019
- "minLength": 1,
3020
- "maxLength": 200,
3021
- "description": "Visible field label."
3022
- },
3023
- "type": {
3118
+ "forms": {
3119
+ "maxItems": 20,
3120
+ "type": "array",
3121
+ "items": {
3122
+ "type": "object",
3123
+ "properties": {
3124
+ "id": {
3125
+ "type": "string",
3126
+ "minLength": 1,
3127
+ "maxLength": 80,
3128
+ "description": "Stable id — persistence dedupe + `manual` openForm(id)."
3129
+ },
3130
+ "on": {
3131
+ "anyOf": [
3132
+ {
3024
3133
  "type": "string",
3025
- "enum": [
3026
- "text",
3027
- "email",
3028
- "tel",
3029
- "url",
3030
- "number",
3031
- "textarea",
3032
- "select",
3033
- "radio",
3034
- "checkbox",
3035
- "multiselect"
3036
- ],
3037
- "description": "Field renderer + built-in validation (`email`/`tel`/`url`/`number` are checked)."
3134
+ "pattern": "^(pre-chat|conversation-closed|panel-close|manual|after-messages:\\d+|idle:\\d+|page-area:.+)$",
3135
+ "description": "When the form fires: `pre-chat`, `after-messages:N`, `conversation-closed`, `panel-close`, `idle:Nseconds`, `page-area:<area>`, or `manual`."
3038
3136
  },
3039
- "placeholder": {
3040
- "type": "string",
3041
- "maxLength": 200
3137
+ {
3138
+ "minItems": 1,
3139
+ "maxItems": 8,
3140
+ "type": "array",
3141
+ "items": {
3142
+ "type": "string",
3143
+ "pattern": "^(pre-chat|conversation-closed|panel-close|manual|after-messages:\\d+|idle:\\d+|page-area:.+)$",
3144
+ "description": "When the form fires: `pre-chat`, `after-messages:N`, `conversation-closed`, `panel-close`, `idle:Nseconds`, `page-area:<area>`, or `manual`."
3145
+ }
3146
+ }
3147
+ ],
3148
+ "description": "Trigger(s) that surface this form."
3149
+ },
3150
+ "when": {
3151
+ "description": "Extra eligibility predicate evaluated when a trigger fires.",
3152
+ "type": "object",
3153
+ "properties": {
3154
+ "missingContext": {
3155
+ "description": "Only show if `userContext` lacks ALL of these keys (progressive profiling).",
3156
+ "maxItems": 20,
3157
+ "type": "array",
3158
+ "items": {
3159
+ "type": "string",
3160
+ "maxLength": 80
3161
+ }
3042
3162
  },
3043
- "required": {
3044
- "default": true,
3045
- "description": "Must be answered to submit. Set `false` to allow skipping.",
3046
- "type": "boolean"
3163
+ "pageArea": {
3164
+ "description": "Only on these `pageContext.area` values.",
3165
+ "anyOf": [
3166
+ {
3167
+ "type": "string",
3168
+ "maxLength": 64
3169
+ },
3170
+ {
3171
+ "maxItems": 20,
3172
+ "type": "array",
3173
+ "items": {
3174
+ "type": "string",
3175
+ "maxLength": 64
3176
+ }
3177
+ }
3178
+ ]
3047
3179
  },
3048
- "defaultValue": {
3049
- "description": "Pre-filled value (comma-join for `multiselect`).",
3050
- "type": "string",
3051
- "maxLength": 2000
3180
+ "minMessages": {
3181
+ "type": "integer",
3182
+ "minimum": 0,
3183
+ "maximum": 1000
3052
3184
  },
3053
- "options": {
3054
- "description": "Choices for `select` / `radio` / `multiselect`.",
3055
- "maxItems": 50,
3056
- "type": "array",
3057
- "items": {
3185
+ "maxMessages": {
3186
+ "type": "integer",
3187
+ "minimum": 0,
3188
+ "maximum": 1000
3189
+ }
3190
+ },
3191
+ "additionalProperties": {}
3192
+ },
3193
+ "fields": {
3194
+ "default": [],
3195
+ "description": "The questions, in order. ≥1 required to show.",
3196
+ "maxItems": 20,
3197
+ "type": "array",
3198
+ "items": {
3199
+ "type": "object",
3200
+ "properties": {
3201
+ "name": {
3202
+ "type": "string",
3203
+ "minLength": 1,
3204
+ "maxLength": 80,
3205
+ "description": "Stable key the answer is recorded under (e.g. `email`)."
3206
+ },
3207
+ "label": {
3208
+ "type": "string",
3209
+ "minLength": 1,
3210
+ "maxLength": 200,
3211
+ "description": "Visible field label."
3212
+ },
3213
+ "type": {
3214
+ "type": "string",
3215
+ "enum": [
3216
+ "text",
3217
+ "email",
3218
+ "tel",
3219
+ "url",
3220
+ "number",
3221
+ "date",
3222
+ "time",
3223
+ "textarea",
3224
+ "select",
3225
+ "radio",
3226
+ "checkbox",
3227
+ "multiselect"
3228
+ ],
3229
+ "description": "Field renderer + built-in validation (email/tel/url/number/date/time checked)."
3230
+ },
3231
+ "placeholder": {
3232
+ "type": "string",
3233
+ "maxLength": 200
3234
+ },
3235
+ "required": {
3236
+ "default": true,
3237
+ "description": "Must be answered to submit. `false` allows skipping.",
3238
+ "type": "boolean"
3239
+ },
3240
+ "defaultValue": {
3241
+ "description": "Pre-filled value (comma-join for `multiselect`).",
3242
+ "type": "string",
3243
+ "maxLength": 2000
3244
+ },
3245
+ "options": {
3246
+ "description": "Choices for `select` / `radio` / `multiselect`.",
3247
+ "maxItems": 50,
3248
+ "type": "array",
3249
+ "items": {
3250
+ "type": "object",
3251
+ "properties": {
3252
+ "value": {
3253
+ "type": "string",
3254
+ "minLength": 1,
3255
+ "maxLength": 200,
3256
+ "description": "The value recorded when this choice is picked."
3257
+ },
3258
+ "label": {
3259
+ "description": "Display text; falls back to `value`.",
3260
+ "type": "string",
3261
+ "minLength": 1,
3262
+ "maxLength": 200
3263
+ },
3264
+ "description": {
3265
+ "description": "Optional sub-label under the choice.",
3266
+ "type": "string",
3267
+ "maxLength": 280
3268
+ }
3269
+ },
3270
+ "required": [
3271
+ "value"
3272
+ ],
3273
+ "additionalProperties": {}
3274
+ }
3275
+ },
3276
+ "validation": {
3058
3277
  "type": "object",
3059
3278
  "properties": {
3060
- "value": {
3279
+ "pattern": {
3280
+ "description": "Regex source string — text-like fields (e.g. `^\\d{5}$`).",
3061
3281
  "type": "string",
3062
- "minLength": 1,
3063
- "maxLength": 200,
3064
- "description": "The value recorded when this choice is picked."
3282
+ "maxLength": 500
3065
3283
  },
3066
- "label": {
3067
- "description": "Display text; falls back to `value`.",
3068
- "type": "string",
3069
- "minLength": 1,
3070
- "maxLength": 200
3284
+ "minLength": {
3285
+ "type": "integer",
3286
+ "minimum": 0,
3287
+ "maximum": 10000
3071
3288
  },
3072
- "description": {
3073
- "description": "Optional sub-label under the choice.",
3074
- "type": "string",
3075
- "maxLength": 280
3289
+ "maxLength": {
3290
+ "type": "integer",
3291
+ "minimum": 1,
3292
+ "maximum": 10000
3293
+ },
3294
+ "min": {
3295
+ "description": "Minimum — `number` fields.",
3296
+ "type": "number"
3297
+ },
3298
+ "max": {
3299
+ "description": "Maximum — `number` fields.",
3300
+ "type": "number"
3301
+ },
3302
+ "minSelections": {
3303
+ "description": "Minimum picks — `multiselect`.",
3304
+ "type": "integer",
3305
+ "minimum": 0,
3306
+ "maximum": 50
3307
+ },
3308
+ "maxSelections": {
3309
+ "description": "Maximum picks — `multiselect`.",
3310
+ "type": "integer",
3311
+ "minimum": 1,
3312
+ "maximum": 50
3076
3313
  }
3077
3314
  },
3078
- "required": [
3079
- "value"
3080
- ],
3081
3315
  "additionalProperties": {}
3316
+ },
3317
+ "allowOther": {
3318
+ "description": "`select`/`radio`/`multiselect` — allow a free-text 'Other' answer.",
3319
+ "type": "boolean"
3320
+ },
3321
+ "validationHint": {
3322
+ "description": "Small helper line under the field.",
3323
+ "type": "string",
3324
+ "maxLength": 280
3082
3325
  }
3083
3326
  },
3084
- "validation": {
3085
- "type": "object",
3086
- "properties": {
3087
- "pattern": {
3088
- "description": "Regex source string — text-like fields (e.g. `^\\d{5}$`).",
3089
- "type": "string",
3090
- "maxLength": 500
3091
- },
3092
- "minLength": {
3093
- "type": "integer",
3094
- "minimum": 0,
3095
- "maximum": 10000
3096
- },
3097
- "maxLength": {
3098
- "type": "integer",
3099
- "minimum": 1,
3100
- "maximum": 10000
3101
- },
3102
- "min": {
3103
- "description": "Minimum — `number` fields.",
3104
- "type": "number"
3105
- },
3106
- "max": {
3107
- "description": "Maximum `number` fields.",
3108
- "type": "number"
3327
+ "required": [
3328
+ "name",
3329
+ "label",
3330
+ "type",
3331
+ "required"
3332
+ ],
3333
+ "additionalProperties": {}
3334
+ }
3335
+ },
3336
+ "title": {
3337
+ "type": "string",
3338
+ "maxLength": 120
3339
+ },
3340
+ "description": {
3341
+ "type": "string",
3342
+ "maxLength": 500
3343
+ },
3344
+ "submitLabel": {
3345
+ "type": "string",
3346
+ "maxLength": 60
3347
+ },
3348
+ "skippable": {
3349
+ "default": false,
3350
+ "description": "Show a Skip control.",
3351
+ "type": "boolean"
3352
+ },
3353
+ "blocking": {
3354
+ "default": false,
3355
+ "description": "Gate the composer (honored only for `pre-chat` / `after-messages`); otherwise renders inline.",
3356
+ "type": "boolean"
3357
+ },
3358
+ "frequency": {
3359
+ "default": "once",
3360
+ "description": "`once` (persisted per-device), `session`, or `always`.",
3361
+ "type": "string",
3362
+ "enum": [
3363
+ "once",
3364
+ "session",
3365
+ "always"
3366
+ ]
3367
+ },
3368
+ "mirrorToContext": {
3369
+ "default": true,
3370
+ "description": "Mirror the answers into `userContext` so they ride every request. Set `false` for PII (DOB, policy #, symptoms) — those then go only to the submit endpoint.",
3371
+ "type": "boolean"
3372
+ }
3373
+ },
3374
+ "required": [
3375
+ "id",
3376
+ "on",
3377
+ "fields",
3378
+ "skippable",
3379
+ "blocking",
3380
+ "frequency",
3381
+ "mirrorToContext"
3382
+ ],
3383
+ "additionalProperties": {}
3384
+ },
3385
+ "description": "Event-driven forms — an ordered list (order = priority). Each binds to trigger event(s), shows blocking or inline, dedupes by frequency, and records via `endpoints.forms`. Generic across verticals.",
3386
+ "examples": [
3387
+ [
3388
+ {
3389
+ "id": "intake",
3390
+ "on": "pre-chat",
3391
+ "blocking": true,
3392
+ "fields": [
3393
+ {
3394
+ "name": "email",
3395
+ "label": "Email",
3396
+ "type": "email"
3397
+ }
3398
+ ]
3399
+ }
3400
+ ],
3401
+ [
3402
+ {
3403
+ "id": "lead",
3404
+ "on": "pre-chat",
3405
+ "blocking": true,
3406
+ "title": "Before we start",
3407
+ "fields": [
3408
+ {
3409
+ "name": "name",
3410
+ "label": "Your name",
3411
+ "type": "text"
3412
+ },
3413
+ {
3414
+ "name": "email",
3415
+ "label": "Work email",
3416
+ "type": "email"
3417
+ }
3418
+ ]
3419
+ },
3420
+ {
3421
+ "id": "csat",
3422
+ "on": "conversation-closed",
3423
+ "frequency": "always",
3424
+ "fields": [
3425
+ {
3426
+ "name": "rating",
3427
+ "label": "How did we do?",
3428
+ "type": "radio",
3429
+ "options": [
3430
+ {
3431
+ "value": "good"
3109
3432
  },
3110
- "minSelections": {
3111
- "description": "Minimum picks — `multiselect`.",
3112
- "type": "integer",
3113
- "minimum": 0,
3114
- "maximum": 50
3433
+ {
3434
+ "value": "bad"
3435
+ }
3436
+ ]
3437
+ }
3438
+ ]
3439
+ },
3440
+ {
3441
+ "id": "appointment",
3442
+ "on": [
3443
+ "page-area:booking",
3444
+ "manual"
3445
+ ],
3446
+ "mirrorToContext": false,
3447
+ "fields": [
3448
+ {
3449
+ "name": "department",
3450
+ "label": "Department",
3451
+ "type": "select",
3452
+ "options": [
3453
+ {
3454
+ "value": "dermatology"
3115
3455
  },
3116
- "maxSelections": {
3117
- "description": "Maximum picks — `multiselect`.",
3118
- "type": "integer",
3119
- "minimum": 1,
3120
- "maximum": 50
3456
+ {
3457
+ "value": "cardiology"
3121
3458
  }
3122
- },
3123
- "additionalProperties": {}
3459
+ ]
3124
3460
  },
3125
- "allowOther": {
3126
- "description": "`select`/`radio`/`multiselect` — allow a free-text 'Other' answer.",
3127
- "type": "boolean"
3461
+ {
3462
+ "name": "date",
3463
+ "label": "Preferred date",
3464
+ "type": "date"
3128
3465
  },
3129
- "validationHint": {
3130
- "description": "Small helper line under the field.",
3131
- "type": "string",
3132
- "maxLength": 280
3466
+ {
3467
+ "name": "consent",
3468
+ "label": "I consent to be contacted",
3469
+ "type": "checkbox"
3133
3470
  }
3134
- },
3135
- "required": [
3136
- "name",
3137
- "label",
3138
- "type",
3139
- "required"
3140
- ],
3141
- "additionalProperties": {}
3471
+ ]
3142
3472
  }
3143
- },
3144
- "submitLabel": {
3145
- "description": "Submit button label (default 'Start chat').",
3146
- "type": "string",
3147
- "maxLength": 60
3148
- },
3149
- "skippable": {
3150
- "default": false,
3151
- "description": "Show a Skip control to dismiss the whole form.",
3152
- "type": "boolean"
3153
- }
3154
- },
3155
- "required": [
3156
- "enabled",
3157
- "fields",
3158
- "skippable"
3159
- ],
3160
- "additionalProperties": {},
3161
- "description": "Pre-chat intake form — a blocking lead/sales-capture gate of typed, validated fields. The same field shape powers the AI ask-input tool. Off by default.",
3162
- "examples": [
3163
- {
3164
- "enabled": false
3165
- },
3166
- {
3167
- "enabled": true,
3168
- "title": "Before we start",
3169
- "description": "Tell us who you are so we can help faster.",
3170
- "submitLabel": "Start chat",
3171
- "fields": [
3172
- {
3173
- "name": "name",
3174
- "label": "Your name",
3175
- "type": "text",
3176
- "required": true
3177
- },
3178
- {
3179
- "name": "email",
3180
- "label": "Work email",
3181
- "type": "email",
3182
- "required": true
3183
- },
3184
- {
3185
- "name": "phone",
3186
- "label": "Phone",
3187
- "type": "tel",
3188
- "required": false
3189
- },
3190
- {
3191
- "name": "topic",
3192
- "label": "What can we help with?",
3193
- "type": "select",
3194
- "required": true,
3195
- "options": [
3196
- {
3197
- "value": "sales",
3198
- "label": "Talk to sales"
3199
- },
3200
- {
3201
- "value": "support",
3202
- "label": "Get support"
3203
- }
3204
- ]
3205
- }
3206
- ]
3207
- }
3473
+ ]
3208
3474
  ]
3209
3475
  },
3210
3476
  "modules": {