@syntrologie/runtime-sdk 2.8.0-canary.211 → 2.8.0-canary.213

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.
@@ -442,1137 +442,1137 @@
442
442
  },
443
443
  "additionalProperties": false,
444
444
  "$defs": {
445
- "tile": {
445
+ "matchOp": {
446
446
  "type": "object",
447
447
  "properties": {
448
- "id": {
449
- "type": "string"
450
- },
451
- "title": {
452
- "type": "string"
453
- },
454
- "subtitle": {
455
- "type": "string"
448
+ "equals": {
449
+ "type": [
450
+ "string",
451
+ "number",
452
+ "boolean"
453
+ ]
456
454
  },
457
- "icon": {
455
+ "contains": {
458
456
  "type": "string"
457
+ }
458
+ },
459
+ "additionalProperties": false,
460
+ "description": "Match operator for counter filters. Exactly one of equals or contains must be specified.",
461
+ "oneOf": [
462
+ {
463
+ "required": [
464
+ "equals"
465
+ ]
459
466
  },
460
- "priority": {
461
- "type": "number"
462
- },
463
- "widget": {
464
- "type": "string",
465
- "enum": [
466
- "adaptive-faq:accordion",
467
- "adaptive-nav:tips",
468
- "adaptive-overlays:workflow-tracker",
469
- "adaptive-viz:chart"
470
- ],
471
- "description": "Registered widget ID. Must match an adaptive widget registration."
472
- },
473
- "props": {
474
- "type": "object",
475
- "additionalProperties": {}
476
- },
477
- "activation": {
478
- "$ref": "#/$defs/activationConfig",
479
- "description": "Route and condition-based activation for this tile"
480
- },
481
- "notifications": {
467
+ {
468
+ "required": [
469
+ "contains"
470
+ ]
471
+ }
472
+ ]
473
+ },
474
+ "counterDef": {
475
+ "type": "object",
476
+ "properties": {
477
+ "events": {
482
478
  "type": "array",
483
479
  "items": {
484
- "type": "object",
485
- "properties": {
486
- "on": {
487
- "type": "string"
488
- },
489
- "title": {
490
- "type": "string"
491
- },
492
- "body": {
493
- "type": "string"
494
- },
495
- "icon": {
496
- "type": "string"
497
- },
498
- "ttl": {
499
- "type": "number"
500
- },
501
- "cooldown": {
502
- "type": "number"
503
- },
504
- "deepLink": {
505
- "type": "object",
506
- "properties": {
507
- "tileId": {
508
- "type": "string"
509
- },
510
- "itemId": {
511
- "type": "string"
512
- }
513
- },
514
- "required": [
515
- "tileId"
516
- ],
517
- "additionalProperties": false
518
- }
519
- },
520
- "required": [
521
- "on",
522
- "title"
480
+ "type": "string",
481
+ "enum": [
482
+ "ui.click",
483
+ "ui.scroll",
484
+ "ui.input",
485
+ "ui.change",
486
+ "ui.submit",
487
+ "ui.hover",
488
+ "ui.idle",
489
+ "ui.scroll_thrash",
490
+ "ui.focus_bounce",
491
+ "nav.page_view",
492
+ "nav.page_leave",
493
+ "behavior.rage_click",
494
+ "behavior.hesitation",
495
+ "behavior.confusion"
523
496
  ],
524
- "additionalProperties": false
525
- }
497
+ "description": "Event name to count. ui.* = user interactions and behavioral detectors, nav.* = page navigation, behavior.* = derived behavioral signals."
498
+ },
499
+ "minItems": 1,
500
+ "description": "Event names to count. Use values from the countable events enum."
501
+ },
502
+ "match": {
503
+ "type": "object",
504
+ "additionalProperties": {
505
+ "$ref": "#/$defs/matchOp"
506
+ },
507
+ "description": "Property filters. Keys are event prop names or element-chain fields (tag_name, $el_text, attr__*). All entries AND together."
526
508
  }
527
509
  },
528
510
  "required": [
529
- "id",
530
- "widget"
511
+ "events"
531
512
  ],
532
513
  "additionalProperties": false,
533
- "allOf": [
534
- {
535
- "if": {
536
- "properties": {
537
- "widget": {
538
- "const": "adaptive-faq:accordion"
539
- }
540
- },
541
- "required": [
542
- "widget"
543
- ]
544
- },
545
- "then": {
546
- "properties": {
547
- "props": {
548
- "$ref": "#/$defs/faqAccordionProps"
549
- }
550
- }
551
- }
552
- },
553
- {
554
- "if": {
555
- "properties": {
556
- "widget": {
557
- "const": "adaptive-nav:tips"
558
- }
559
- },
560
- "required": [
561
- "widget"
562
- ]
563
- },
564
- "then": {
565
- "properties": {
566
- "props": {
567
- "$ref": "#/$defs/navTipsProps"
568
- }
569
- }
570
- }
514
+ "description": "Defines what events to count. Registered as an accumulator predicate at config-load time."
515
+ },
516
+ "activationConfig": {
517
+ "type": "object",
518
+ "properties": {
519
+ "routes": {
520
+ "$ref": "#/$defs/routeFilter"
571
521
  },
572
- {
573
- "if": {
574
- "properties": {
575
- "widget": {
576
- "const": "adaptive-overlays:workflow-tracker"
577
- }
578
- },
579
- "required": [
580
- "widget"
581
- ]
522
+ "onlyIfPopulated": {
523
+ "type": "boolean",
524
+ "description": "When true, tile is hidden if none of its props.actions[] have an active triggerWhen. Useful for tiles with all-deferred FAQ questions or nav tips. Default: false."
525
+ }
526
+ },
527
+ "required": [
528
+ "routes"
529
+ ],
530
+ "additionalProperties": false
531
+ },
532
+ "routeFilter": {
533
+ "type": "object",
534
+ "properties": {
535
+ "include": {
536
+ "type": "array",
537
+ "items": {
538
+ "type": "string"
582
539
  },
583
- "then": {
584
- "properties": {
585
- "props": {
586
- "$ref": "#/$defs/workflowTrackerProps"
587
- }
588
- }
589
- }
540
+ "description": "URL patterns where this tile appears. Supports *, **, :param wildcards. Use sparingly — mostly to isolate to specific webapp functionality. Omit to appear on all routes."
590
541
  },
591
- {
592
- "if": {
593
- "properties": {
594
- "widget": {
595
- "const": "adaptive-viz:chart"
596
- }
597
- },
598
- "required": [
599
- "widget"
600
- ]
542
+ "exclude": {
543
+ "type": "array",
544
+ "items": {
545
+ "type": "string"
601
546
  },
602
- "then": {
603
- "properties": {
604
- "props": {
605
- "$ref": "#/$defs/vizChartProps"
606
- }
607
- }
608
- }
547
+ "description": "URL patterns where this tile should NOT appear. Evaluated before include."
609
548
  }
610
- ]
549
+ },
550
+ "additionalProperties": false
611
551
  },
612
- "themeConfig": {
552
+ "ruleStrategy": {
613
553
  "type": "object",
614
554
  "properties": {
615
- "mode": {
555
+ "type": {
616
556
  "type": "string",
617
- "enum": [
618
- "dark",
619
- "light"
620
- ]
621
- },
622
- "fontFamily": {
623
- "type": "string"
557
+ "const": "rules"
624
558
  },
625
- "colorPrimary": {
626
- "type": "string"
559
+ "rules": {
560
+ "type": "array",
561
+ "items": {
562
+ "$ref": "#/$defs/rule"
563
+ },
564
+ "description": "Ordered list of rules. Evaluated top-to-bottom — first match wins."
627
565
  },
628
- "colorPrimaryHover": {
566
+ "default": {
567
+ "description": "Fallback value when no rule matches. For triggerWhen: false = do not fire by default."
568
+ }
569
+ },
570
+ "required": [
571
+ "type",
572
+ "rules",
573
+ "default"
574
+ ],
575
+ "additionalProperties": false,
576
+ "description": "Rule-based strategy. Evaluates rules top-to-bottom. First rule where ALL conditions match returns its value. If no rule matches, returns default. For triggerWhen: set value=true on matching rules, default=false."
577
+ },
578
+ "scoreStrategy": {
579
+ "type": "object",
580
+ "properties": {
581
+ "type": {
582
+ "type": "string",
583
+ "const": "score"
584
+ },
585
+ "field": {
629
586
  "type": "string"
630
587
  },
631
- "borderRadius": {
588
+ "threshold": {
589
+ "type": "number"
590
+ },
591
+ "above": {},
592
+ "below": {}
593
+ },
594
+ "required": [
595
+ "type",
596
+ "field",
597
+ "threshold",
598
+ "above",
599
+ "below"
600
+ ],
601
+ "additionalProperties": false,
602
+ "description": "Score-based strategy. Compares a field value against a threshold."
603
+ },
604
+ "modelStrategy": {
605
+ "type": "object",
606
+ "properties": {
607
+ "type": {
608
+ "type": "string",
609
+ "const": "model"
610
+ },
611
+ "modelId": {
632
612
  "type": "string"
633
613
  },
634
- "canvas": {
614
+ "inputs": {
615
+ "type": "array",
616
+ "items": {
617
+ "type": "string"
618
+ }
619
+ },
620
+ "outputMapping": {
621
+ "type": "object",
622
+ "additionalProperties": {}
623
+ },
624
+ "default": {}
625
+ },
626
+ "required": [
627
+ "type",
628
+ "modelId",
629
+ "inputs",
630
+ "outputMapping",
631
+ "default"
632
+ ],
633
+ "additionalProperties": false,
634
+ "description": "ML model strategy. Sends inputs to a model and maps outputs."
635
+ },
636
+ "externalStrategy": {
637
+ "type": "object",
638
+ "properties": {
639
+ "type": {
640
+ "type": "string",
641
+ "const": "external"
642
+ },
643
+ "endpoint": {
644
+ "type": "string"
645
+ },
646
+ "method": {
647
+ "type": "string",
648
+ "enum": [
649
+ "GET",
650
+ "POST"
651
+ ]
652
+ },
653
+ "default": {},
654
+ "timeoutMs": {
655
+ "type": "number"
656
+ }
657
+ },
658
+ "required": [
659
+ "type",
660
+ "endpoint",
661
+ "default"
662
+ ],
663
+ "additionalProperties": false,
664
+ "description": "External API strategy. Calls an endpoint to determine the value."
665
+ },
666
+ "rule": {
667
+ "type": "object",
668
+ "properties": {
669
+ "conditions": {
670
+ "type": "array",
671
+ "items": {
672
+ "$ref": "#/$defs/condition"
673
+ },
674
+ "description": "Array of conditions — ALL must match (AND logic) for this rule to fire."
675
+ },
676
+ "value": {
677
+ "description": "Value returned when all conditions match. For triggerWhen: true = fire the action."
678
+ }
679
+ },
680
+ "required": [
681
+ "conditions",
682
+ "value"
683
+ ],
684
+ "additionalProperties": false,
685
+ "description": "A single rule. ALL conditions must match (AND logic). Rules in a strategy are evaluated top-to-bottom — first rule where all conditions match wins and returns its value."
686
+ },
687
+ "condition": {
688
+ "oneOf": [
689
+ {
635
690
  "type": "object",
636
691
  "properties": {
637
- "position": {
692
+ "type": {
638
693
  "type": "string",
639
- "enum": [
640
- "left",
641
- "right"
642
- ]
694
+ "const": "page_url"
643
695
  },
644
- "layout": {
696
+ "url": {
645
697
  "type": "string",
646
- "enum": [
647
- "overlay",
648
- "push"
649
- ]
650
- },
651
- "background": {
652
- "type": "string"
653
- },
654
- "blur": {
655
- "type": "string"
656
- },
657
- "border": {
658
- "type": "string"
659
- },
660
- "width": {
661
- "type": "string"
662
- },
663
- "padding": {
664
- "type": "string"
665
- },
666
- "transitionDuration": {
667
- "type": "string"
668
- },
669
- "transitionEasing": {
670
- "type": "string"
671
- },
672
- "transitionFade": {
673
- "type": "string"
674
- },
675
- "backdrop": {
676
- "type": "boolean"
677
- },
678
- "backdropColor": {
679
- "type": "string"
698
+ "description": "URL path to match (e.g. \"/pricing\", \"/dashboard\")"
680
699
  }
681
700
  },
682
- "additionalProperties": false
701
+ "required": [
702
+ "type",
703
+ "url"
704
+ ],
705
+ "additionalProperties": false,
706
+ "description": "Fires when the current page URL matches."
683
707
  },
684
- "launcher": {
708
+ {
685
709
  "type": "object",
686
710
  "properties": {
687
- "background": {
688
- "type": "string"
689
- },
690
- "backgroundHover": {
691
- "type": "string"
692
- },
693
- "color": {
694
- "type": "string"
695
- },
696
- "size": {
697
- "type": "string"
698
- },
699
- "shadow": {
700
- "type": "string"
701
- },
702
- "borderRadius": {
703
- "type": "string"
711
+ "type": {
712
+ "type": "string",
713
+ "const": "route"
704
714
  },
705
- "icon": {
706
- "type": "string"
715
+ "routeId": {
716
+ "type": "string",
717
+ "description": "Named route ID from the route filter"
707
718
  }
708
719
  },
709
- "additionalProperties": false
720
+ "required": [
721
+ "type",
722
+ "routeId"
723
+ ],
724
+ "additionalProperties": false,
725
+ "description": "Fires when the current route matches a named route ID."
710
726
  },
711
- "tile": {
727
+ {
712
728
  "type": "object",
713
729
  "properties": {
714
- "background": {
715
- "type": "string"
716
- },
717
- "backgroundHover": {
718
- "type": "string"
730
+ "type": {
731
+ "type": "string",
732
+ "const": "anchor_visible"
719
733
  },
720
- "border": {
721
- "type": "string"
734
+ "anchorId": {
735
+ "type": "string",
736
+ "description": "CSS selector of the anchor element"
722
737
  },
723
- "borderRadius": {
724
- "type": "string"
738
+ "state": {
739
+ "type": "string",
740
+ "enum": [
741
+ "visible",
742
+ "present",
743
+ "absent"
744
+ ],
745
+ "description": "\"visible\" = in viewport, \"present\" = in DOM, \"absent\" = not in DOM"
746
+ }
747
+ },
748
+ "required": [
749
+ "type",
750
+ "anchorId",
751
+ "state"
752
+ ],
753
+ "additionalProperties": false,
754
+ "description": "Fires based on a DOM element visibility state (visible/present/absent in viewport or DOM)."
755
+ },
756
+ {
757
+ "type": "object",
758
+ "properties": {
759
+ "type": {
760
+ "type": "string",
761
+ "const": "event_occurred"
725
762
  },
726
- "shadow": {
727
- "type": "string"
763
+ "eventName": {
764
+ "type": "string",
765
+ "description": "Event name (e.g. \"ui.click\", \"$pageview\")"
728
766
  },
729
- "titleColor": {
730
- "type": "string"
731
- },
732
- "textColor": {
733
- "type": "string"
734
- },
735
- "iconBackground": {
736
- "type": "string"
737
- },
738
- "iconShadow": {
739
- "type": "string"
740
- },
741
- "headerPadding": {
742
- "type": "string"
743
- },
744
- "bodyPadding": {
745
- "type": "string"
746
- },
747
- "gap": {
748
- "type": "string"
767
+ "withinMs": {
768
+ "type": "number",
769
+ "description": "Time window in ms. Omit = any time this session."
749
770
  }
750
771
  },
751
- "additionalProperties": false
772
+ "required": [
773
+ "type",
774
+ "eventName"
775
+ ],
776
+ "additionalProperties": false,
777
+ "description": "Fires when a specific event has occurred during this session."
752
778
  },
753
- "overlay": {
779
+ {
754
780
  "type": "object",
755
781
  "properties": {
756
- "background": {
757
- "type": "string"
758
- },
759
- "textColor": {
760
- "type": "string"
761
- },
762
- "titleColor": {
763
- "type": "string"
782
+ "type": {
783
+ "type": "string",
784
+ "const": "state_equals"
764
785
  },
765
- "arrowColor": {
766
- "type": "string"
786
+ "key": {
787
+ "type": "string",
788
+ "description": "Key in the SDK persistent state store. Only valid for keys the host app explicitly sets via syntro.state.set()."
767
789
  },
768
- "arrowSize": {
769
- "type": "string"
790
+ "value": {
791
+ "description": "Expected value to match against"
792
+ }
793
+ },
794
+ "required": [
795
+ "type",
796
+ "key"
797
+ ],
798
+ "additionalProperties": false,
799
+ "description": "Checks the SDK persistent state store (localStorage). ONLY for host-app state set via syntro.state.set() — NOT for user attributes like region, device, or UTM params (those are handled by segment targeting). Do NOT use this for targeting."
800
+ },
801
+ {
802
+ "type": "object",
803
+ "properties": {
804
+ "type": {
805
+ "type": "string",
806
+ "const": "viewport"
770
807
  },
771
- "border": {
772
- "type": "string"
808
+ "minWidth": {
809
+ "type": "number",
810
+ "description": "Minimum viewport width in pixels"
773
811
  },
774
- "borderRadius": {
775
- "type": "string"
812
+ "maxWidth": {
813
+ "type": "number",
814
+ "description": "Maximum viewport width in pixels"
776
815
  },
777
- "scrimOpacity": {
778
- "type": "string"
816
+ "minHeight": {
817
+ "type": "number",
818
+ "description": "Minimum viewport height in pixels"
779
819
  },
780
- "highlightRing": {
781
- "type": "string"
820
+ "maxHeight": {
821
+ "type": "number",
822
+ "description": "Maximum viewport height in pixels"
782
823
  }
783
824
  },
784
- "additionalProperties": false
825
+ "required": [
826
+ "type"
827
+ ],
828
+ "additionalProperties": false,
829
+ "description": "Fires based on viewport (screen) size. Use for responsive behavior (e.g. mobile vs desktop)."
785
830
  },
786
- "notification": {
831
+ {
787
832
  "type": "object",
788
833
  "properties": {
789
- "background": {
790
- "type": "string"
791
- },
792
- "textColor": {
793
- "type": "string"
794
- },
795
- "textSecondaryColor": {
796
- "type": "string"
834
+ "type": {
835
+ "type": "string",
836
+ "const": "session_metric"
797
837
  },
798
- "border": {
799
- "type": "string"
838
+ "key": {
839
+ "type": "string",
840
+ "enum": [
841
+ "time_on_page",
842
+ "page_views",
843
+ "scroll_depth"
844
+ ],
845
+ "description": "Session metric key: \"time_on_page\", \"page_views\", or \"scroll_depth\""
800
846
  },
801
- "borderRadius": {
802
- "type": "string"
847
+ "operator": {
848
+ "type": "string",
849
+ "enum": [
850
+ "gte",
851
+ "lte",
852
+ "eq",
853
+ "gt",
854
+ "lt"
855
+ ]
803
856
  },
804
- "successColor": {
805
- "type": "string"
857
+ "threshold": {
858
+ "type": "number",
859
+ "description": "Numeric threshold to compare against"
860
+ }
861
+ },
862
+ "required": [
863
+ "type",
864
+ "key",
865
+ "operator",
866
+ "threshold"
867
+ ],
868
+ "additionalProperties": false,
869
+ "description": "Fires when a session metric crosses a threshold. Valid keys: \"time_on_page\" (seconds), \"page_views\" (count), \"scroll_depth\" (0-100)."
870
+ },
871
+ {
872
+ "type": "object",
873
+ "properties": {
874
+ "type": {
875
+ "type": "string",
876
+ "const": "dismissed"
806
877
  },
807
- "warningColor": {
808
- "type": "string"
878
+ "key": {
879
+ "type": "string",
880
+ "description": "Dismissal key (usually a tile or action ID)"
809
881
  },
810
- "errorColor": {
811
- "type": "string"
882
+ "inverted": {
883
+ "type": "boolean",
884
+ "description": "When true, fires if NOT dismissed (default behavior)"
885
+ }
886
+ },
887
+ "required": [
888
+ "type",
889
+ "key"
890
+ ],
891
+ "additionalProperties": false,
892
+ "description": "Checks if an item has been dismissed by the user."
893
+ },
894
+ {
895
+ "type": "object",
896
+ "properties": {
897
+ "type": {
898
+ "type": "string",
899
+ "const": "cooldown_active"
812
900
  },
813
- "iconBackground": {
814
- "type": "string"
901
+ "key": {
902
+ "type": "string",
903
+ "description": "Cooldown key"
815
904
  },
816
- "progressGradient": {
817
- "type": "string"
905
+ "inverted": {
906
+ "type": "boolean",
907
+ "description": "When true, fires if cooldown is NOT active"
818
908
  }
819
909
  },
820
- "additionalProperties": false
910
+ "required": [
911
+ "type",
912
+ "key"
913
+ ],
914
+ "additionalProperties": false,
915
+ "description": "Checks if a cooldown timer is currently active."
821
916
  },
822
- "content": {
917
+ {
823
918
  "type": "object",
824
919
  "properties": {
825
- "background": {
826
- "type": "string"
920
+ "type": {
921
+ "type": "string",
922
+ "const": "frequency_limit"
827
923
  },
828
- "backgroundHover": {
829
- "type": "string"
924
+ "key": {
925
+ "type": "string",
926
+ "description": "Frequency counter key"
830
927
  },
831
- "border": {
832
- "type": "string"
928
+ "limit": {
929
+ "type": "number",
930
+ "description": "Maximum allowed count"
833
931
  },
834
- "borderRadius": {
835
- "type": "string"
932
+ "inverted": {
933
+ "type": "boolean",
934
+ "description": "When true, fires if limit NOT reached"
935
+ }
936
+ },
937
+ "required": [
938
+ "type",
939
+ "key",
940
+ "limit"
941
+ ],
942
+ "additionalProperties": false,
943
+ "description": "Checks if a frequency limit has been reached."
944
+ },
945
+ {
946
+ "type": "object",
947
+ "properties": {
948
+ "type": {
949
+ "type": "string",
950
+ "const": "event_count"
836
951
  },
837
- "textColor": {
838
- "type": "string"
952
+ "key": {
953
+ "type": "string",
954
+ "description": "Unique key for this counter (used for accumulator registration)"
839
955
  },
840
- "textSecondaryColor": {
841
- "type": "string"
956
+ "operator": {
957
+ "type": "string",
958
+ "enum": [
959
+ "gte",
960
+ "lte",
961
+ "eq",
962
+ "gt",
963
+ "lt"
964
+ ]
842
965
  },
843
- "itemDivider": {
844
- "type": "string"
966
+ "count": {
967
+ "type": "integer",
968
+ "minimum": 0,
969
+ "description": "Target count threshold"
845
970
  },
846
- "itemGap": {
847
- "type": "string"
971
+ "withinMs": {
972
+ "type": "number",
973
+ "exclusiveMinimum": 0,
974
+ "description": "Time window in ms. Omit = count across entire session."
848
975
  },
849
- "itemPadding": {
850
- "type": "string"
851
- },
852
- "itemFontSize": {
853
- "type": "string"
854
- },
855
- "bodyPadding": {
856
- "type": "string"
857
- },
858
- "bodyFontSize": {
859
- "type": "string"
860
- },
861
- "categoryPadding": {
862
- "type": "string"
863
- },
864
- "categoryGap": {
865
- "type": "string"
866
- },
867
- "categoryFontSize": {
868
- "type": "string"
869
- },
870
- "searchBackground": {
871
- "type": "string"
872
- },
873
- "searchColor": {
874
- "type": "string"
875
- },
876
- "chevronColor": {
877
- "type": "string"
878
- }
879
- },
880
- "additionalProperties": false
881
- },
882
- "mobile": {
883
- "type": "object",
884
- "properties": {
885
- "canvas": {
886
- "type": "object",
887
- "properties": {
888
- "position": {
889
- "type": "string",
890
- "enum": [
891
- "left",
892
- "right"
893
- ]
894
- },
895
- "layout": {
896
- "type": "string",
897
- "enum": [
898
- "overlay",
899
- "push"
900
- ]
901
- },
902
- "background": {
903
- "type": "string"
904
- },
905
- "blur": {
906
- "type": "string"
907
- },
908
- "border": {
909
- "type": "string"
910
- },
911
- "width": {
912
- "type": "string"
913
- },
914
- "padding": {
915
- "type": "string"
916
- },
917
- "transitionDuration": {
918
- "type": "string"
919
- },
920
- "transitionEasing": {
921
- "type": "string"
922
- },
923
- "transitionFade": {
924
- "type": "string"
925
- },
926
- "backdrop": {
927
- "type": "boolean"
928
- },
929
- "backdropColor": {
930
- "type": "string"
931
- }
932
- },
933
- "additionalProperties": false
976
+ "counter": {
977
+ "$ref": "#/$defs/counterDef"
934
978
  }
935
979
  },
936
- "additionalProperties": false
937
- }
938
- },
939
- "additionalProperties": false
940
- },
941
- "launcherConfig": {
942
- "type": "object",
943
- "properties": {
944
- "enabled": {
945
- "type": "boolean"
946
- },
947
- "label": {
948
- "type": "string"
949
- },
950
- "position": {
951
- "type": "string"
952
- },
953
- "animate": {
954
- "type": "boolean"
955
- },
956
- "animationStyle": {
957
- "type": "string",
958
- "enum": [
959
- "pulse",
960
- "bounce",
961
- "glow"
962
- ]
963
- },
964
- "notificationCount": {
965
- "type": "number"
980
+ "required": [
981
+ "type",
982
+ "key",
983
+ "operator",
984
+ "count"
985
+ ],
986
+ "additionalProperties": false,
987
+ "description": "Fires when accumulated event count crosses a threshold. Most powerful trigger type. Define a counter to specify which events to count and optional property filters."
966
988
  }
967
- },
968
- "additionalProperties": false
989
+ ]
969
990
  },
970
- "matchOp": {
971
- "type": "object",
972
- "properties": {
973
- "equals": {
974
- "type": [
975
- "string",
976
- "number",
977
- "boolean"
978
- ]
979
- },
980
- "contains": {
981
- "type": "string"
982
- }
983
- },
984
- "additionalProperties": false,
985
- "description": "Match operator for counter filters. Exactly one of equals or contains must be specified.",
991
+ "decisionStrategy": {
986
992
  "oneOf": [
987
993
  {
988
- "required": [
989
- "equals"
990
- ]
994
+ "$ref": "#/$defs/ruleStrategy"
991
995
  },
992
996
  {
993
- "required": [
994
- "contains"
995
- ]
996
- }
997
- ]
998
- },
999
- "counterDef": {
1000
- "type": "object",
1001
- "properties": {
1002
- "events": {
1003
- "type": "array",
1004
- "items": {
1005
- "type": "string",
1006
- "enum": [
1007
- "ui.click",
1008
- "ui.scroll",
1009
- "ui.input",
1010
- "ui.change",
1011
- "ui.submit",
1012
- "ui.hover",
1013
- "ui.idle",
1014
- "ui.scroll_thrash",
1015
- "ui.focus_bounce",
1016
- "nav.page_view",
1017
- "nav.page_leave",
1018
- "behavior.rage_click",
1019
- "behavior.hesitation",
1020
- "behavior.confusion"
1021
- ],
1022
- "description": "Event name to count. ui.* = user interactions and behavioral detectors, nav.* = page navigation, behavior.* = derived behavioral signals."
1023
- },
1024
- "minItems": 1,
1025
- "description": "Event names to count. Use values from the countable events enum."
1026
- },
1027
- "match": {
1028
- "type": "object",
1029
- "additionalProperties": {
1030
- "$ref": "#/$defs/matchOp"
1031
- },
1032
- "description": "Property filters. Keys are event prop names or element-chain fields (tag_name, $el_text, attr__*). All entries AND together."
1033
- }
1034
- },
1035
- "required": [
1036
- "events"
1037
- ],
1038
- "additionalProperties": false,
1039
- "description": "Defines what events to count. Registered as an accumulator predicate at config-load time."
1040
- },
1041
- "activationConfig": {
1042
- "type": "object",
1043
- "properties": {
1044
- "routes": {
1045
- "$ref": "#/$defs/routeFilter"
1046
- },
1047
- "onlyIfPopulated": {
1048
- "type": "boolean",
1049
- "description": "When true, tile is hidden if none of its props.actions[] have an active triggerWhen. Useful for tiles with all-deferred FAQ questions or nav tips. Default: false."
1050
- }
1051
- },
1052
- "required": [
1053
- "routes"
1054
- ],
1055
- "additionalProperties": false
1056
- },
1057
- "routeFilter": {
1058
- "type": "object",
1059
- "properties": {
1060
- "include": {
1061
- "type": "array",
1062
- "items": {
1063
- "type": "string"
1064
- },
1065
- "description": "URL patterns where this tile appears. Supports *, **, :param wildcards. Use sparingly — mostly to isolate to specific webapp functionality. Omit to appear on all routes."
1066
- },
1067
- "exclude": {
1068
- "type": "array",
1069
- "items": {
1070
- "type": "string"
1071
- },
1072
- "description": "URL patterns where this tile should NOT appear. Evaluated before include."
1073
- }
1074
- },
1075
- "additionalProperties": false
1076
- },
1077
- "ruleStrategy": {
1078
- "type": "object",
1079
- "properties": {
1080
- "type": {
1081
- "type": "string",
1082
- "const": "rules"
997
+ "$ref": "#/$defs/scoreStrategy"
1083
998
  },
1084
- "rules": {
1085
- "type": "array",
1086
- "items": {
1087
- "$ref": "#/$defs/rule"
1088
- },
1089
- "description": "Ordered list of rules. Evaluated top-to-bottom — first match wins."
999
+ {
1000
+ "$ref": "#/$defs/modelStrategy"
1090
1001
  },
1091
- "default": {
1092
- "description": "Fallback value when no rule matches. For triggerWhen: false = do not fire by default."
1002
+ {
1003
+ "$ref": "#/$defs/externalStrategy"
1093
1004
  }
1094
- },
1095
- "required": [
1096
- "type",
1097
- "rules",
1098
- "default"
1099
- ],
1100
- "additionalProperties": false,
1101
- "description": "Rule-based strategy. Evaluates rules top-to-bottom. First rule where ALL conditions match returns its value. If no rule matches, returns default. For triggerWhen: set value=true on matching rules, default=false."
1102
- },
1103
- "scoreStrategy": {
1104
- "type": "object",
1105
- "properties": {
1106
- "type": {
1107
- "type": "string",
1108
- "const": "score"
1109
- },
1110
- "field": {
1111
- "type": "string"
1112
- },
1113
- "threshold": {
1114
- "type": "number"
1115
- },
1116
- "above": {},
1117
- "below": {}
1118
- },
1119
- "required": [
1120
- "type",
1121
- "field",
1122
- "threshold",
1123
- "above",
1124
- "below"
1125
- ],
1126
- "additionalProperties": false,
1127
- "description": "Score-based strategy. Compares a field value against a threshold."
1005
+ ]
1128
1006
  },
1129
- "modelStrategy": {
1007
+ "anchorId": {
1130
1008
  "type": "object",
1009
+ "description": "DOM element target. selector = CSS selector, route = URL path(s) where the element exists.",
1131
1010
  "properties": {
1132
- "type": {
1011
+ "selector": {
1133
1012
  "type": "string",
1134
- "const": "model"
1135
- },
1136
- "modelId": {
1137
- "type": "string"
1138
- },
1139
- "inputs": {
1140
- "type": "array",
1141
- "items": {
1142
- "type": "string"
1143
- }
1144
- },
1145
- "outputMapping": {
1146
- "type": "object",
1147
- "additionalProperties": {}
1013
+ "description": "CSS selector for the target element"
1148
1014
  },
1149
- "default": {}
1015
+ "route": {
1016
+ "anyOf": [
1017
+ {
1018
+ "type": "string"
1019
+ },
1020
+ {
1021
+ "type": "array",
1022
+ "items": {
1023
+ "type": "string"
1024
+ }
1025
+ }
1026
+ ],
1027
+ "description": "URL path(s) where this element exists"
1028
+ }
1150
1029
  },
1151
1030
  "required": [
1152
- "type",
1153
- "modelId",
1154
- "inputs",
1155
- "outputMapping",
1156
- "default"
1031
+ "selector",
1032
+ "route"
1157
1033
  ],
1158
- "additionalProperties": false,
1159
- "description": "ML model strategy. Sends inputs to a model and maps outputs."
1034
+ "additionalProperties": false
1160
1035
  },
1161
- "externalStrategy": {
1036
+ "notify": {
1162
1037
  "type": "object",
1038
+ "description": "Optional toast notification shown when this action triggers.",
1163
1039
  "properties": {
1164
- "type": {
1040
+ "title": {
1165
1041
  "type": "string",
1166
- "const": "external"
1167
- },
1168
- "endpoint": {
1169
- "type": "string"
1042
+ "description": "Notification title"
1170
1043
  },
1171
- "method": {
1044
+ "body": {
1172
1045
  "type": "string",
1173
- "enum": [
1174
- "GET",
1175
- "POST"
1176
- ]
1046
+ "description": "Notification body text"
1177
1047
  },
1178
- "default": {},
1179
- "timeoutMs": {
1180
- "type": "number"
1048
+ "icon": {
1049
+ "type": "string",
1050
+ "description": "Notification icon (emoji or URL)"
1181
1051
  }
1182
1052
  },
1183
- "required": [
1184
- "type",
1185
- "endpoint",
1186
- "default"
1187
- ],
1188
- "additionalProperties": false,
1189
- "description": "External API strategy. Calls an endpoint to determine the value."
1053
+ "additionalProperties": false
1190
1054
  },
1191
- "rule": {
1055
+ "tile": {
1192
1056
  "type": "object",
1193
1057
  "properties": {
1194
- "conditions": {
1058
+ "id": {
1059
+ "type": "string"
1060
+ },
1061
+ "title": {
1062
+ "type": "string"
1063
+ },
1064
+ "subtitle": {
1065
+ "type": "string"
1066
+ },
1067
+ "icon": {
1068
+ "type": "string"
1069
+ },
1070
+ "priority": {
1071
+ "type": "number"
1072
+ },
1073
+ "widget": {
1074
+ "type": "string",
1075
+ "enum": [
1076
+ "adaptive-faq:accordion",
1077
+ "adaptive-nav:tips",
1078
+ "adaptive-overlays:workflow-tracker",
1079
+ "adaptive-viz:chart"
1080
+ ],
1081
+ "description": "Registered widget ID. Must match an adaptive widget registration."
1082
+ },
1083
+ "props": {
1084
+ "type": "object",
1085
+ "additionalProperties": {}
1086
+ },
1087
+ "activation": {
1088
+ "$ref": "#/$defs/activationConfig",
1089
+ "description": "Route and condition-based activation for this tile"
1090
+ },
1091
+ "notifications": {
1195
1092
  "type": "array",
1196
1093
  "items": {
1197
- "$ref": "#/$defs/condition"
1198
- },
1199
- "description": "Array of conditions — ALL must match (AND logic) for this rule to fire."
1200
- },
1201
- "value": {
1202
- "description": "Value returned when all conditions match. For triggerWhen: true = fire the action."
1094
+ "type": "object",
1095
+ "properties": {
1096
+ "on": {
1097
+ "type": "string"
1098
+ },
1099
+ "title": {
1100
+ "type": "string"
1101
+ },
1102
+ "body": {
1103
+ "type": "string"
1104
+ },
1105
+ "icon": {
1106
+ "type": "string"
1107
+ },
1108
+ "ttl": {
1109
+ "type": "number"
1110
+ },
1111
+ "cooldown": {
1112
+ "type": "number"
1113
+ },
1114
+ "deepLink": {
1115
+ "type": "object",
1116
+ "properties": {
1117
+ "tileId": {
1118
+ "type": "string"
1119
+ },
1120
+ "itemId": {
1121
+ "type": "string"
1122
+ }
1123
+ },
1124
+ "required": [
1125
+ "tileId"
1126
+ ],
1127
+ "additionalProperties": false
1128
+ }
1129
+ },
1130
+ "required": [
1131
+ "on",
1132
+ "title"
1133
+ ],
1134
+ "additionalProperties": false
1135
+ }
1203
1136
  }
1204
1137
  },
1205
1138
  "required": [
1206
- "conditions",
1207
- "value"
1139
+ "id",
1140
+ "widget"
1208
1141
  ],
1209
1142
  "additionalProperties": false,
1210
- "description": "A single rule. ALL conditions must match (AND logic). Rules in a strategy are evaluated top-to-bottom — first rule where all conditions match wins and returns its value."
1211
- },
1212
- "condition": {
1213
- "oneOf": [
1143
+ "allOf": [
1214
1144
  {
1215
- "type": "object",
1216
- "properties": {
1217
- "type": {
1218
- "type": "string",
1219
- "const": "page_url"
1145
+ "if": {
1146
+ "properties": {
1147
+ "widget": {
1148
+ "const": "adaptive-faq:accordion"
1149
+ }
1220
1150
  },
1221
- "url": {
1222
- "type": "string",
1223
- "description": "URL path to match (e.g. \"/pricing\", \"/dashboard\")"
1224
- }
1151
+ "required": [
1152
+ "widget"
1153
+ ]
1225
1154
  },
1226
- "required": [
1227
- "type",
1228
- "url"
1229
- ],
1230
- "additionalProperties": false,
1231
- "description": "Fires when the current page URL matches."
1155
+ "then": {
1156
+ "properties": {
1157
+ "props": {
1158
+ "$ref": "#/$defs/faqAccordionProps"
1159
+ }
1160
+ }
1161
+ }
1232
1162
  },
1233
1163
  {
1234
- "type": "object",
1235
- "properties": {
1236
- "type": {
1237
- "type": "string",
1238
- "const": "route"
1164
+ "if": {
1165
+ "properties": {
1166
+ "widget": {
1167
+ "const": "adaptive-nav:tips"
1168
+ }
1239
1169
  },
1240
- "routeId": {
1241
- "type": "string",
1242
- "description": "Named route ID from the route filter"
1170
+ "required": [
1171
+ "widget"
1172
+ ]
1173
+ },
1174
+ "then": {
1175
+ "properties": {
1176
+ "props": {
1177
+ "$ref": "#/$defs/navTipsProps"
1178
+ }
1243
1179
  }
1180
+ }
1181
+ },
1182
+ {
1183
+ "if": {
1184
+ "properties": {
1185
+ "widget": {
1186
+ "const": "adaptive-overlays:workflow-tracker"
1187
+ }
1188
+ },
1189
+ "required": [
1190
+ "widget"
1191
+ ]
1244
1192
  },
1245
- "required": [
1246
- "type",
1247
- "routeId"
1248
- ],
1249
- "additionalProperties": false,
1250
- "description": "Fires when the current route matches a named route ID."
1193
+ "then": {
1194
+ "properties": {
1195
+ "props": {
1196
+ "$ref": "#/$defs/workflowTrackerProps"
1197
+ }
1198
+ }
1199
+ }
1251
1200
  },
1252
1201
  {
1202
+ "if": {
1203
+ "properties": {
1204
+ "widget": {
1205
+ "const": "adaptive-viz:chart"
1206
+ }
1207
+ },
1208
+ "required": [
1209
+ "widget"
1210
+ ]
1211
+ },
1212
+ "then": {
1213
+ "properties": {
1214
+ "props": {
1215
+ "$ref": "#/$defs/vizChartProps"
1216
+ }
1217
+ }
1218
+ }
1219
+ }
1220
+ ]
1221
+ },
1222
+ "themeConfig": {
1223
+ "type": "object",
1224
+ "properties": {
1225
+ "mode": {
1226
+ "type": "string",
1227
+ "enum": [
1228
+ "dark",
1229
+ "light"
1230
+ ]
1231
+ },
1232
+ "fontFamily": {
1233
+ "type": "string"
1234
+ },
1235
+ "colorPrimary": {
1236
+ "type": "string"
1237
+ },
1238
+ "colorPrimaryHover": {
1239
+ "type": "string"
1240
+ },
1241
+ "borderRadius": {
1242
+ "type": "string"
1243
+ },
1244
+ "canvas": {
1253
1245
  "type": "object",
1254
1246
  "properties": {
1255
- "type": {
1256
- "type": "string",
1257
- "const": "anchor_visible"
1258
- },
1259
- "anchorId": {
1247
+ "position": {
1260
1248
  "type": "string",
1261
- "description": "CSS selector of the anchor element"
1249
+ "enum": [
1250
+ "left",
1251
+ "right"
1252
+ ]
1262
1253
  },
1263
- "state": {
1254
+ "layout": {
1264
1255
  "type": "string",
1265
1256
  "enum": [
1266
- "visible",
1267
- "present",
1268
- "absent"
1269
- ],
1270
- "description": "\"visible\" = in viewport, \"present\" = in DOM, \"absent\" = not in DOM"
1271
- }
1272
- },
1273
- "required": [
1274
- "type",
1275
- "anchorId",
1276
- "state"
1277
- ],
1278
- "additionalProperties": false,
1279
- "description": "Fires based on a DOM element visibility state (visible/present/absent in viewport or DOM)."
1280
- },
1281
- {
1282
- "type": "object",
1283
- "properties": {
1284
- "type": {
1285
- "type": "string",
1286
- "const": "event_occurred"
1257
+ "overlay",
1258
+ "push"
1259
+ ]
1287
1260
  },
1288
- "eventName": {
1289
- "type": "string",
1290
- "description": "Event name (e.g. \"ui.click\", \"$pageview\")"
1261
+ "background": {
1262
+ "type": "string"
1291
1263
  },
1292
- "withinMs": {
1293
- "type": "number",
1294
- "description": "Time window in ms. Omit = any time this session."
1295
- }
1296
- },
1297
- "required": [
1298
- "type",
1299
- "eventName"
1300
- ],
1301
- "additionalProperties": false,
1302
- "description": "Fires when a specific event has occurred during this session."
1303
- },
1304
- {
1305
- "type": "object",
1306
- "properties": {
1307
- "type": {
1308
- "type": "string",
1309
- "const": "state_equals"
1264
+ "blur": {
1265
+ "type": "string"
1310
1266
  },
1311
- "key": {
1312
- "type": "string",
1313
- "description": "Key in the SDK persistent state store. Only valid for keys the host app explicitly sets via syntro.state.set()."
1267
+ "border": {
1268
+ "type": "string"
1314
1269
  },
1315
- "value": {
1316
- "description": "Expected value to match against"
1317
- }
1318
- },
1319
- "required": [
1320
- "type",
1321
- "key"
1322
- ],
1323
- "additionalProperties": false,
1324
- "description": "Checks the SDK persistent state store (localStorage). ONLY for host-app state set via syntro.state.set() — NOT for user attributes like region, device, or UTM params (those are handled by segment targeting). Do NOT use this for targeting."
1325
- },
1326
- {
1327
- "type": "object",
1328
- "properties": {
1329
- "type": {
1330
- "type": "string",
1331
- "const": "viewport"
1270
+ "width": {
1271
+ "type": "string"
1332
1272
  },
1333
- "minWidth": {
1334
- "type": "number",
1335
- "description": "Minimum viewport width in pixels"
1273
+ "padding": {
1274
+ "type": "string"
1336
1275
  },
1337
- "maxWidth": {
1338
- "type": "number",
1339
- "description": "Maximum viewport width in pixels"
1276
+ "transitionDuration": {
1277
+ "type": "string"
1340
1278
  },
1341
- "minHeight": {
1342
- "type": "number",
1343
- "description": "Minimum viewport height in pixels"
1279
+ "transitionEasing": {
1280
+ "type": "string"
1344
1281
  },
1345
- "maxHeight": {
1346
- "type": "number",
1347
- "description": "Maximum viewport height in pixels"
1282
+ "transitionFade": {
1283
+ "type": "string"
1284
+ },
1285
+ "backdrop": {
1286
+ "type": "boolean"
1287
+ },
1288
+ "backdropColor": {
1289
+ "type": "string"
1348
1290
  }
1349
1291
  },
1350
- "required": [
1351
- "type"
1352
- ],
1353
- "additionalProperties": false,
1354
- "description": "Fires based on viewport (screen) size. Use for responsive behavior (e.g. mobile vs desktop)."
1292
+ "additionalProperties": false
1355
1293
  },
1356
- {
1294
+ "launcher": {
1357
1295
  "type": "object",
1358
1296
  "properties": {
1359
- "type": {
1360
- "type": "string",
1361
- "const": "session_metric"
1297
+ "background": {
1298
+ "type": "string"
1362
1299
  },
1363
- "key": {
1364
- "type": "string",
1365
- "enum": [
1366
- "time_on_page",
1367
- "page_views",
1368
- "scroll_depth"
1369
- ],
1370
- "description": "Session metric key: \"time_on_page\", \"page_views\", or \"scroll_depth\""
1300
+ "backgroundHover": {
1301
+ "type": "string"
1371
1302
  },
1372
- "operator": {
1373
- "type": "string",
1374
- "enum": [
1375
- "gte",
1376
- "lte",
1377
- "eq",
1378
- "gt",
1379
- "lt"
1380
- ]
1303
+ "color": {
1304
+ "type": "string"
1381
1305
  },
1382
- "threshold": {
1383
- "type": "number",
1384
- "description": "Numeric threshold to compare against"
1306
+ "size": {
1307
+ "type": "string"
1308
+ },
1309
+ "shadow": {
1310
+ "type": "string"
1311
+ },
1312
+ "borderRadius": {
1313
+ "type": "string"
1314
+ },
1315
+ "icon": {
1316
+ "type": "string"
1385
1317
  }
1386
1318
  },
1387
- "required": [
1388
- "type",
1389
- "key",
1390
- "operator",
1391
- "threshold"
1392
- ],
1393
- "additionalProperties": false,
1394
- "description": "Fires when a session metric crosses a threshold. Valid keys: \"time_on_page\" (seconds), \"page_views\" (count), \"scroll_depth\" (0-100)."
1319
+ "additionalProperties": false
1395
1320
  },
1396
- {
1321
+ "tile": {
1397
1322
  "type": "object",
1398
1323
  "properties": {
1399
- "type": {
1400
- "type": "string",
1401
- "const": "dismissed"
1324
+ "background": {
1325
+ "type": "string"
1402
1326
  },
1403
- "key": {
1404
- "type": "string",
1405
- "description": "Dismissal key (usually a tile or action ID)"
1327
+ "backgroundHover": {
1328
+ "type": "string"
1406
1329
  },
1407
- "inverted": {
1408
- "type": "boolean",
1409
- "description": "When true, fires if NOT dismissed (default behavior)"
1330
+ "border": {
1331
+ "type": "string"
1332
+ },
1333
+ "borderRadius": {
1334
+ "type": "string"
1335
+ },
1336
+ "shadow": {
1337
+ "type": "string"
1338
+ },
1339
+ "titleColor": {
1340
+ "type": "string"
1341
+ },
1342
+ "textColor": {
1343
+ "type": "string"
1344
+ },
1345
+ "iconBackground": {
1346
+ "type": "string"
1347
+ },
1348
+ "iconShadow": {
1349
+ "type": "string"
1350
+ },
1351
+ "headerPadding": {
1352
+ "type": "string"
1353
+ },
1354
+ "bodyPadding": {
1355
+ "type": "string"
1356
+ },
1357
+ "gap": {
1358
+ "type": "string"
1410
1359
  }
1411
1360
  },
1412
- "required": [
1413
- "type",
1414
- "key"
1415
- ],
1416
- "additionalProperties": false,
1417
- "description": "Checks if an item has been dismissed by the user."
1361
+ "additionalProperties": false
1418
1362
  },
1419
- {
1363
+ "overlay": {
1420
1364
  "type": "object",
1421
1365
  "properties": {
1422
- "type": {
1423
- "type": "string",
1424
- "const": "cooldown_active"
1366
+ "background": {
1367
+ "type": "string"
1425
1368
  },
1426
- "key": {
1427
- "type": "string",
1428
- "description": "Cooldown key"
1369
+ "textColor": {
1370
+ "type": "string"
1429
1371
  },
1430
- "inverted": {
1431
- "type": "boolean",
1432
- "description": "When true, fires if cooldown is NOT active"
1372
+ "titleColor": {
1373
+ "type": "string"
1374
+ },
1375
+ "arrowColor": {
1376
+ "type": "string"
1377
+ },
1378
+ "arrowSize": {
1379
+ "type": "string"
1380
+ },
1381
+ "border": {
1382
+ "type": "string"
1383
+ },
1384
+ "borderRadius": {
1385
+ "type": "string"
1386
+ },
1387
+ "scrimOpacity": {
1388
+ "type": "string"
1389
+ },
1390
+ "highlightRing": {
1391
+ "type": "string"
1433
1392
  }
1434
1393
  },
1435
- "required": [
1436
- "type",
1437
- "key"
1438
- ],
1439
- "additionalProperties": false,
1440
- "description": "Checks if a cooldown timer is currently active."
1394
+ "additionalProperties": false
1441
1395
  },
1442
- {
1396
+ "notification": {
1443
1397
  "type": "object",
1444
1398
  "properties": {
1445
- "type": {
1446
- "type": "string",
1447
- "const": "frequency_limit"
1399
+ "background": {
1400
+ "type": "string"
1448
1401
  },
1449
- "key": {
1450
- "type": "string",
1451
- "description": "Frequency counter key"
1402
+ "textColor": {
1403
+ "type": "string"
1452
1404
  },
1453
- "limit": {
1454
- "type": "number",
1455
- "description": "Maximum allowed count"
1405
+ "textSecondaryColor": {
1406
+ "type": "string"
1456
1407
  },
1457
- "inverted": {
1458
- "type": "boolean",
1459
- "description": "When true, fires if limit NOT reached"
1408
+ "border": {
1409
+ "type": "string"
1410
+ },
1411
+ "borderRadius": {
1412
+ "type": "string"
1413
+ },
1414
+ "successColor": {
1415
+ "type": "string"
1416
+ },
1417
+ "warningColor": {
1418
+ "type": "string"
1419
+ },
1420
+ "errorColor": {
1421
+ "type": "string"
1422
+ },
1423
+ "iconBackground": {
1424
+ "type": "string"
1425
+ },
1426
+ "progressGradient": {
1427
+ "type": "string"
1460
1428
  }
1461
1429
  },
1462
- "required": [
1463
- "type",
1464
- "key",
1465
- "limit"
1466
- ],
1467
- "additionalProperties": false,
1468
- "description": "Checks if a frequency limit has been reached."
1430
+ "additionalProperties": false
1469
1431
  },
1470
- {
1432
+ "content": {
1471
1433
  "type": "object",
1472
1434
  "properties": {
1473
- "type": {
1474
- "type": "string",
1475
- "const": "event_count"
1435
+ "background": {
1436
+ "type": "string"
1476
1437
  },
1477
- "key": {
1478
- "type": "string",
1479
- "description": "Unique key for this counter (used for accumulator registration)"
1438
+ "backgroundHover": {
1439
+ "type": "string"
1480
1440
  },
1481
- "operator": {
1482
- "type": "string",
1483
- "enum": [
1484
- "gte",
1485
- "lte",
1486
- "eq",
1487
- "gt",
1488
- "lt"
1489
- ]
1441
+ "border": {
1442
+ "type": "string"
1490
1443
  },
1491
- "count": {
1492
- "type": "integer",
1493
- "minimum": 0,
1494
- "description": "Target count threshold"
1444
+ "borderRadius": {
1445
+ "type": "string"
1495
1446
  },
1496
- "withinMs": {
1497
- "type": "number",
1498
- "exclusiveMinimum": 0,
1499
- "description": "Time window in ms. Omit = count across entire session."
1447
+ "textColor": {
1448
+ "type": "string"
1500
1449
  },
1501
- "counter": {
1502
- "$ref": "#/$defs/counterDef"
1450
+ "textSecondaryColor": {
1451
+ "type": "string"
1452
+ },
1453
+ "itemDivider": {
1454
+ "type": "string"
1455
+ },
1456
+ "itemGap": {
1457
+ "type": "string"
1458
+ },
1459
+ "itemPadding": {
1460
+ "type": "string"
1461
+ },
1462
+ "itemFontSize": {
1463
+ "type": "string"
1464
+ },
1465
+ "bodyPadding": {
1466
+ "type": "string"
1467
+ },
1468
+ "bodyFontSize": {
1469
+ "type": "string"
1470
+ },
1471
+ "categoryPadding": {
1472
+ "type": "string"
1473
+ },
1474
+ "categoryGap": {
1475
+ "type": "string"
1476
+ },
1477
+ "categoryFontSize": {
1478
+ "type": "string"
1479
+ },
1480
+ "searchBackground": {
1481
+ "type": "string"
1482
+ },
1483
+ "searchColor": {
1484
+ "type": "string"
1485
+ },
1486
+ "chevronColor": {
1487
+ "type": "string"
1503
1488
  }
1504
1489
  },
1505
- "required": [
1506
- "type",
1507
- "key",
1508
- "operator",
1509
- "count"
1510
- ],
1511
- "additionalProperties": false,
1512
- "description": "Fires when accumulated event count crosses a threshold. Most powerful trigger type. Define a counter to specify which events to count and optional property filters."
1513
- }
1514
- ]
1515
- },
1516
- "decisionStrategy": {
1517
- "oneOf": [
1518
- {
1519
- "$ref": "#/$defs/ruleStrategy"
1520
- },
1521
- {
1522
- "$ref": "#/$defs/scoreStrategy"
1523
- },
1524
- {
1525
- "$ref": "#/$defs/modelStrategy"
1526
- },
1527
- {
1528
- "$ref": "#/$defs/externalStrategy"
1529
- }
1530
- ]
1531
- },
1532
- "anchorId": {
1533
- "type": "object",
1534
- "description": "DOM element target. selector = CSS selector, route = URL path(s) where the element exists.",
1535
- "properties": {
1536
- "selector": {
1537
- "type": "string",
1538
- "description": "CSS selector for the target element"
1490
+ "additionalProperties": false
1539
1491
  },
1540
- "route": {
1541
- "anyOf": [
1542
- {
1543
- "type": "string"
1544
- },
1545
- {
1546
- "type": "array",
1547
- "items": {
1548
- "type": "string"
1549
- }
1492
+ "mobile": {
1493
+ "type": "object",
1494
+ "properties": {
1495
+ "canvas": {
1496
+ "type": "object",
1497
+ "properties": {
1498
+ "position": {
1499
+ "type": "string",
1500
+ "enum": [
1501
+ "left",
1502
+ "right"
1503
+ ]
1504
+ },
1505
+ "layout": {
1506
+ "type": "string",
1507
+ "enum": [
1508
+ "overlay",
1509
+ "push"
1510
+ ]
1511
+ },
1512
+ "background": {
1513
+ "type": "string"
1514
+ },
1515
+ "blur": {
1516
+ "type": "string"
1517
+ },
1518
+ "border": {
1519
+ "type": "string"
1520
+ },
1521
+ "width": {
1522
+ "type": "string"
1523
+ },
1524
+ "padding": {
1525
+ "type": "string"
1526
+ },
1527
+ "transitionDuration": {
1528
+ "type": "string"
1529
+ },
1530
+ "transitionEasing": {
1531
+ "type": "string"
1532
+ },
1533
+ "transitionFade": {
1534
+ "type": "string"
1535
+ },
1536
+ "backdrop": {
1537
+ "type": "boolean"
1538
+ },
1539
+ "backdropColor": {
1540
+ "type": "string"
1541
+ }
1542
+ },
1543
+ "additionalProperties": false
1550
1544
  }
1551
- ],
1552
- "description": "URL path(s) where this element exists"
1545
+ },
1546
+ "additionalProperties": false
1553
1547
  }
1554
1548
  },
1555
- "required": [
1556
- "selector",
1557
- "route"
1558
- ],
1559
1549
  "additionalProperties": false
1560
1550
  },
1561
- "notify": {
1551
+ "launcherConfig": {
1562
1552
  "type": "object",
1563
- "description": "Optional toast notification shown when this action triggers.",
1564
1553
  "properties": {
1565
- "title": {
1566
- "type": "string",
1567
- "description": "Notification title"
1554
+ "enabled": {
1555
+ "type": "boolean"
1568
1556
  },
1569
- "body": {
1570
- "type": "string",
1571
- "description": "Notification body text"
1557
+ "label": {
1558
+ "type": "string"
1572
1559
  },
1573
- "icon": {
1560
+ "position": {
1561
+ "type": "string"
1562
+ },
1563
+ "animate": {
1564
+ "type": "boolean"
1565
+ },
1566
+ "animationStyle": {
1574
1567
  "type": "string",
1575
- "description": "Notification icon (emoji or URL)"
1568
+ "enum": [
1569
+ "pulse",
1570
+ "bounce",
1571
+ "glow"
1572
+ ]
1573
+ },
1574
+ "notificationCount": {
1575
+ "type": "number"
1576
1576
  }
1577
1577
  },
1578
1578
  "additionalProperties": false
@@ -3403,488 +3403,7 @@
3403
3403
  "triggerWhen": {
3404
3404
  "anyOf": [
3405
3405
  {
3406
- "anyOf": [
3407
- {
3408
- "type": "object",
3409
- "properties": {
3410
- "type": {
3411
- "type": "string",
3412
- "const": "rules"
3413
- },
3414
- "rules": {
3415
- "type": "array",
3416
- "items": {
3417
- "type": "object",
3418
- "properties": {
3419
- "conditions": {
3420
- "type": "array",
3421
- "items": {
3422
- "anyOf": [
3423
- {
3424
- "type": "object",
3425
- "properties": {
3426
- "type": {
3427
- "type": "string",
3428
- "const": "page_url"
3429
- },
3430
- "url": {
3431
- "type": "string",
3432
- "description": "URL path to match (e.g. \"/pricing\", \"/dashboard\")"
3433
- }
3434
- },
3435
- "required": [
3436
- "type",
3437
- "url"
3438
- ],
3439
- "additionalProperties": false,
3440
- "description": "Fires when the current page URL matches. Use for page-specific actions. Example: {\"type\": \"page_url\", \"url\": \"/pricing\"}"
3441
- },
3442
- {
3443
- "type": "object",
3444
- "properties": {
3445
- "type": {
3446
- "type": "string",
3447
- "const": "route"
3448
- },
3449
- "routeId": {
3450
- "type": "string",
3451
- "description": "Named route ID from the route filter"
3452
- }
3453
- },
3454
- "required": [
3455
- "type",
3456
- "routeId"
3457
- ],
3458
- "additionalProperties": false,
3459
- "description": "Fires when the current route matches a named route ID."
3460
- },
3461
- {
3462
- "type": "object",
3463
- "properties": {
3464
- "type": {
3465
- "type": "string",
3466
- "const": "anchor_visible"
3467
- },
3468
- "anchorId": {
3469
- "type": "string",
3470
- "description": "CSS selector of the anchor element"
3471
- },
3472
- "state": {
3473
- "type": "string",
3474
- "enum": [
3475
- "visible",
3476
- "present",
3477
- "absent"
3478
- ],
3479
- "description": "\"visible\" = in viewport, \"present\" = in DOM, \"absent\" = not in DOM"
3480
- }
3481
- },
3482
- "required": [
3483
- "type",
3484
- "anchorId",
3485
- "state"
3486
- ],
3487
- "additionalProperties": false,
3488
- "description": "Fires based on a DOM element's visibility state. Example: {\"type\": \"anchor_visible\", \"anchorId\": \"#cta-button\", \"state\": \"visible\"}"
3489
- },
3490
- {
3491
- "type": "object",
3492
- "properties": {
3493
- "type": {
3494
- "type": "string",
3495
- "const": "event_occurred"
3496
- },
3497
- "eventName": {
3498
- "type": "string",
3499
- "description": "Event name (e.g. \"ui.click\", \"$pageview\")"
3500
- },
3501
- "withinMs": {
3502
- "type": "number",
3503
- "description": "Time window in ms. Omit = any time this session."
3504
- }
3505
- },
3506
- "required": [
3507
- "type",
3508
- "eventName"
3509
- ],
3510
- "additionalProperties": false,
3511
- "description": "Fires when a specific event has occurred during this session. Example: {\"type\": \"event_occurred\", \"eventName\": \"ui.click\", \"withinMs\": 5000}"
3512
- },
3513
- {
3514
- "type": "object",
3515
- "properties": {
3516
- "type": {
3517
- "type": "string",
3518
- "const": "state_equals"
3519
- },
3520
- "key": {
3521
- "type": "string",
3522
- "description": "Key in the SDK persistent state store (localStorage). Only valid for keys the host app explicitly sets via syntro.state.set()."
3523
- },
3524
- "value": {
3525
- "description": "Expected value to match against"
3526
- }
3527
- },
3528
- "required": [
3529
- "type",
3530
- "key"
3531
- ],
3532
- "additionalProperties": false,
3533
- "description": "Checks the SDK persistent state store (localStorage). ONLY for host-app state set via syntro.state.set() — NOT for user attributes like region, device, or UTM params (those are handled by segment targeting). Do NOT use this for targeting. If you do not know the valid state keys, do not use this condition type."
3534
- },
3535
- {
3536
- "type": "object",
3537
- "properties": {
3538
- "type": {
3539
- "type": "string",
3540
- "const": "viewport"
3541
- },
3542
- "minWidth": {
3543
- "type": "number",
3544
- "description": "Minimum viewport width in pixels"
3545
- },
3546
- "maxWidth": {
3547
- "type": "number",
3548
- "description": "Maximum viewport width in pixels"
3549
- },
3550
- "minHeight": {
3551
- "type": "number",
3552
- "description": "Minimum viewport height in pixels"
3553
- },
3554
- "maxHeight": {
3555
- "type": "number",
3556
- "description": "Maximum viewport height in pixels"
3557
- }
3558
- },
3559
- "required": [
3560
- "type"
3561
- ],
3562
- "additionalProperties": false,
3563
- "description": "Fires based on viewport (screen) size. Use for responsive behavior. Example: {\"type\": \"viewport\", \"minWidth\": 768} — fires on tablet and larger."
3564
- },
3565
- {
3566
- "type": "object",
3567
- "properties": {
3568
- "type": {
3569
- "type": "string",
3570
- "const": "session_metric"
3571
- },
3572
- "key": {
3573
- "type": "string",
3574
- "enum": [
3575
- "time_on_page",
3576
- "page_views",
3577
- "scroll_depth"
3578
- ],
3579
- "description": "Session metric key. time_on_page = seconds on current page, page_views = pages visited this session, scroll_depth = 0-100 percentage."
3580
- },
3581
- "operator": {
3582
- "type": "string",
3583
- "enum": [
3584
- "gte",
3585
- "lte",
3586
- "eq",
3587
- "gt",
3588
- "lt"
3589
- ]
3590
- },
3591
- "threshold": {
3592
- "type": "number",
3593
- "description": "Numeric threshold to compare against"
3594
- }
3595
- },
3596
- "required": [
3597
- "type",
3598
- "key",
3599
- "operator",
3600
- "threshold"
3601
- ],
3602
- "additionalProperties": false,
3603
- "description": "Fires when a session metric crosses a threshold. Valid keys: \"time_on_page\" (seconds), \"page_views\" (count), \"scroll_depth\" (0-100). Example: {\"type\": \"session_metric\", \"key\": \"time_on_page\", \"operator\": \"gte\", \"threshold\": 30}"
3604
- },
3605
- {
3606
- "type": "object",
3607
- "properties": {
3608
- "type": {
3609
- "type": "string",
3610
- "const": "dismissed"
3611
- },
3612
- "key": {
3613
- "type": "string",
3614
- "description": "Dismissal key (usually a tile or action ID)"
3615
- },
3616
- "inverted": {
3617
- "type": "boolean",
3618
- "description": "When true, fires if NOT dismissed (default behavior)"
3619
- }
3620
- },
3621
- "required": [
3622
- "type",
3623
- "key"
3624
- ],
3625
- "additionalProperties": false,
3626
- "description": "Checks if an item has been dismissed by the user. Use with inverted: true to show only if not dismissed."
3627
- },
3628
- {
3629
- "type": "object",
3630
- "properties": {
3631
- "type": {
3632
- "type": "string",
3633
- "const": "cooldown_active"
3634
- },
3635
- "key": {
3636
- "type": "string",
3637
- "description": "Cooldown key"
3638
- },
3639
- "inverted": {
3640
- "type": "boolean",
3641
- "description": "When true, fires if cooldown is NOT active"
3642
- }
3643
- },
3644
- "required": [
3645
- "type",
3646
- "key"
3647
- ],
3648
- "additionalProperties": false,
3649
- "description": "Checks if a cooldown timer is currently active. Use to prevent showing the same intervention too frequently."
3650
- },
3651
- {
3652
- "type": "object",
3653
- "properties": {
3654
- "type": {
3655
- "type": "string",
3656
- "const": "frequency_limit"
3657
- },
3658
- "key": {
3659
- "type": "string",
3660
- "description": "Frequency counter key"
3661
- },
3662
- "limit": {
3663
- "type": "number",
3664
- "description": "Maximum allowed count"
3665
- },
3666
- "inverted": {
3667
- "type": "boolean",
3668
- "description": "When true, fires if limit NOT reached"
3669
- }
3670
- },
3671
- "required": [
3672
- "type",
3673
- "key",
3674
- "limit"
3675
- ],
3676
- "additionalProperties": false,
3677
- "description": "Checks if a frequency limit has been reached. Use to cap how many times an action fires per session."
3678
- },
3679
- {
3680
- "type": "object",
3681
- "properties": {
3682
- "type": {
3683
- "type": "string",
3684
- "const": "event_count"
3685
- },
3686
- "key": {
3687
- "type": "string",
3688
- "description": "Unique key for this counter (used for accumulator registration)"
3689
- },
3690
- "operator": {
3691
- "type": "string",
3692
- "enum": [
3693
- "gte",
3694
- "lte",
3695
- "eq",
3696
- "gt",
3697
- "lt"
3698
- ]
3699
- },
3700
- "count": {
3701
- "type": "integer",
3702
- "minimum": 0,
3703
- "description": "Target count threshold"
3704
- },
3705
- "withinMs": {
3706
- "type": "number",
3707
- "exclusiveMinimum": 0,
3708
- "description": "Time window in ms. Omit = count across entire session."
3709
- },
3710
- "counter": {
3711
- "type": "object",
3712
- "properties": {
3713
- "events": {
3714
- "type": "array",
3715
- "items": {
3716
- "type": "string",
3717
- "enum": [
3718
- "ui.click",
3719
- "ui.scroll",
3720
- "ui.input",
3721
- "ui.change",
3722
- "ui.submit",
3723
- "ui.hover",
3724
- "ui.idle",
3725
- "ui.scroll_thrash",
3726
- "ui.focus_bounce",
3727
- "nav.page_view",
3728
- "nav.page_leave",
3729
- "behavior.rage_click",
3730
- "behavior.hesitation",
3731
- "behavior.confusion"
3732
- ],
3733
- "description": "Event name to count. ui.* = user interactions and behavioral detectors, nav.* = page navigation, behavior.* = derived behavioral signals."
3734
- },
3735
- "minItems": 1,
3736
- "description": "Event names to count. Use values from the countable events enum."
3737
- },
3738
- "match": {
3739
- "type": "object",
3740
- "additionalProperties": {
3741
- "type": "object",
3742
- "properties": {
3743
- "equals": {
3744
- "type": [
3745
- "string",
3746
- "number",
3747
- "boolean"
3748
- ]
3749
- },
3750
- "contains": {
3751
- "type": "string"
3752
- }
3753
- },
3754
- "additionalProperties": false,
3755
- "description": "Match operator for counter filters. Exactly one of equals or contains must be specified."
3756
- },
3757
- "description": "Property filters. Keys are event prop names or element-chain fields (tag_name, $el_text, attr__*). All entries AND together."
3758
- }
3759
- },
3760
- "required": [
3761
- "events"
3762
- ],
3763
- "additionalProperties": false,
3764
- "description": "Inline counter definition. Defines what events to count."
3765
- }
3766
- },
3767
- "required": [
3768
- "type",
3769
- "key",
3770
- "operator",
3771
- "count"
3772
- ],
3773
- "additionalProperties": false,
3774
- "description": "Fires when accumulated event count crosses a threshold. Most powerful trigger type. Example: {\"type\": \"event_count\", \"key\": \"pricing-clicks\", \"operator\": \"gte\", \"count\": 3, \"counter\": {\"events\": [\"ui.click\"], \"match\": {\"attr__data-cta\": {\"contains\": \"pricing\"}}}}"
3775
- }
3776
- ]
3777
- },
3778
- "description": "Array of conditions — ALL must match (AND logic) for this rule to fire."
3779
- },
3780
- "value": {
3781
- "description": "Value returned when all conditions match. For triggerWhen: true = fire the action."
3782
- }
3783
- },
3784
- "required": [
3785
- "conditions"
3786
- ],
3787
- "additionalProperties": false,
3788
- "description": "A single rule. ALL conditions must match (AND logic). Rules in a strategy are evaluated top-to-bottom — first rule where all conditions match wins and returns its value."
3789
- },
3790
- "description": "Ordered list of rules. Evaluated top-to-bottom — first match wins."
3791
- },
3792
- "default": {
3793
- "description": "Fallback value when no rule matches. For triggerWhen: false = do not fire by default."
3794
- }
3795
- },
3796
- "required": [
3797
- "type",
3798
- "rules"
3799
- ],
3800
- "additionalProperties": false,
3801
- "description": "Rule-based strategy. Evaluates rules top-to-bottom. First rule where ALL conditions match returns its value. If no rule matches, returns default. For triggerWhen: set value=true on matching rules, default=false."
3802
- },
3803
- {
3804
- "type": "object",
3805
- "properties": {
3806
- "type": {
3807
- "type": "string",
3808
- "const": "score"
3809
- },
3810
- "field": {
3811
- "type": "string"
3812
- },
3813
- "threshold": {
3814
- "type": "number"
3815
- },
3816
- "above": {},
3817
- "below": {}
3818
- },
3819
- "required": [
3820
- "type",
3821
- "field",
3822
- "threshold"
3823
- ],
3824
- "additionalProperties": false,
3825
- "description": "Score-based strategy. Compares a field value against a threshold."
3826
- },
3827
- {
3828
- "type": "object",
3829
- "properties": {
3830
- "type": {
3831
- "type": "string",
3832
- "const": "model"
3833
- },
3834
- "modelId": {
3835
- "type": "string"
3836
- },
3837
- "inputs": {
3838
- "type": "array",
3839
- "items": {
3840
- "type": "string"
3841
- }
3842
- },
3843
- "outputMapping": {
3844
- "type": "object",
3845
- "additionalProperties": {}
3846
- },
3847
- "default": {}
3848
- },
3849
- "required": [
3850
- "type",
3851
- "modelId",
3852
- "inputs",
3853
- "outputMapping"
3854
- ],
3855
- "additionalProperties": false,
3856
- "description": "ML model strategy. Sends inputs to a model and maps outputs."
3857
- },
3858
- {
3859
- "type": "object",
3860
- "properties": {
3861
- "type": {
3862
- "type": "string",
3863
- "const": "external"
3864
- },
3865
- "endpoint": {
3866
- "type": "string"
3867
- },
3868
- "method": {
3869
- "type": "string",
3870
- "enum": [
3871
- "GET",
3872
- "POST"
3873
- ]
3874
- },
3875
- "default": {},
3876
- "timeoutMs": {
3877
- "type": "number"
3878
- }
3879
- },
3880
- "required": [
3881
- "type",
3882
- "endpoint"
3883
- ],
3884
- "additionalProperties": false,
3885
- "description": "External API strategy. Calls an endpoint to determine the value."
3886
- }
3887
- ]
3406
+ "$ref": "#/$defs/decisionStrategy"
3888
3407
  },
3889
3408
  {
3890
3409
  "type": "null"
@@ -4471,521 +3990,40 @@
4471
3990
  "items": {
4472
3991
  "type": "string"
4473
3992
  },
4474
- "description": "Optional list of context keys passed to the endpoint to ground the answer."
4475
- },
4476
- "cache": {
4477
- "type": "string",
4478
- "enum": [
4479
- "session",
4480
- "none"
4481
- ],
4482
- "description": "\"session\" caches the generated answer for the user's session; \"none\" regenerates on each expand."
4483
- },
4484
- "fallback": {
4485
- "type": "string",
4486
- "description": "Static fallback answer text shown if the AI endpoint fails or times out."
4487
- }
4488
- },
4489
- "required": [
4490
- "endpoint"
4491
- ],
4492
- "additionalProperties": false,
4493
- "description": "Optional AI answer generation config. When set, the answer field is used as a fallback only."
4494
- }
4495
- },
4496
- "required": [
4497
- "id",
4498
- "question",
4499
- "answer"
4500
- ],
4501
- "additionalProperties": false,
4502
- "description": "Per-question configuration including the question text, answer content, and optional metadata."
4503
- },
4504
- "triggerWhen": {
4505
- "anyOf": [
4506
- {
4507
- "anyOf": [
4508
- {
4509
- "type": "object",
4510
- "properties": {
4511
- "type": {
4512
- "type": "string",
4513
- "const": "rules"
4514
- },
4515
- "rules": {
4516
- "type": "array",
4517
- "items": {
4518
- "type": "object",
4519
- "properties": {
4520
- "conditions": {
4521
- "type": "array",
4522
- "items": {
4523
- "anyOf": [
4524
- {
4525
- "type": "object",
4526
- "properties": {
4527
- "type": {
4528
- "type": "string",
4529
- "const": "page_url"
4530
- },
4531
- "url": {
4532
- "type": "string",
4533
- "description": "URL path to match (e.g. \"/pricing\", \"/dashboard\")"
4534
- }
4535
- },
4536
- "required": [
4537
- "type",
4538
- "url"
4539
- ],
4540
- "additionalProperties": false,
4541
- "description": "Fires when the current page URL matches. Use for page-specific actions. Example: {\"type\": \"page_url\", \"url\": \"/pricing\"}"
4542
- },
4543
- {
4544
- "type": "object",
4545
- "properties": {
4546
- "type": {
4547
- "type": "string",
4548
- "const": "route"
4549
- },
4550
- "routeId": {
4551
- "type": "string",
4552
- "description": "Named route ID from the route filter"
4553
- }
4554
- },
4555
- "required": [
4556
- "type",
4557
- "routeId"
4558
- ],
4559
- "additionalProperties": false,
4560
- "description": "Fires when the current route matches a named route ID."
4561
- },
4562
- {
4563
- "type": "object",
4564
- "properties": {
4565
- "type": {
4566
- "type": "string",
4567
- "const": "anchor_visible"
4568
- },
4569
- "anchorId": {
4570
- "type": "string",
4571
- "description": "CSS selector of the anchor element"
4572
- },
4573
- "state": {
4574
- "type": "string",
4575
- "enum": [
4576
- "visible",
4577
- "present",
4578
- "absent"
4579
- ],
4580
- "description": "\"visible\" = in viewport, \"present\" = in DOM, \"absent\" = not in DOM"
4581
- }
4582
- },
4583
- "required": [
4584
- "type",
4585
- "anchorId",
4586
- "state"
4587
- ],
4588
- "additionalProperties": false,
4589
- "description": "Fires based on a DOM element's visibility state. Example: {\"type\": \"anchor_visible\", \"anchorId\": \"#cta-button\", \"state\": \"visible\"}"
4590
- },
4591
- {
4592
- "type": "object",
4593
- "properties": {
4594
- "type": {
4595
- "type": "string",
4596
- "const": "event_occurred"
4597
- },
4598
- "eventName": {
4599
- "type": "string",
4600
- "description": "Event name (e.g. \"ui.click\", \"$pageview\")"
4601
- },
4602
- "withinMs": {
4603
- "type": "number",
4604
- "description": "Time window in ms. Omit = any time this session."
4605
- }
4606
- },
4607
- "required": [
4608
- "type",
4609
- "eventName"
4610
- ],
4611
- "additionalProperties": false,
4612
- "description": "Fires when a specific event has occurred during this session. Example: {\"type\": \"event_occurred\", \"eventName\": \"ui.click\", \"withinMs\": 5000}"
4613
- },
4614
- {
4615
- "type": "object",
4616
- "properties": {
4617
- "type": {
4618
- "type": "string",
4619
- "const": "state_equals"
4620
- },
4621
- "key": {
4622
- "type": "string",
4623
- "description": "Key in the SDK persistent state store (localStorage). Only valid for keys the host app explicitly sets via syntro.state.set()."
4624
- },
4625
- "value": {
4626
- "description": "Expected value to match against"
4627
- }
4628
- },
4629
- "required": [
4630
- "type",
4631
- "key"
4632
- ],
4633
- "additionalProperties": false,
4634
- "description": "Checks the SDK persistent state store (localStorage). ONLY for host-app state set via syntro.state.set() — NOT for user attributes like region, device, or UTM params (those are handled by segment targeting). Do NOT use this for targeting. If you do not know the valid state keys, do not use this condition type."
4635
- },
4636
- {
4637
- "type": "object",
4638
- "properties": {
4639
- "type": {
4640
- "type": "string",
4641
- "const": "viewport"
4642
- },
4643
- "minWidth": {
4644
- "type": "number",
4645
- "description": "Minimum viewport width in pixels"
4646
- },
4647
- "maxWidth": {
4648
- "type": "number",
4649
- "description": "Maximum viewport width in pixels"
4650
- },
4651
- "minHeight": {
4652
- "type": "number",
4653
- "description": "Minimum viewport height in pixels"
4654
- },
4655
- "maxHeight": {
4656
- "type": "number",
4657
- "description": "Maximum viewport height in pixels"
4658
- }
4659
- },
4660
- "required": [
4661
- "type"
4662
- ],
4663
- "additionalProperties": false,
4664
- "description": "Fires based on viewport (screen) size. Use for responsive behavior. Example: {\"type\": \"viewport\", \"minWidth\": 768} — fires on tablet and larger."
4665
- },
4666
- {
4667
- "type": "object",
4668
- "properties": {
4669
- "type": {
4670
- "type": "string",
4671
- "const": "session_metric"
4672
- },
4673
- "key": {
4674
- "type": "string",
4675
- "enum": [
4676
- "time_on_page",
4677
- "page_views",
4678
- "scroll_depth"
4679
- ],
4680
- "description": "Session metric key. time_on_page = seconds on current page, page_views = pages visited this session, scroll_depth = 0-100 percentage."
4681
- },
4682
- "operator": {
4683
- "type": "string",
4684
- "enum": [
4685
- "gte",
4686
- "lte",
4687
- "eq",
4688
- "gt",
4689
- "lt"
4690
- ]
4691
- },
4692
- "threshold": {
4693
- "type": "number",
4694
- "description": "Numeric threshold to compare against"
4695
- }
4696
- },
4697
- "required": [
4698
- "type",
4699
- "key",
4700
- "operator",
4701
- "threshold"
4702
- ],
4703
- "additionalProperties": false,
4704
- "description": "Fires when a session metric crosses a threshold. Valid keys: \"time_on_page\" (seconds), \"page_views\" (count), \"scroll_depth\" (0-100). Example: {\"type\": \"session_metric\", \"key\": \"time_on_page\", \"operator\": \"gte\", \"threshold\": 30}"
4705
- },
4706
- {
4707
- "type": "object",
4708
- "properties": {
4709
- "type": {
4710
- "type": "string",
4711
- "const": "dismissed"
4712
- },
4713
- "key": {
4714
- "type": "string",
4715
- "description": "Dismissal key (usually a tile or action ID)"
4716
- },
4717
- "inverted": {
4718
- "type": "boolean",
4719
- "description": "When true, fires if NOT dismissed (default behavior)"
4720
- }
4721
- },
4722
- "required": [
4723
- "type",
4724
- "key"
4725
- ],
4726
- "additionalProperties": false,
4727
- "description": "Checks if an item has been dismissed by the user. Use with inverted: true to show only if not dismissed."
4728
- },
4729
- {
4730
- "type": "object",
4731
- "properties": {
4732
- "type": {
4733
- "type": "string",
4734
- "const": "cooldown_active"
4735
- },
4736
- "key": {
4737
- "type": "string",
4738
- "description": "Cooldown key"
4739
- },
4740
- "inverted": {
4741
- "type": "boolean",
4742
- "description": "When true, fires if cooldown is NOT active"
4743
- }
4744
- },
4745
- "required": [
4746
- "type",
4747
- "key"
4748
- ],
4749
- "additionalProperties": false,
4750
- "description": "Checks if a cooldown timer is currently active. Use to prevent showing the same intervention too frequently."
4751
- },
4752
- {
4753
- "type": "object",
4754
- "properties": {
4755
- "type": {
4756
- "type": "string",
4757
- "const": "frequency_limit"
4758
- },
4759
- "key": {
4760
- "type": "string",
4761
- "description": "Frequency counter key"
4762
- },
4763
- "limit": {
4764
- "type": "number",
4765
- "description": "Maximum allowed count"
4766
- },
4767
- "inverted": {
4768
- "type": "boolean",
4769
- "description": "When true, fires if limit NOT reached"
4770
- }
4771
- },
4772
- "required": [
4773
- "type",
4774
- "key",
4775
- "limit"
4776
- ],
4777
- "additionalProperties": false,
4778
- "description": "Checks if a frequency limit has been reached. Use to cap how many times an action fires per session."
4779
- },
4780
- {
4781
- "type": "object",
4782
- "properties": {
4783
- "type": {
4784
- "type": "string",
4785
- "const": "event_count"
4786
- },
4787
- "key": {
4788
- "type": "string",
4789
- "description": "Unique key for this counter (used for accumulator registration)"
4790
- },
4791
- "operator": {
4792
- "type": "string",
4793
- "enum": [
4794
- "gte",
4795
- "lte",
4796
- "eq",
4797
- "gt",
4798
- "lt"
4799
- ]
4800
- },
4801
- "count": {
4802
- "type": "integer",
4803
- "minimum": 0,
4804
- "description": "Target count threshold"
4805
- },
4806
- "withinMs": {
4807
- "type": "number",
4808
- "exclusiveMinimum": 0,
4809
- "description": "Time window in ms. Omit = count across entire session."
4810
- },
4811
- "counter": {
4812
- "type": "object",
4813
- "properties": {
4814
- "events": {
4815
- "type": "array",
4816
- "items": {
4817
- "type": "string",
4818
- "enum": [
4819
- "ui.click",
4820
- "ui.scroll",
4821
- "ui.input",
4822
- "ui.change",
4823
- "ui.submit",
4824
- "ui.hover",
4825
- "ui.idle",
4826
- "ui.scroll_thrash",
4827
- "ui.focus_bounce",
4828
- "nav.page_view",
4829
- "nav.page_leave",
4830
- "behavior.rage_click",
4831
- "behavior.hesitation",
4832
- "behavior.confusion"
4833
- ],
4834
- "description": "Event name to count. ui.* = user interactions and behavioral detectors, nav.* = page navigation, behavior.* = derived behavioral signals."
4835
- },
4836
- "minItems": 1,
4837
- "description": "Event names to count. Use values from the countable events enum."
4838
- },
4839
- "match": {
4840
- "type": "object",
4841
- "additionalProperties": {
4842
- "type": "object",
4843
- "properties": {
4844
- "equals": {
4845
- "type": [
4846
- "string",
4847
- "number",
4848
- "boolean"
4849
- ]
4850
- },
4851
- "contains": {
4852
- "type": "string"
4853
- }
4854
- },
4855
- "additionalProperties": false,
4856
- "description": "Match operator for counter filters. Exactly one of equals or contains must be specified."
4857
- },
4858
- "description": "Property filters. Keys are event prop names or element-chain fields (tag_name, $el_text, attr__*). All entries AND together."
4859
- }
4860
- },
4861
- "required": [
4862
- "events"
4863
- ],
4864
- "additionalProperties": false,
4865
- "description": "Inline counter definition. Defines what events to count."
4866
- }
4867
- },
4868
- "required": [
4869
- "type",
4870
- "key",
4871
- "operator",
4872
- "count"
4873
- ],
4874
- "additionalProperties": false,
4875
- "description": "Fires when accumulated event count crosses a threshold. Most powerful trigger type. Example: {\"type\": \"event_count\", \"key\": \"pricing-clicks\", \"operator\": \"gte\", \"count\": 3, \"counter\": {\"events\": [\"ui.click\"], \"match\": {\"attr__data-cta\": {\"contains\": \"pricing\"}}}}"
4876
- }
4877
- ]
4878
- },
4879
- "description": "Array of conditions — ALL must match (AND logic) for this rule to fire."
4880
- },
4881
- "value": {
4882
- "description": "Value returned when all conditions match. For triggerWhen: true = fire the action."
4883
- }
4884
- },
4885
- "required": [
4886
- "conditions"
4887
- ],
4888
- "additionalProperties": false,
4889
- "description": "A single rule. ALL conditions must match (AND logic). Rules in a strategy are evaluated top-to-bottom — first rule where all conditions match wins and returns its value."
4890
- },
4891
- "description": "Ordered list of rules. Evaluated top-to-bottom — first match wins."
4892
- },
4893
- "default": {
4894
- "description": "Fallback value when no rule matches. For triggerWhen: false = do not fire by default."
4895
- }
4896
- },
4897
- "required": [
4898
- "type",
4899
- "rules"
4900
- ],
4901
- "additionalProperties": false,
4902
- "description": "Rule-based strategy. Evaluates rules top-to-bottom. First rule where ALL conditions match returns its value. If no rule matches, returns default. For triggerWhen: set value=true on matching rules, default=false."
4903
- },
4904
- {
4905
- "type": "object",
4906
- "properties": {
4907
- "type": {
4908
- "type": "string",
4909
- "const": "score"
4910
- },
4911
- "field": {
4912
- "type": "string"
4913
- },
4914
- "threshold": {
4915
- "type": "number"
4916
- },
4917
- "above": {},
4918
- "below": {}
4919
- },
4920
- "required": [
4921
- "type",
4922
- "field",
4923
- "threshold"
4924
- ],
4925
- "additionalProperties": false,
4926
- "description": "Score-based strategy. Compares a field value against a threshold."
4927
- },
4928
- {
4929
- "type": "object",
4930
- "properties": {
4931
- "type": {
4932
- "type": "string",
4933
- "const": "model"
4934
- },
4935
- "modelId": {
4936
- "type": "string"
4937
- },
4938
- "inputs": {
4939
- "type": "array",
4940
- "items": {
4941
- "type": "string"
4942
- }
4943
- },
4944
- "outputMapping": {
4945
- "type": "object",
4946
- "additionalProperties": {}
4947
- },
4948
- "default": {}
4949
- },
4950
- "required": [
4951
- "type",
4952
- "modelId",
4953
- "inputs",
4954
- "outputMapping"
4955
- ],
4956
- "additionalProperties": false,
4957
- "description": "ML model strategy. Sends inputs to a model and maps outputs."
4958
- },
4959
- {
4960
- "type": "object",
4961
- "properties": {
4962
- "type": {
4963
- "type": "string",
4964
- "const": "external"
4965
- },
4966
- "endpoint": {
4967
- "type": "string"
4968
- },
4969
- "method": {
4970
- "type": "string",
4971
- "enum": [
4972
- "GET",
4973
- "POST"
4974
- ]
4975
- },
4976
- "default": {},
4977
- "timeoutMs": {
4978
- "type": "number"
4979
- }
4980
- },
4981
- "required": [
4982
- "type",
4983
- "endpoint"
3993
+ "description": "Optional list of context keys passed to the endpoint to ground the answer."
3994
+ },
3995
+ "cache": {
3996
+ "type": "string",
3997
+ "enum": [
3998
+ "session",
3999
+ "none"
4984
4000
  ],
4985
- "additionalProperties": false,
4986
- "description": "External API strategy. Calls an endpoint to determine the value."
4001
+ "description": "\"session\" caches the generated answer for the user's session; \"none\" regenerates on each expand."
4002
+ },
4003
+ "fallback": {
4004
+ "type": "string",
4005
+ "description": "Static fallback answer text shown if the AI endpoint fails or times out."
4987
4006
  }
4988
- ]
4007
+ },
4008
+ "required": [
4009
+ "endpoint"
4010
+ ],
4011
+ "additionalProperties": false,
4012
+ "description": "Optional AI answer generation config. When set, the answer field is used as a fallback only."
4013
+ }
4014
+ },
4015
+ "required": [
4016
+ "id",
4017
+ "question",
4018
+ "answer"
4019
+ ],
4020
+ "additionalProperties": false,
4021
+ "description": "Per-question configuration including the question text, answer content, and optional metadata."
4022
+ },
4023
+ "triggerWhen": {
4024
+ "anyOf": [
4025
+ {
4026
+ "$ref": "#/$defs/decisionStrategy"
4989
4027
  },
4990
4028
  {
4991
4029
  "type": "null"
@@ -5077,509 +4115,41 @@
5077
4115
  "type": "object",
5078
4116
  "additionalProperties": {
5079
4117
  "type": "array",
5080
- "items": {
5081
- "type": "string"
5082
- }
5083
- },
5084
- "description": "Map of segment ID → ordered list of question IDs. Each segment gets its own item order."
5085
- }
5086
- },
5087
- "required": [
5088
- "type",
5089
- "segmentWeights"
5090
- ],
5091
- "additionalProperties": false,
5092
- "description": "Segment-based ordering strategy. Each key is a segment ID and the value is the ordered list of item IDs shown to users in that segment."
5093
- }
5094
- ],
5095
- "description": "Controls the display order of items. Omit for static (config) order, \"priority\" to sort by priority field, or a segment object for per-segment ordering."
5096
- },
5097
- "injections": {
5098
- "type": "array",
5099
- "items": {
5100
- "type": "object",
5101
- "properties": {
5102
- "trigger": {
5103
- "anyOf": [
5104
- {
5105
- "type": "object",
5106
- "properties": {
5107
- "type": {
5108
- "type": "string",
5109
- "const": "rules"
5110
- },
5111
- "rules": {
5112
- "type": "array",
5113
- "items": {
5114
- "type": "object",
5115
- "properties": {
5116
- "conditions": {
5117
- "type": "array",
5118
- "items": {
5119
- "anyOf": [
5120
- {
5121
- "type": "object",
5122
- "properties": {
5123
- "type": {
5124
- "type": "string",
5125
- "const": "page_url"
5126
- },
5127
- "url": {
5128
- "type": "string",
5129
- "description": "URL path to match (e.g. \"/pricing\", \"/dashboard\")"
5130
- }
5131
- },
5132
- "required": [
5133
- "type",
5134
- "url"
5135
- ],
5136
- "additionalProperties": false,
5137
- "description": "Fires when the current page URL matches. Use for page-specific actions. Example: {\"type\": \"page_url\", \"url\": \"/pricing\"}"
5138
- },
5139
- {
5140
- "type": "object",
5141
- "properties": {
5142
- "type": {
5143
- "type": "string",
5144
- "const": "route"
5145
- },
5146
- "routeId": {
5147
- "type": "string",
5148
- "description": "Named route ID from the route filter"
5149
- }
5150
- },
5151
- "required": [
5152
- "type",
5153
- "routeId"
5154
- ],
5155
- "additionalProperties": false,
5156
- "description": "Fires when the current route matches a named route ID."
5157
- },
5158
- {
5159
- "type": "object",
5160
- "properties": {
5161
- "type": {
5162
- "type": "string",
5163
- "const": "anchor_visible"
5164
- },
5165
- "anchorId": {
5166
- "type": "string",
5167
- "description": "CSS selector of the anchor element"
5168
- },
5169
- "state": {
5170
- "type": "string",
5171
- "enum": [
5172
- "visible",
5173
- "present",
5174
- "absent"
5175
- ],
5176
- "description": "\"visible\" = in viewport, \"present\" = in DOM, \"absent\" = not in DOM"
5177
- }
5178
- },
5179
- "required": [
5180
- "type",
5181
- "anchorId",
5182
- "state"
5183
- ],
5184
- "additionalProperties": false,
5185
- "description": "Fires based on a DOM element's visibility state. Example: {\"type\": \"anchor_visible\", \"anchorId\": \"#cta-button\", \"state\": \"visible\"}"
5186
- },
5187
- {
5188
- "type": "object",
5189
- "properties": {
5190
- "type": {
5191
- "type": "string",
5192
- "const": "event_occurred"
5193
- },
5194
- "eventName": {
5195
- "type": "string",
5196
- "description": "Event name (e.g. \"ui.click\", \"$pageview\")"
5197
- },
5198
- "withinMs": {
5199
- "type": "number",
5200
- "description": "Time window in ms. Omit = any time this session."
5201
- }
5202
- },
5203
- "required": [
5204
- "type",
5205
- "eventName"
5206
- ],
5207
- "additionalProperties": false,
5208
- "description": "Fires when a specific event has occurred during this session. Example: {\"type\": \"event_occurred\", \"eventName\": \"ui.click\", \"withinMs\": 5000}"
5209
- },
5210
- {
5211
- "type": "object",
5212
- "properties": {
5213
- "type": {
5214
- "type": "string",
5215
- "const": "state_equals"
5216
- },
5217
- "key": {
5218
- "type": "string",
5219
- "description": "Key in the SDK persistent state store (localStorage). Only valid for keys the host app explicitly sets via syntro.state.set()."
5220
- },
5221
- "value": {
5222
- "description": "Expected value to match against"
5223
- }
5224
- },
5225
- "required": [
5226
- "type",
5227
- "key"
5228
- ],
5229
- "additionalProperties": false,
5230
- "description": "Checks the SDK persistent state store (localStorage). ONLY for host-app state set via syntro.state.set() — NOT for user attributes like region, device, or UTM params (those are handled by segment targeting). Do NOT use this for targeting. If you do not know the valid state keys, do not use this condition type."
5231
- },
5232
- {
5233
- "type": "object",
5234
- "properties": {
5235
- "type": {
5236
- "type": "string",
5237
- "const": "viewport"
5238
- },
5239
- "minWidth": {
5240
- "type": "number",
5241
- "description": "Minimum viewport width in pixels"
5242
- },
5243
- "maxWidth": {
5244
- "type": "number",
5245
- "description": "Maximum viewport width in pixels"
5246
- },
5247
- "minHeight": {
5248
- "type": "number",
5249
- "description": "Minimum viewport height in pixels"
5250
- },
5251
- "maxHeight": {
5252
- "type": "number",
5253
- "description": "Maximum viewport height in pixels"
5254
- }
5255
- },
5256
- "required": [
5257
- "type"
5258
- ],
5259
- "additionalProperties": false,
5260
- "description": "Fires based on viewport (screen) size. Use for responsive behavior. Example: {\"type\": \"viewport\", \"minWidth\": 768} — fires on tablet and larger."
5261
- },
5262
- {
5263
- "type": "object",
5264
- "properties": {
5265
- "type": {
5266
- "type": "string",
5267
- "const": "session_metric"
5268
- },
5269
- "key": {
5270
- "type": "string",
5271
- "enum": [
5272
- "time_on_page",
5273
- "page_views",
5274
- "scroll_depth"
5275
- ],
5276
- "description": "Session metric key. time_on_page = seconds on current page, page_views = pages visited this session, scroll_depth = 0-100 percentage."
5277
- },
5278
- "operator": {
5279
- "type": "string",
5280
- "enum": [
5281
- "gte",
5282
- "lte",
5283
- "eq",
5284
- "gt",
5285
- "lt"
5286
- ]
5287
- },
5288
- "threshold": {
5289
- "type": "number",
5290
- "description": "Numeric threshold to compare against"
5291
- }
5292
- },
5293
- "required": [
5294
- "type",
5295
- "key",
5296
- "operator",
5297
- "threshold"
5298
- ],
5299
- "additionalProperties": false,
5300
- "description": "Fires when a session metric crosses a threshold. Valid keys: \"time_on_page\" (seconds), \"page_views\" (count), \"scroll_depth\" (0-100). Example: {\"type\": \"session_metric\", \"key\": \"time_on_page\", \"operator\": \"gte\", \"threshold\": 30}"
5301
- },
5302
- {
5303
- "type": "object",
5304
- "properties": {
5305
- "type": {
5306
- "type": "string",
5307
- "const": "dismissed"
5308
- },
5309
- "key": {
5310
- "type": "string",
5311
- "description": "Dismissal key (usually a tile or action ID)"
5312
- },
5313
- "inverted": {
5314
- "type": "boolean",
5315
- "description": "When true, fires if NOT dismissed (default behavior)"
5316
- }
5317
- },
5318
- "required": [
5319
- "type",
5320
- "key"
5321
- ],
5322
- "additionalProperties": false,
5323
- "description": "Checks if an item has been dismissed by the user. Use with inverted: true to show only if not dismissed."
5324
- },
5325
- {
5326
- "type": "object",
5327
- "properties": {
5328
- "type": {
5329
- "type": "string",
5330
- "const": "cooldown_active"
5331
- },
5332
- "key": {
5333
- "type": "string",
5334
- "description": "Cooldown key"
5335
- },
5336
- "inverted": {
5337
- "type": "boolean",
5338
- "description": "When true, fires if cooldown is NOT active"
5339
- }
5340
- },
5341
- "required": [
5342
- "type",
5343
- "key"
5344
- ],
5345
- "additionalProperties": false,
5346
- "description": "Checks if a cooldown timer is currently active. Use to prevent showing the same intervention too frequently."
5347
- },
5348
- {
5349
- "type": "object",
5350
- "properties": {
5351
- "type": {
5352
- "type": "string",
5353
- "const": "frequency_limit"
5354
- },
5355
- "key": {
5356
- "type": "string",
5357
- "description": "Frequency counter key"
5358
- },
5359
- "limit": {
5360
- "type": "number",
5361
- "description": "Maximum allowed count"
5362
- },
5363
- "inverted": {
5364
- "type": "boolean",
5365
- "description": "When true, fires if limit NOT reached"
5366
- }
5367
- },
5368
- "required": [
5369
- "type",
5370
- "key",
5371
- "limit"
5372
- ],
5373
- "additionalProperties": false,
5374
- "description": "Checks if a frequency limit has been reached. Use to cap how many times an action fires per session."
5375
- },
5376
- {
5377
- "type": "object",
5378
- "properties": {
5379
- "type": {
5380
- "type": "string",
5381
- "const": "event_count"
5382
- },
5383
- "key": {
5384
- "type": "string",
5385
- "description": "Unique key for this counter (used for accumulator registration)"
5386
- },
5387
- "operator": {
5388
- "type": "string",
5389
- "enum": [
5390
- "gte",
5391
- "lte",
5392
- "eq",
5393
- "gt",
5394
- "lt"
5395
- ]
5396
- },
5397
- "count": {
5398
- "type": "integer",
5399
- "minimum": 0,
5400
- "description": "Target count threshold"
5401
- },
5402
- "withinMs": {
5403
- "type": "number",
5404
- "exclusiveMinimum": 0,
5405
- "description": "Time window in ms. Omit = count across entire session."
5406
- },
5407
- "counter": {
5408
- "type": "object",
5409
- "properties": {
5410
- "events": {
5411
- "type": "array",
5412
- "items": {
5413
- "type": "string",
5414
- "enum": [
5415
- "ui.click",
5416
- "ui.scroll",
5417
- "ui.input",
5418
- "ui.change",
5419
- "ui.submit",
5420
- "ui.hover",
5421
- "ui.idle",
5422
- "ui.scroll_thrash",
5423
- "ui.focus_bounce",
5424
- "nav.page_view",
5425
- "nav.page_leave",
5426
- "behavior.rage_click",
5427
- "behavior.hesitation",
5428
- "behavior.confusion"
5429
- ],
5430
- "description": "Event name to count. ui.* = user interactions and behavioral detectors, nav.* = page navigation, behavior.* = derived behavioral signals."
5431
- },
5432
- "minItems": 1,
5433
- "description": "Event names to count. Use values from the countable events enum."
5434
- },
5435
- "match": {
5436
- "type": "object",
5437
- "additionalProperties": {
5438
- "type": "object",
5439
- "properties": {
5440
- "equals": {
5441
- "type": [
5442
- "string",
5443
- "number",
5444
- "boolean"
5445
- ]
5446
- },
5447
- "contains": {
5448
- "type": "string"
5449
- }
5450
- },
5451
- "additionalProperties": false,
5452
- "description": "Match operator for counter filters. Exactly one of equals or contains must be specified."
5453
- },
5454
- "description": "Property filters. Keys are event prop names or element-chain fields (tag_name, $el_text, attr__*). All entries AND together."
5455
- }
5456
- },
5457
- "required": [
5458
- "events"
5459
- ],
5460
- "additionalProperties": false,
5461
- "description": "Inline counter definition. Defines what events to count."
5462
- }
5463
- },
5464
- "required": [
5465
- "type",
5466
- "key",
5467
- "operator",
5468
- "count"
5469
- ],
5470
- "additionalProperties": false,
5471
- "description": "Fires when accumulated event count crosses a threshold. Most powerful trigger type. Example: {\"type\": \"event_count\", \"key\": \"pricing-clicks\", \"operator\": \"gte\", \"count\": 3, \"counter\": {\"events\": [\"ui.click\"], \"match\": {\"attr__data-cta\": {\"contains\": \"pricing\"}}}}"
5472
- }
5473
- ]
5474
- },
5475
- "description": "Array of conditions — ALL must match (AND logic) for this rule to fire."
5476
- },
5477
- "value": {
5478
- "description": "Value returned when all conditions match. For triggerWhen: true = fire the action."
5479
- }
5480
- },
5481
- "required": [
5482
- "conditions"
5483
- ],
5484
- "additionalProperties": false,
5485
- "description": "A single rule. ALL conditions must match (AND logic). Rules in a strategy are evaluated top-to-bottom — first rule where all conditions match wins and returns its value."
5486
- },
5487
- "description": "Ordered list of rules. Evaluated top-to-bottom — first match wins."
5488
- },
5489
- "default": {
5490
- "description": "Fallback value when no rule matches. For triggerWhen: false = do not fire by default."
5491
- }
5492
- },
5493
- "required": [
5494
- "type",
5495
- "rules"
5496
- ],
5497
- "additionalProperties": false,
5498
- "description": "Rule-based strategy. Evaluates rules top-to-bottom. First rule where ALL conditions match returns its value. If no rule matches, returns default. For triggerWhen: set value=true on matching rules, default=false."
4118
+ "items": {
4119
+ "type": "string"
4120
+ }
5499
4121
  },
4122
+ "description": "Map of segment ID → ordered list of question IDs. Each segment gets its own item order."
4123
+ }
4124
+ },
4125
+ "required": [
4126
+ "type",
4127
+ "segmentWeights"
4128
+ ],
4129
+ "additionalProperties": false,
4130
+ "description": "Segment-based ordering strategy. Each key is a segment ID and the value is the ordered list of item IDs shown to users in that segment."
4131
+ }
4132
+ ],
4133
+ "description": "Controls the display order of items. Omit for static (config) order, \"priority\" to sort by priority field, or a segment object for per-segment ordering."
4134
+ },
4135
+ "injections": {
4136
+ "type": "array",
4137
+ "items": {
4138
+ "type": "object",
4139
+ "properties": {
4140
+ "trigger": {
4141
+ "anyOf": [
5500
4142
  {
5501
- "type": "object",
5502
- "properties": {
5503
- "type": {
5504
- "type": "string",
5505
- "const": "score"
5506
- },
5507
- "field": {
5508
- "type": "string"
5509
- },
5510
- "threshold": {
5511
- "type": "number"
5512
- },
5513
- "above": {},
5514
- "below": {}
5515
- },
5516
- "required": [
5517
- "type",
5518
- "field",
5519
- "threshold"
5520
- ],
5521
- "additionalProperties": false,
5522
- "description": "Score-based strategy. Compares a field value against a threshold."
4143
+ "$ref": "#/$defs/ruleStrategy"
5523
4144
  },
5524
4145
  {
5525
- "type": "object",
5526
- "properties": {
5527
- "type": {
5528
- "type": "string",
5529
- "const": "model"
5530
- },
5531
- "modelId": {
5532
- "type": "string"
5533
- },
5534
- "inputs": {
5535
- "type": "array",
5536
- "items": {
5537
- "type": "string"
5538
- }
5539
- },
5540
- "outputMapping": {
5541
- "type": "object",
5542
- "additionalProperties": {}
5543
- },
5544
- "default": {}
5545
- },
5546
- "required": [
5547
- "type",
5548
- "modelId",
5549
- "inputs",
5550
- "outputMapping"
5551
- ],
5552
- "additionalProperties": false,
5553
- "description": "ML model strategy. Sends inputs to a model and maps outputs."
4146
+ "$ref": "#/$defs/scoreStrategy"
5554
4147
  },
5555
4148
  {
5556
- "type": "object",
5557
- "properties": {
5558
- "type": {
5559
- "type": "string",
5560
- "const": "external"
5561
- },
5562
- "endpoint": {
5563
- "type": "string"
5564
- },
5565
- "method": {
5566
- "type": "string",
5567
- "enum": [
5568
- "GET",
5569
- "POST"
5570
- ]
5571
- },
5572
- "default": {},
5573
- "timeoutMs": {
5574
- "type": "number"
5575
- }
5576
- },
5577
- "required": [
5578
- "type",
5579
- "endpoint"
5580
- ],
5581
- "additionalProperties": false,
5582
- "description": "External API strategy. Calls an endpoint to determine the value."
4149
+ "$ref": "#/$defs/modelStrategy"
4150
+ },
4151
+ {
4152
+ "$ref": "#/$defs/externalStrategy"
5583
4153
  }
5584
4154
  ],
5585
4155
  "description": "Decision strategy that evaluates to true when the items should be injected (e.g. page URL matches checkout)."
@@ -5771,488 +4341,7 @@
5771
4341
  "triggerWhen": {
5772
4342
  "anyOf": [
5773
4343
  {
5774
- "anyOf": [
5775
- {
5776
- "type": "object",
5777
- "properties": {
5778
- "type": {
5779
- "type": "string",
5780
- "const": "rules"
5781
- },
5782
- "rules": {
5783
- "type": "array",
5784
- "items": {
5785
- "type": "object",
5786
- "properties": {
5787
- "conditions": {
5788
- "type": "array",
5789
- "items": {
5790
- "anyOf": [
5791
- {
5792
- "type": "object",
5793
- "properties": {
5794
- "type": {
5795
- "type": "string",
5796
- "const": "page_url"
5797
- },
5798
- "url": {
5799
- "type": "string",
5800
- "description": "URL path to match (e.g. \"/pricing\", \"/dashboard\")"
5801
- }
5802
- },
5803
- "required": [
5804
- "type",
5805
- "url"
5806
- ],
5807
- "additionalProperties": false,
5808
- "description": "Fires when the current page URL matches. Use for page-specific actions. Example: {\"type\": \"page_url\", \"url\": \"/pricing\"}"
5809
- },
5810
- {
5811
- "type": "object",
5812
- "properties": {
5813
- "type": {
5814
- "type": "string",
5815
- "const": "route"
5816
- },
5817
- "routeId": {
5818
- "type": "string",
5819
- "description": "Named route ID from the route filter"
5820
- }
5821
- },
5822
- "required": [
5823
- "type",
5824
- "routeId"
5825
- ],
5826
- "additionalProperties": false,
5827
- "description": "Fires when the current route matches a named route ID."
5828
- },
5829
- {
5830
- "type": "object",
5831
- "properties": {
5832
- "type": {
5833
- "type": "string",
5834
- "const": "anchor_visible"
5835
- },
5836
- "anchorId": {
5837
- "type": "string",
5838
- "description": "CSS selector of the anchor element"
5839
- },
5840
- "state": {
5841
- "type": "string",
5842
- "enum": [
5843
- "visible",
5844
- "present",
5845
- "absent"
5846
- ],
5847
- "description": "\"visible\" = in viewport, \"present\" = in DOM, \"absent\" = not in DOM"
5848
- }
5849
- },
5850
- "required": [
5851
- "type",
5852
- "anchorId",
5853
- "state"
5854
- ],
5855
- "additionalProperties": false,
5856
- "description": "Fires based on a DOM element's visibility state. Example: {\"type\": \"anchor_visible\", \"anchorId\": \"#cta-button\", \"state\": \"visible\"}"
5857
- },
5858
- {
5859
- "type": "object",
5860
- "properties": {
5861
- "type": {
5862
- "type": "string",
5863
- "const": "event_occurred"
5864
- },
5865
- "eventName": {
5866
- "type": "string",
5867
- "description": "Event name (e.g. \"ui.click\", \"$pageview\")"
5868
- },
5869
- "withinMs": {
5870
- "type": "number",
5871
- "description": "Time window in ms. Omit = any time this session."
5872
- }
5873
- },
5874
- "required": [
5875
- "type",
5876
- "eventName"
5877
- ],
5878
- "additionalProperties": false,
5879
- "description": "Fires when a specific event has occurred during this session. Example: {\"type\": \"event_occurred\", \"eventName\": \"ui.click\", \"withinMs\": 5000}"
5880
- },
5881
- {
5882
- "type": "object",
5883
- "properties": {
5884
- "type": {
5885
- "type": "string",
5886
- "const": "state_equals"
5887
- },
5888
- "key": {
5889
- "type": "string",
5890
- "description": "Key in the SDK persistent state store (localStorage). Only valid for keys the host app explicitly sets via syntro.state.set()."
5891
- },
5892
- "value": {
5893
- "description": "Expected value to match against"
5894
- }
5895
- },
5896
- "required": [
5897
- "type",
5898
- "key"
5899
- ],
5900
- "additionalProperties": false,
5901
- "description": "Checks the SDK persistent state store (localStorage). ONLY for host-app state set via syntro.state.set() — NOT for user attributes like region, device, or UTM params (those are handled by segment targeting). Do NOT use this for targeting. If you do not know the valid state keys, do not use this condition type."
5902
- },
5903
- {
5904
- "type": "object",
5905
- "properties": {
5906
- "type": {
5907
- "type": "string",
5908
- "const": "viewport"
5909
- },
5910
- "minWidth": {
5911
- "type": "number",
5912
- "description": "Minimum viewport width in pixels"
5913
- },
5914
- "maxWidth": {
5915
- "type": "number",
5916
- "description": "Maximum viewport width in pixels"
5917
- },
5918
- "minHeight": {
5919
- "type": "number",
5920
- "description": "Minimum viewport height in pixels"
5921
- },
5922
- "maxHeight": {
5923
- "type": "number",
5924
- "description": "Maximum viewport height in pixels"
5925
- }
5926
- },
5927
- "required": [
5928
- "type"
5929
- ],
5930
- "additionalProperties": false,
5931
- "description": "Fires based on viewport (screen) size. Use for responsive behavior. Example: {\"type\": \"viewport\", \"minWidth\": 768} — fires on tablet and larger."
5932
- },
5933
- {
5934
- "type": "object",
5935
- "properties": {
5936
- "type": {
5937
- "type": "string",
5938
- "const": "session_metric"
5939
- },
5940
- "key": {
5941
- "type": "string",
5942
- "enum": [
5943
- "time_on_page",
5944
- "page_views",
5945
- "scroll_depth"
5946
- ],
5947
- "description": "Session metric key. time_on_page = seconds on current page, page_views = pages visited this session, scroll_depth = 0-100 percentage."
5948
- },
5949
- "operator": {
5950
- "type": "string",
5951
- "enum": [
5952
- "gte",
5953
- "lte",
5954
- "eq",
5955
- "gt",
5956
- "lt"
5957
- ]
5958
- },
5959
- "threshold": {
5960
- "type": "number",
5961
- "description": "Numeric threshold to compare against"
5962
- }
5963
- },
5964
- "required": [
5965
- "type",
5966
- "key",
5967
- "operator",
5968
- "threshold"
5969
- ],
5970
- "additionalProperties": false,
5971
- "description": "Fires when a session metric crosses a threshold. Valid keys: \"time_on_page\" (seconds), \"page_views\" (count), \"scroll_depth\" (0-100). Example: {\"type\": \"session_metric\", \"key\": \"time_on_page\", \"operator\": \"gte\", \"threshold\": 30}"
5972
- },
5973
- {
5974
- "type": "object",
5975
- "properties": {
5976
- "type": {
5977
- "type": "string",
5978
- "const": "dismissed"
5979
- },
5980
- "key": {
5981
- "type": "string",
5982
- "description": "Dismissal key (usually a tile or action ID)"
5983
- },
5984
- "inverted": {
5985
- "type": "boolean",
5986
- "description": "When true, fires if NOT dismissed (default behavior)"
5987
- }
5988
- },
5989
- "required": [
5990
- "type",
5991
- "key"
5992
- ],
5993
- "additionalProperties": false,
5994
- "description": "Checks if an item has been dismissed by the user. Use with inverted: true to show only if not dismissed."
5995
- },
5996
- {
5997
- "type": "object",
5998
- "properties": {
5999
- "type": {
6000
- "type": "string",
6001
- "const": "cooldown_active"
6002
- },
6003
- "key": {
6004
- "type": "string",
6005
- "description": "Cooldown key"
6006
- },
6007
- "inverted": {
6008
- "type": "boolean",
6009
- "description": "When true, fires if cooldown is NOT active"
6010
- }
6011
- },
6012
- "required": [
6013
- "type",
6014
- "key"
6015
- ],
6016
- "additionalProperties": false,
6017
- "description": "Checks if a cooldown timer is currently active. Use to prevent showing the same intervention too frequently."
6018
- },
6019
- {
6020
- "type": "object",
6021
- "properties": {
6022
- "type": {
6023
- "type": "string",
6024
- "const": "frequency_limit"
6025
- },
6026
- "key": {
6027
- "type": "string",
6028
- "description": "Frequency counter key"
6029
- },
6030
- "limit": {
6031
- "type": "number",
6032
- "description": "Maximum allowed count"
6033
- },
6034
- "inverted": {
6035
- "type": "boolean",
6036
- "description": "When true, fires if limit NOT reached"
6037
- }
6038
- },
6039
- "required": [
6040
- "type",
6041
- "key",
6042
- "limit"
6043
- ],
6044
- "additionalProperties": false,
6045
- "description": "Checks if a frequency limit has been reached. Use to cap how many times an action fires per session."
6046
- },
6047
- {
6048
- "type": "object",
6049
- "properties": {
6050
- "type": {
6051
- "type": "string",
6052
- "const": "event_count"
6053
- },
6054
- "key": {
6055
- "type": "string",
6056
- "description": "Unique key for this counter (used for accumulator registration)"
6057
- },
6058
- "operator": {
6059
- "type": "string",
6060
- "enum": [
6061
- "gte",
6062
- "lte",
6063
- "eq",
6064
- "gt",
6065
- "lt"
6066
- ]
6067
- },
6068
- "count": {
6069
- "type": "integer",
6070
- "minimum": 0,
6071
- "description": "Target count threshold"
6072
- },
6073
- "withinMs": {
6074
- "type": "number",
6075
- "exclusiveMinimum": 0,
6076
- "description": "Time window in ms. Omit = count across entire session."
6077
- },
6078
- "counter": {
6079
- "type": "object",
6080
- "properties": {
6081
- "events": {
6082
- "type": "array",
6083
- "items": {
6084
- "type": "string",
6085
- "enum": [
6086
- "ui.click",
6087
- "ui.scroll",
6088
- "ui.input",
6089
- "ui.change",
6090
- "ui.submit",
6091
- "ui.hover",
6092
- "ui.idle",
6093
- "ui.scroll_thrash",
6094
- "ui.focus_bounce",
6095
- "nav.page_view",
6096
- "nav.page_leave",
6097
- "behavior.rage_click",
6098
- "behavior.hesitation",
6099
- "behavior.confusion"
6100
- ],
6101
- "description": "Event name to count. ui.* = user interactions and behavioral detectors, nav.* = page navigation, behavior.* = derived behavioral signals."
6102
- },
6103
- "minItems": 1,
6104
- "description": "Event names to count. Use values from the countable events enum."
6105
- },
6106
- "match": {
6107
- "type": "object",
6108
- "additionalProperties": {
6109
- "type": "object",
6110
- "properties": {
6111
- "equals": {
6112
- "type": [
6113
- "string",
6114
- "number",
6115
- "boolean"
6116
- ]
6117
- },
6118
- "contains": {
6119
- "type": "string"
6120
- }
6121
- },
6122
- "additionalProperties": false,
6123
- "description": "Match operator for counter filters. Exactly one of equals or contains must be specified."
6124
- },
6125
- "description": "Property filters. Keys are event prop names or element-chain fields (tag_name, $el_text, attr__*). All entries AND together."
6126
- }
6127
- },
6128
- "required": [
6129
- "events"
6130
- ],
6131
- "additionalProperties": false,
6132
- "description": "Inline counter definition. Defines what events to count."
6133
- }
6134
- },
6135
- "required": [
6136
- "type",
6137
- "key",
6138
- "operator",
6139
- "count"
6140
- ],
6141
- "additionalProperties": false,
6142
- "description": "Fires when accumulated event count crosses a threshold. Most powerful trigger type. Example: {\"type\": \"event_count\", \"key\": \"pricing-clicks\", \"operator\": \"gte\", \"count\": 3, \"counter\": {\"events\": [\"ui.click\"], \"match\": {\"attr__data-cta\": {\"contains\": \"pricing\"}}}}"
6143
- }
6144
- ]
6145
- },
6146
- "description": "Array of conditions — ALL must match (AND logic) for this rule to fire."
6147
- },
6148
- "value": {
6149
- "description": "Value returned when all conditions match. For triggerWhen: true = fire the action."
6150
- }
6151
- },
6152
- "required": [
6153
- "conditions"
6154
- ],
6155
- "additionalProperties": false,
6156
- "description": "A single rule. ALL conditions must match (AND logic). Rules in a strategy are evaluated top-to-bottom — first rule where all conditions match wins and returns its value."
6157
- },
6158
- "description": "Ordered list of rules. Evaluated top-to-bottom — first match wins."
6159
- },
6160
- "default": {
6161
- "description": "Fallback value when no rule matches. For triggerWhen: false = do not fire by default."
6162
- }
6163
- },
6164
- "required": [
6165
- "type",
6166
- "rules"
6167
- ],
6168
- "additionalProperties": false,
6169
- "description": "Rule-based strategy. Evaluates rules top-to-bottom. First rule where ALL conditions match returns its value. If no rule matches, returns default. For triggerWhen: set value=true on matching rules, default=false."
6170
- },
6171
- {
6172
- "type": "object",
6173
- "properties": {
6174
- "type": {
6175
- "type": "string",
6176
- "const": "score"
6177
- },
6178
- "field": {
6179
- "type": "string"
6180
- },
6181
- "threshold": {
6182
- "type": "number"
6183
- },
6184
- "above": {},
6185
- "below": {}
6186
- },
6187
- "required": [
6188
- "type",
6189
- "field",
6190
- "threshold"
6191
- ],
6192
- "additionalProperties": false,
6193
- "description": "Score-based strategy. Compares a field value against a threshold."
6194
- },
6195
- {
6196
- "type": "object",
6197
- "properties": {
6198
- "type": {
6199
- "type": "string",
6200
- "const": "model"
6201
- },
6202
- "modelId": {
6203
- "type": "string"
6204
- },
6205
- "inputs": {
6206
- "type": "array",
6207
- "items": {
6208
- "type": "string"
6209
- }
6210
- },
6211
- "outputMapping": {
6212
- "type": "object",
6213
- "additionalProperties": {}
6214
- },
6215
- "default": {}
6216
- },
6217
- "required": [
6218
- "type",
6219
- "modelId",
6220
- "inputs",
6221
- "outputMapping"
6222
- ],
6223
- "additionalProperties": false,
6224
- "description": "ML model strategy. Sends inputs to a model and maps outputs."
6225
- },
6226
- {
6227
- "type": "object",
6228
- "properties": {
6229
- "type": {
6230
- "type": "string",
6231
- "const": "external"
6232
- },
6233
- "endpoint": {
6234
- "type": "string"
6235
- },
6236
- "method": {
6237
- "type": "string",
6238
- "enum": [
6239
- "GET",
6240
- "POST"
6241
- ]
6242
- },
6243
- "default": {},
6244
- "timeoutMs": {
6245
- "type": "number"
6246
- }
6247
- },
6248
- "required": [
6249
- "type",
6250
- "endpoint"
6251
- ],
6252
- "additionalProperties": false,
6253
- "description": "External API strategy. Calls an endpoint to determine the value."
6254
- }
6255
- ]
4344
+ "$ref": "#/$defs/decisionStrategy"
6256
4345
  },
6257
4346
  {
6258
4347
  "type": "null"
@@ -6446,488 +4535,7 @@
6446
4535
  "triggerWhen": {
6447
4536
  "anyOf": [
6448
4537
  {
6449
- "anyOf": [
6450
- {
6451
- "type": "object",
6452
- "properties": {
6453
- "type": {
6454
- "type": "string",
6455
- "const": "rules"
6456
- },
6457
- "rules": {
6458
- "type": "array",
6459
- "items": {
6460
- "type": "object",
6461
- "properties": {
6462
- "conditions": {
6463
- "type": "array",
6464
- "items": {
6465
- "anyOf": [
6466
- {
6467
- "type": "object",
6468
- "properties": {
6469
- "type": {
6470
- "type": "string",
6471
- "const": "page_url"
6472
- },
6473
- "url": {
6474
- "type": "string",
6475
- "description": "URL path to match (e.g. \"/pricing\", \"/dashboard\")"
6476
- }
6477
- },
6478
- "required": [
6479
- "type",
6480
- "url"
6481
- ],
6482
- "additionalProperties": false,
6483
- "description": "Fires when the current page URL matches. Use for page-specific actions. Example: {\"type\": \"page_url\", \"url\": \"/pricing\"}"
6484
- },
6485
- {
6486
- "type": "object",
6487
- "properties": {
6488
- "type": {
6489
- "type": "string",
6490
- "const": "route"
6491
- },
6492
- "routeId": {
6493
- "type": "string",
6494
- "description": "Named route ID from the route filter"
6495
- }
6496
- },
6497
- "required": [
6498
- "type",
6499
- "routeId"
6500
- ],
6501
- "additionalProperties": false,
6502
- "description": "Fires when the current route matches a named route ID."
6503
- },
6504
- {
6505
- "type": "object",
6506
- "properties": {
6507
- "type": {
6508
- "type": "string",
6509
- "const": "anchor_visible"
6510
- },
6511
- "anchorId": {
6512
- "type": "string",
6513
- "description": "CSS selector of the anchor element"
6514
- },
6515
- "state": {
6516
- "type": "string",
6517
- "enum": [
6518
- "visible",
6519
- "present",
6520
- "absent"
6521
- ],
6522
- "description": "\"visible\" = in viewport, \"present\" = in DOM, \"absent\" = not in DOM"
6523
- }
6524
- },
6525
- "required": [
6526
- "type",
6527
- "anchorId",
6528
- "state"
6529
- ],
6530
- "additionalProperties": false,
6531
- "description": "Fires based on a DOM element's visibility state. Example: {\"type\": \"anchor_visible\", \"anchorId\": \"#cta-button\", \"state\": \"visible\"}"
6532
- },
6533
- {
6534
- "type": "object",
6535
- "properties": {
6536
- "type": {
6537
- "type": "string",
6538
- "const": "event_occurred"
6539
- },
6540
- "eventName": {
6541
- "type": "string",
6542
- "description": "Event name (e.g. \"ui.click\", \"$pageview\")"
6543
- },
6544
- "withinMs": {
6545
- "type": "number",
6546
- "description": "Time window in ms. Omit = any time this session."
6547
- }
6548
- },
6549
- "required": [
6550
- "type",
6551
- "eventName"
6552
- ],
6553
- "additionalProperties": false,
6554
- "description": "Fires when a specific event has occurred during this session. Example: {\"type\": \"event_occurred\", \"eventName\": \"ui.click\", \"withinMs\": 5000}"
6555
- },
6556
- {
6557
- "type": "object",
6558
- "properties": {
6559
- "type": {
6560
- "type": "string",
6561
- "const": "state_equals"
6562
- },
6563
- "key": {
6564
- "type": "string",
6565
- "description": "Key in the SDK persistent state store (localStorage). Only valid for keys the host app explicitly sets via syntro.state.set()."
6566
- },
6567
- "value": {
6568
- "description": "Expected value to match against"
6569
- }
6570
- },
6571
- "required": [
6572
- "type",
6573
- "key"
6574
- ],
6575
- "additionalProperties": false,
6576
- "description": "Checks the SDK persistent state store (localStorage). ONLY for host-app state set via syntro.state.set() — NOT for user attributes like region, device, or UTM params (those are handled by segment targeting). Do NOT use this for targeting. If you do not know the valid state keys, do not use this condition type."
6577
- },
6578
- {
6579
- "type": "object",
6580
- "properties": {
6581
- "type": {
6582
- "type": "string",
6583
- "const": "viewport"
6584
- },
6585
- "minWidth": {
6586
- "type": "number",
6587
- "description": "Minimum viewport width in pixels"
6588
- },
6589
- "maxWidth": {
6590
- "type": "number",
6591
- "description": "Maximum viewport width in pixels"
6592
- },
6593
- "minHeight": {
6594
- "type": "number",
6595
- "description": "Minimum viewport height in pixels"
6596
- },
6597
- "maxHeight": {
6598
- "type": "number",
6599
- "description": "Maximum viewport height in pixels"
6600
- }
6601
- },
6602
- "required": [
6603
- "type"
6604
- ],
6605
- "additionalProperties": false,
6606
- "description": "Fires based on viewport (screen) size. Use for responsive behavior. Example: {\"type\": \"viewport\", \"minWidth\": 768} — fires on tablet and larger."
6607
- },
6608
- {
6609
- "type": "object",
6610
- "properties": {
6611
- "type": {
6612
- "type": "string",
6613
- "const": "session_metric"
6614
- },
6615
- "key": {
6616
- "type": "string",
6617
- "enum": [
6618
- "time_on_page",
6619
- "page_views",
6620
- "scroll_depth"
6621
- ],
6622
- "description": "Session metric key. time_on_page = seconds on current page, page_views = pages visited this session, scroll_depth = 0-100 percentage."
6623
- },
6624
- "operator": {
6625
- "type": "string",
6626
- "enum": [
6627
- "gte",
6628
- "lte",
6629
- "eq",
6630
- "gt",
6631
- "lt"
6632
- ]
6633
- },
6634
- "threshold": {
6635
- "type": "number",
6636
- "description": "Numeric threshold to compare against"
6637
- }
6638
- },
6639
- "required": [
6640
- "type",
6641
- "key",
6642
- "operator",
6643
- "threshold"
6644
- ],
6645
- "additionalProperties": false,
6646
- "description": "Fires when a session metric crosses a threshold. Valid keys: \"time_on_page\" (seconds), \"page_views\" (count), \"scroll_depth\" (0-100). Example: {\"type\": \"session_metric\", \"key\": \"time_on_page\", \"operator\": \"gte\", \"threshold\": 30}"
6647
- },
6648
- {
6649
- "type": "object",
6650
- "properties": {
6651
- "type": {
6652
- "type": "string",
6653
- "const": "dismissed"
6654
- },
6655
- "key": {
6656
- "type": "string",
6657
- "description": "Dismissal key (usually a tile or action ID)"
6658
- },
6659
- "inverted": {
6660
- "type": "boolean",
6661
- "description": "When true, fires if NOT dismissed (default behavior)"
6662
- }
6663
- },
6664
- "required": [
6665
- "type",
6666
- "key"
6667
- ],
6668
- "additionalProperties": false,
6669
- "description": "Checks if an item has been dismissed by the user. Use with inverted: true to show only if not dismissed."
6670
- },
6671
- {
6672
- "type": "object",
6673
- "properties": {
6674
- "type": {
6675
- "type": "string",
6676
- "const": "cooldown_active"
6677
- },
6678
- "key": {
6679
- "type": "string",
6680
- "description": "Cooldown key"
6681
- },
6682
- "inverted": {
6683
- "type": "boolean",
6684
- "description": "When true, fires if cooldown is NOT active"
6685
- }
6686
- },
6687
- "required": [
6688
- "type",
6689
- "key"
6690
- ],
6691
- "additionalProperties": false,
6692
- "description": "Checks if a cooldown timer is currently active. Use to prevent showing the same intervention too frequently."
6693
- },
6694
- {
6695
- "type": "object",
6696
- "properties": {
6697
- "type": {
6698
- "type": "string",
6699
- "const": "frequency_limit"
6700
- },
6701
- "key": {
6702
- "type": "string",
6703
- "description": "Frequency counter key"
6704
- },
6705
- "limit": {
6706
- "type": "number",
6707
- "description": "Maximum allowed count"
6708
- },
6709
- "inverted": {
6710
- "type": "boolean",
6711
- "description": "When true, fires if limit NOT reached"
6712
- }
6713
- },
6714
- "required": [
6715
- "type",
6716
- "key",
6717
- "limit"
6718
- ],
6719
- "additionalProperties": false,
6720
- "description": "Checks if a frequency limit has been reached. Use to cap how many times an action fires per session."
6721
- },
6722
- {
6723
- "type": "object",
6724
- "properties": {
6725
- "type": {
6726
- "type": "string",
6727
- "const": "event_count"
6728
- },
6729
- "key": {
6730
- "type": "string",
6731
- "description": "Unique key for this counter (used for accumulator registration)"
6732
- },
6733
- "operator": {
6734
- "type": "string",
6735
- "enum": [
6736
- "gte",
6737
- "lte",
6738
- "eq",
6739
- "gt",
6740
- "lt"
6741
- ]
6742
- },
6743
- "count": {
6744
- "type": "integer",
6745
- "minimum": 0,
6746
- "description": "Target count threshold"
6747
- },
6748
- "withinMs": {
6749
- "type": "number",
6750
- "exclusiveMinimum": 0,
6751
- "description": "Time window in ms. Omit = count across entire session."
6752
- },
6753
- "counter": {
6754
- "type": "object",
6755
- "properties": {
6756
- "events": {
6757
- "type": "array",
6758
- "items": {
6759
- "type": "string",
6760
- "enum": [
6761
- "ui.click",
6762
- "ui.scroll",
6763
- "ui.input",
6764
- "ui.change",
6765
- "ui.submit",
6766
- "ui.hover",
6767
- "ui.idle",
6768
- "ui.scroll_thrash",
6769
- "ui.focus_bounce",
6770
- "nav.page_view",
6771
- "nav.page_leave",
6772
- "behavior.rage_click",
6773
- "behavior.hesitation",
6774
- "behavior.confusion"
6775
- ],
6776
- "description": "Event name to count. ui.* = user interactions and behavioral detectors, nav.* = page navigation, behavior.* = derived behavioral signals."
6777
- },
6778
- "minItems": 1,
6779
- "description": "Event names to count. Use values from the countable events enum."
6780
- },
6781
- "match": {
6782
- "type": "object",
6783
- "additionalProperties": {
6784
- "type": "object",
6785
- "properties": {
6786
- "equals": {
6787
- "type": [
6788
- "string",
6789
- "number",
6790
- "boolean"
6791
- ]
6792
- },
6793
- "contains": {
6794
- "type": "string"
6795
- }
6796
- },
6797
- "additionalProperties": false,
6798
- "description": "Match operator for counter filters. Exactly one of equals or contains must be specified."
6799
- },
6800
- "description": "Property filters. Keys are event prop names or element-chain fields (tag_name, $el_text, attr__*). All entries AND together."
6801
- }
6802
- },
6803
- "required": [
6804
- "events"
6805
- ],
6806
- "additionalProperties": false,
6807
- "description": "Inline counter definition. Defines what events to count."
6808
- }
6809
- },
6810
- "required": [
6811
- "type",
6812
- "key",
6813
- "operator",
6814
- "count"
6815
- ],
6816
- "additionalProperties": false,
6817
- "description": "Fires when accumulated event count crosses a threshold. Most powerful trigger type. Example: {\"type\": \"event_count\", \"key\": \"pricing-clicks\", \"operator\": \"gte\", \"count\": 3, \"counter\": {\"events\": [\"ui.click\"], \"match\": {\"attr__data-cta\": {\"contains\": \"pricing\"}}}}"
6818
- }
6819
- ]
6820
- },
6821
- "description": "Array of conditions — ALL must match (AND logic) for this rule to fire."
6822
- },
6823
- "value": {
6824
- "description": "Value returned when all conditions match. For triggerWhen: true = fire the action."
6825
- }
6826
- },
6827
- "required": [
6828
- "conditions"
6829
- ],
6830
- "additionalProperties": false,
6831
- "description": "A single rule. ALL conditions must match (AND logic). Rules in a strategy are evaluated top-to-bottom — first rule where all conditions match wins and returns its value."
6832
- },
6833
- "description": "Ordered list of rules. Evaluated top-to-bottom — first match wins."
6834
- },
6835
- "default": {
6836
- "description": "Fallback value when no rule matches. For triggerWhen: false = do not fire by default."
6837
- }
6838
- },
6839
- "required": [
6840
- "type",
6841
- "rules"
6842
- ],
6843
- "additionalProperties": false,
6844
- "description": "Rule-based strategy. Evaluates rules top-to-bottom. First rule where ALL conditions match returns its value. If no rule matches, returns default. For triggerWhen: set value=true on matching rules, default=false."
6845
- },
6846
- {
6847
- "type": "object",
6848
- "properties": {
6849
- "type": {
6850
- "type": "string",
6851
- "const": "score"
6852
- },
6853
- "field": {
6854
- "type": "string"
6855
- },
6856
- "threshold": {
6857
- "type": "number"
6858
- },
6859
- "above": {},
6860
- "below": {}
6861
- },
6862
- "required": [
6863
- "type",
6864
- "field",
6865
- "threshold"
6866
- ],
6867
- "additionalProperties": false,
6868
- "description": "Score-based strategy. Compares a field value against a threshold."
6869
- },
6870
- {
6871
- "type": "object",
6872
- "properties": {
6873
- "type": {
6874
- "type": "string",
6875
- "const": "model"
6876
- },
6877
- "modelId": {
6878
- "type": "string"
6879
- },
6880
- "inputs": {
6881
- "type": "array",
6882
- "items": {
6883
- "type": "string"
6884
- }
6885
- },
6886
- "outputMapping": {
6887
- "type": "object",
6888
- "additionalProperties": {}
6889
- },
6890
- "default": {}
6891
- },
6892
- "required": [
6893
- "type",
6894
- "modelId",
6895
- "inputs",
6896
- "outputMapping"
6897
- ],
6898
- "additionalProperties": false,
6899
- "description": "ML model strategy. Sends inputs to a model and maps outputs."
6900
- },
6901
- {
6902
- "type": "object",
6903
- "properties": {
6904
- "type": {
6905
- "type": "string",
6906
- "const": "external"
6907
- },
6908
- "endpoint": {
6909
- "type": "string"
6910
- },
6911
- "method": {
6912
- "type": "string",
6913
- "enum": [
6914
- "GET",
6915
- "POST"
6916
- ]
6917
- },
6918
- "default": {},
6919
- "timeoutMs": {
6920
- "type": "number"
6921
- }
6922
- },
6923
- "required": [
6924
- "type",
6925
- "endpoint"
6926
- ],
6927
- "additionalProperties": false,
6928
- "description": "External API strategy. Calls an endpoint to determine the value."
6929
- }
6930
- ]
4538
+ "$ref": "#/$defs/decisionStrategy"
6931
4539
  },
6932
4540
  {
6933
4541
  "type": "null"