@helpai/elements 0.13.1 → 0.14.1

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,354 @@
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 so the visitor can dismiss the form.",
1590
+ "type": "boolean"
1591
+ },
1592
+ "frequency": {
1593
+ "default": "once",
1594
+ "description": "`once` (persisted per-device), `session`, or `always`.",
1595
+ "type": "string",
1596
+ "enum": [
1597
+ "once",
1598
+ "session",
1599
+ "always"
1600
+ ]
1601
+ },
1602
+ "mirrorToContext": {
1603
+ "default": true,
1604
+ "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.",
1605
+ "type": "boolean"
1606
+ }
1607
+ },
1608
+ "required": [
1609
+ "id",
1610
+ "on",
1611
+ "fields",
1612
+ "skippable",
1613
+ "frequency",
1614
+ "mirrorToContext"
1615
+ ],
1616
+ "additionalProperties": {}
1617
+ },
1618
+ "description": "Event-driven forms — an ordered list (order = priority). Each binds to trigger event(s), gates the chat until submit/skip, dedupes by frequency, and records via `endpoints.submitForm`. Generic across verticals.",
1619
+ "examples": [
1620
+ [
1621
+ {
1622
+ "id": "intake",
1623
+ "on": "pre-chat",
1624
+ "fields": [
1625
+ {
1626
+ "name": "email",
1627
+ "label": "Email",
1628
+ "type": "email"
1629
+ }
1630
+ ]
1631
+ }
1632
+ ],
1633
+ [
1634
+ {
1635
+ "id": "lead",
1636
+ "on": "pre-chat",
1637
+ "title": "Before we start",
1638
+ "fields": [
1639
+ {
1640
+ "name": "name",
1641
+ "label": "Your name",
1642
+ "type": "text"
1643
+ },
1644
+ {
1645
+ "name": "email",
1646
+ "label": "Work email",
1647
+ "type": "email"
1648
+ }
1649
+ ]
1650
+ },
1651
+ {
1652
+ "id": "csat",
1653
+ "on": "conversation-closed",
1654
+ "frequency": "always",
1655
+ "fields": [
1656
+ {
1657
+ "name": "rating",
1658
+ "label": "How did we do?",
1659
+ "type": "radio",
1660
+ "options": [
1661
+ {
1662
+ "value": "good"
1481
1663
  },
1482
- "minSelections": {
1483
- "description": "Minimum picks — `multiselect`.",
1484
- "type": "integer",
1485
- "minimum": 0,
1486
- "maximum": 50
1664
+ {
1665
+ "value": "bad"
1666
+ }
1667
+ ]
1668
+ }
1669
+ ]
1670
+ },
1671
+ {
1672
+ "id": "appointment",
1673
+ "on": [
1674
+ "page-area:booking",
1675
+ "manual"
1676
+ ],
1677
+ "mirrorToContext": false,
1678
+ "fields": [
1679
+ {
1680
+ "name": "department",
1681
+ "label": "Department",
1682
+ "type": "select",
1683
+ "options": [
1684
+ {
1685
+ "value": "dermatology"
1487
1686
  },
1488
- "maxSelections": {
1489
- "description": "Maximum picks — `multiselect`.",
1490
- "type": "integer",
1491
- "minimum": 1,
1492
- "maximum": 50
1687
+ {
1688
+ "value": "cardiology"
1493
1689
  }
1494
- },
1495
- "additionalProperties": {}
1690
+ ]
1496
1691
  },
1497
- "allowOther": {
1498
- "description": "`select`/`radio`/`multiselect` — allow a free-text 'Other' answer.",
1499
- "type": "boolean"
1692
+ {
1693
+ "name": "date",
1694
+ "label": "Preferred date",
1695
+ "type": "date"
1500
1696
  },
1501
- "validationHint": {
1502
- "description": "Small helper line under the field.",
1503
- "type": "string",
1504
- "maxLength": 280
1697
+ {
1698
+ "name": "consent",
1699
+ "label": "I consent to be contacted",
1700
+ "type": "checkbox"
1505
1701
  }
1506
- },
1507
- "required": [
1508
- "name",
1509
- "label",
1510
- "type",
1511
- "required"
1512
- ],
1513
- "additionalProperties": {}
1702
+ ]
1514
1703
  }
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
- }
1704
+ ]
1580
1705
  ]
1581
1706
  },
1582
1707
  "modules": {
@@ -2982,229 +3107,354 @@
2982
3107
  }
2983
3108
  ]
2984
3109
  },
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": {
3110
+ "forms": {
3111
+ "maxItems": 20,
3112
+ "type": "array",
3113
+ "items": {
3114
+ "type": "object",
3115
+ "properties": {
3116
+ "id": {
3117
+ "type": "string",
3118
+ "minLength": 1,
3119
+ "maxLength": 80,
3120
+ "description": "Stable id — persistence dedupe + `manual` openForm(id)."
3121
+ },
3122
+ "on": {
3123
+ "anyOf": [
3124
+ {
3024
3125
  "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)."
3126
+ "pattern": "^(pre-chat|conversation-closed|panel-close|manual|after-messages:\\d+|idle:\\d+|page-area:.+)$",
3127
+ "description": "When the form fires: `pre-chat`, `after-messages:N`, `conversation-closed`, `panel-close`, `idle:Nseconds`, `page-area:<area>`, or `manual`."
3038
3128
  },
3039
- "placeholder": {
3040
- "type": "string",
3041
- "maxLength": 200
3129
+ {
3130
+ "minItems": 1,
3131
+ "maxItems": 8,
3132
+ "type": "array",
3133
+ "items": {
3134
+ "type": "string",
3135
+ "pattern": "^(pre-chat|conversation-closed|panel-close|manual|after-messages:\\d+|idle:\\d+|page-area:.+)$",
3136
+ "description": "When the form fires: `pre-chat`, `after-messages:N`, `conversation-closed`, `panel-close`, `idle:Nseconds`, `page-area:<area>`, or `manual`."
3137
+ }
3138
+ }
3139
+ ],
3140
+ "description": "Trigger(s) that surface this form."
3141
+ },
3142
+ "when": {
3143
+ "description": "Extra eligibility predicate evaluated when a trigger fires.",
3144
+ "type": "object",
3145
+ "properties": {
3146
+ "missingContext": {
3147
+ "description": "Only show if `userContext` lacks ALL of these keys (progressive profiling).",
3148
+ "maxItems": 20,
3149
+ "type": "array",
3150
+ "items": {
3151
+ "type": "string",
3152
+ "maxLength": 80
3153
+ }
3042
3154
  },
3043
- "required": {
3044
- "default": true,
3045
- "description": "Must be answered to submit. Set `false` to allow skipping.",
3046
- "type": "boolean"
3155
+ "pageArea": {
3156
+ "description": "Only on these `pageContext.area` values.",
3157
+ "anyOf": [
3158
+ {
3159
+ "type": "string",
3160
+ "maxLength": 64
3161
+ },
3162
+ {
3163
+ "maxItems": 20,
3164
+ "type": "array",
3165
+ "items": {
3166
+ "type": "string",
3167
+ "maxLength": 64
3168
+ }
3169
+ }
3170
+ ]
3047
3171
  },
3048
- "defaultValue": {
3049
- "description": "Pre-filled value (comma-join for `multiselect`).",
3050
- "type": "string",
3051
- "maxLength": 2000
3172
+ "minMessages": {
3173
+ "type": "integer",
3174
+ "minimum": 0,
3175
+ "maximum": 1000
3052
3176
  },
3053
- "options": {
3054
- "description": "Choices for `select` / `radio` / `multiselect`.",
3055
- "maxItems": 50,
3056
- "type": "array",
3057
- "items": {
3177
+ "maxMessages": {
3178
+ "type": "integer",
3179
+ "minimum": 0,
3180
+ "maximum": 1000
3181
+ }
3182
+ },
3183
+ "additionalProperties": {}
3184
+ },
3185
+ "fields": {
3186
+ "default": [],
3187
+ "description": "The questions, in order. ≥1 required to show.",
3188
+ "maxItems": 20,
3189
+ "type": "array",
3190
+ "items": {
3191
+ "type": "object",
3192
+ "properties": {
3193
+ "name": {
3194
+ "type": "string",
3195
+ "minLength": 1,
3196
+ "maxLength": 80,
3197
+ "description": "Stable key the answer is recorded under (e.g. `email`)."
3198
+ },
3199
+ "label": {
3200
+ "type": "string",
3201
+ "minLength": 1,
3202
+ "maxLength": 200,
3203
+ "description": "Visible field label."
3204
+ },
3205
+ "type": {
3206
+ "type": "string",
3207
+ "enum": [
3208
+ "text",
3209
+ "email",
3210
+ "tel",
3211
+ "url",
3212
+ "number",
3213
+ "date",
3214
+ "time",
3215
+ "textarea",
3216
+ "select",
3217
+ "radio",
3218
+ "checkbox",
3219
+ "multiselect"
3220
+ ],
3221
+ "description": "Field renderer + built-in validation (email/tel/url/number/date/time checked)."
3222
+ },
3223
+ "placeholder": {
3224
+ "type": "string",
3225
+ "maxLength": 200
3226
+ },
3227
+ "required": {
3228
+ "default": true,
3229
+ "description": "Must be answered to submit. `false` allows skipping.",
3230
+ "type": "boolean"
3231
+ },
3232
+ "defaultValue": {
3233
+ "description": "Pre-filled value (comma-join for `multiselect`).",
3234
+ "type": "string",
3235
+ "maxLength": 2000
3236
+ },
3237
+ "options": {
3238
+ "description": "Choices for `select` / `radio` / `multiselect`.",
3239
+ "maxItems": 50,
3240
+ "type": "array",
3241
+ "items": {
3242
+ "type": "object",
3243
+ "properties": {
3244
+ "value": {
3245
+ "type": "string",
3246
+ "minLength": 1,
3247
+ "maxLength": 200,
3248
+ "description": "The value recorded when this choice is picked."
3249
+ },
3250
+ "label": {
3251
+ "description": "Display text; falls back to `value`.",
3252
+ "type": "string",
3253
+ "minLength": 1,
3254
+ "maxLength": 200
3255
+ },
3256
+ "description": {
3257
+ "description": "Optional sub-label under the choice.",
3258
+ "type": "string",
3259
+ "maxLength": 280
3260
+ }
3261
+ },
3262
+ "required": [
3263
+ "value"
3264
+ ],
3265
+ "additionalProperties": {}
3266
+ }
3267
+ },
3268
+ "validation": {
3058
3269
  "type": "object",
3059
3270
  "properties": {
3060
- "value": {
3271
+ "pattern": {
3272
+ "description": "Regex source string — text-like fields (e.g. `^\\d{5}$`).",
3061
3273
  "type": "string",
3062
- "minLength": 1,
3063
- "maxLength": 200,
3064
- "description": "The value recorded when this choice is picked."
3274
+ "maxLength": 500
3065
3275
  },
3066
- "label": {
3067
- "description": "Display text; falls back to `value`.",
3068
- "type": "string",
3069
- "minLength": 1,
3070
- "maxLength": 200
3276
+ "minLength": {
3277
+ "type": "integer",
3278
+ "minimum": 0,
3279
+ "maximum": 10000
3071
3280
  },
3072
- "description": {
3073
- "description": "Optional sub-label under the choice.",
3074
- "type": "string",
3075
- "maxLength": 280
3281
+ "maxLength": {
3282
+ "type": "integer",
3283
+ "minimum": 1,
3284
+ "maximum": 10000
3285
+ },
3286
+ "min": {
3287
+ "description": "Minimum — `number` fields.",
3288
+ "type": "number"
3289
+ },
3290
+ "max": {
3291
+ "description": "Maximum — `number` fields.",
3292
+ "type": "number"
3293
+ },
3294
+ "minSelections": {
3295
+ "description": "Minimum picks — `multiselect`.",
3296
+ "type": "integer",
3297
+ "minimum": 0,
3298
+ "maximum": 50
3299
+ },
3300
+ "maxSelections": {
3301
+ "description": "Maximum picks — `multiselect`.",
3302
+ "type": "integer",
3303
+ "minimum": 1,
3304
+ "maximum": 50
3076
3305
  }
3077
3306
  },
3078
- "required": [
3079
- "value"
3080
- ],
3081
3307
  "additionalProperties": {}
3308
+ },
3309
+ "allowOther": {
3310
+ "description": "`select`/`radio`/`multiselect` — allow a free-text 'Other' answer.",
3311
+ "type": "boolean"
3312
+ },
3313
+ "validationHint": {
3314
+ "description": "Small helper line under the field.",
3315
+ "type": "string",
3316
+ "maxLength": 280
3082
3317
  }
3083
3318
  },
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"
3319
+ "required": [
3320
+ "name",
3321
+ "label",
3322
+ "type",
3323
+ "required"
3324
+ ],
3325
+ "additionalProperties": {}
3326
+ }
3327
+ },
3328
+ "title": {
3329
+ "type": "string",
3330
+ "maxLength": 120
3331
+ },
3332
+ "description": {
3333
+ "type": "string",
3334
+ "maxLength": 500
3335
+ },
3336
+ "submitLabel": {
3337
+ "type": "string",
3338
+ "maxLength": 60
3339
+ },
3340
+ "skippable": {
3341
+ "default": false,
3342
+ "description": "Show a Skip control so the visitor can dismiss the form.",
3343
+ "type": "boolean"
3344
+ },
3345
+ "frequency": {
3346
+ "default": "once",
3347
+ "description": "`once` (persisted per-device), `session`, or `always`.",
3348
+ "type": "string",
3349
+ "enum": [
3350
+ "once",
3351
+ "session",
3352
+ "always"
3353
+ ]
3354
+ },
3355
+ "mirrorToContext": {
3356
+ "default": true,
3357
+ "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.",
3358
+ "type": "boolean"
3359
+ }
3360
+ },
3361
+ "required": [
3362
+ "id",
3363
+ "on",
3364
+ "fields",
3365
+ "skippable",
3366
+ "frequency",
3367
+ "mirrorToContext"
3368
+ ],
3369
+ "additionalProperties": {}
3370
+ },
3371
+ "description": "Event-driven forms — an ordered list (order = priority). Each binds to trigger event(s), gates the chat until submit/skip, dedupes by frequency, and records via `endpoints.submitForm`. Generic across verticals.",
3372
+ "examples": [
3373
+ [
3374
+ {
3375
+ "id": "intake",
3376
+ "on": "pre-chat",
3377
+ "fields": [
3378
+ {
3379
+ "name": "email",
3380
+ "label": "Email",
3381
+ "type": "email"
3382
+ }
3383
+ ]
3384
+ }
3385
+ ],
3386
+ [
3387
+ {
3388
+ "id": "lead",
3389
+ "on": "pre-chat",
3390
+ "title": "Before we start",
3391
+ "fields": [
3392
+ {
3393
+ "name": "name",
3394
+ "label": "Your name",
3395
+ "type": "text"
3396
+ },
3397
+ {
3398
+ "name": "email",
3399
+ "label": "Work email",
3400
+ "type": "email"
3401
+ }
3402
+ ]
3403
+ },
3404
+ {
3405
+ "id": "csat",
3406
+ "on": "conversation-closed",
3407
+ "frequency": "always",
3408
+ "fields": [
3409
+ {
3410
+ "name": "rating",
3411
+ "label": "How did we do?",
3412
+ "type": "radio",
3413
+ "options": [
3414
+ {
3415
+ "value": "good"
3109
3416
  },
3110
- "minSelections": {
3111
- "description": "Minimum picks — `multiselect`.",
3112
- "type": "integer",
3113
- "minimum": 0,
3114
- "maximum": 50
3417
+ {
3418
+ "value": "bad"
3419
+ }
3420
+ ]
3421
+ }
3422
+ ]
3423
+ },
3424
+ {
3425
+ "id": "appointment",
3426
+ "on": [
3427
+ "page-area:booking",
3428
+ "manual"
3429
+ ],
3430
+ "mirrorToContext": false,
3431
+ "fields": [
3432
+ {
3433
+ "name": "department",
3434
+ "label": "Department",
3435
+ "type": "select",
3436
+ "options": [
3437
+ {
3438
+ "value": "dermatology"
3115
3439
  },
3116
- "maxSelections": {
3117
- "description": "Maximum picks — `multiselect`.",
3118
- "type": "integer",
3119
- "minimum": 1,
3120
- "maximum": 50
3440
+ {
3441
+ "value": "cardiology"
3121
3442
  }
3122
- },
3123
- "additionalProperties": {}
3443
+ ]
3124
3444
  },
3125
- "allowOther": {
3126
- "description": "`select`/`radio`/`multiselect` — allow a free-text 'Other' answer.",
3127
- "type": "boolean"
3445
+ {
3446
+ "name": "date",
3447
+ "label": "Preferred date",
3448
+ "type": "date"
3128
3449
  },
3129
- "validationHint": {
3130
- "description": "Small helper line under the field.",
3131
- "type": "string",
3132
- "maxLength": 280
3450
+ {
3451
+ "name": "consent",
3452
+ "label": "I consent to be contacted",
3453
+ "type": "checkbox"
3133
3454
  }
3134
- },
3135
- "required": [
3136
- "name",
3137
- "label",
3138
- "type",
3139
- "required"
3140
- ],
3141
- "additionalProperties": {}
3455
+ ]
3142
3456
  }
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
- }
3457
+ ]
3208
3458
  ]
3209
3459
  },
3210
3460
  "modules": {