@feastalytics/cli 0.1.11 → 0.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -8983,7 +8983,7 @@ var CLI_MANIFEST = {
8983
8983
  {
8984
8984
  "id": "applyFunnelTemplate",
8985
8985
  "domain": "campaigns",
8986
- "description": "Applies a funnel template to an existing campaign that has no funnel set up yet (override.initialScreenId is null). Uses the campaign's existing promotion/imageUrl when the funnel template needs them. For reservation-prepay funnels pass funnelType: 'prepay' AND isReservationFunnel: true \u2014 it is not inferred from funnelType. skipSignUp: true builds the funnel without the sign-up screen.",
8986
+ "description": "Applies a funnel template to an existing campaign that has no funnel set up yet (override.initialScreenId is null). Pass a templateId from listFunnelTemplates: offer-basic (Sign Up routes straight to the offer wallet \u2014 no payment step; use this for offers redeemed in person), offer-prepay / offer-direct-prepay (Sign Up / landing routes to a Stripe payment screen), reservation-offer-basic (Sign Up routes to reservation-or-wallet), reservation-offer-prepay / reservation-offer-direct-prepay (payment then reservation), reservation-only. The promotion's canPrePay flag does NOT change what gets built \u2014 prepay templates always insert the payment screen, and are rejected unless the promotion has canPrePay: true and a price. The legacy funnelType/skipSignUp/isReservationFunnel input is deprecated but still accepted.",
8987
8987
  "type": "mutation",
8988
8988
  "path": [
8989
8989
  "api",
@@ -8992,31 +8992,60 @@ var CLI_MANIFEST = {
8992
8992
  "chooseFunnelTemplate"
8993
8993
  ],
8994
8994
  "inputJsonSchema": {
8995
- "type": "object",
8996
- "properties": {
8997
- "campaignId": {
8998
- "type": "string"
8999
- },
9000
- "funnelType": {
9001
- "type": "string",
9002
- "enum": [
9003
- "reservation",
9004
- "prepay",
9005
- "simpleRewards"
9006
- ]
9007
- },
9008
- "skipSignUp": {
9009
- "type": "boolean"
8995
+ "anyOf": [
8996
+ {
8997
+ "type": "object",
8998
+ "properties": {
8999
+ "campaignId": {
9000
+ "type": "string"
9001
+ },
9002
+ "templateId": {
9003
+ "type": "string",
9004
+ "enum": [
9005
+ "offer-basic",
9006
+ "offer-prepay",
9007
+ "offer-direct-prepay",
9008
+ "reservation-offer-basic",
9009
+ "reservation-offer-prepay",
9010
+ "reservation-offer-direct-prepay",
9011
+ "reservation-only"
9012
+ ]
9013
+ }
9014
+ },
9015
+ "required": [
9016
+ "campaignId",
9017
+ "templateId"
9018
+ ],
9019
+ "additionalProperties": false
9010
9020
  },
9011
- "isReservationFunnel": {
9012
- "type": "boolean"
9021
+ {
9022
+ "type": "object",
9023
+ "properties": {
9024
+ "campaignId": {
9025
+ "type": "string"
9026
+ },
9027
+ "funnelType": {
9028
+ "type": "string",
9029
+ "enum": [
9030
+ "reservation",
9031
+ "prepay",
9032
+ "simpleRewards"
9033
+ ]
9034
+ },
9035
+ "skipSignUp": {
9036
+ "type": "boolean"
9037
+ },
9038
+ "isReservationFunnel": {
9039
+ "type": "boolean"
9040
+ }
9041
+ },
9042
+ "required": [
9043
+ "campaignId",
9044
+ "funnelType"
9045
+ ],
9046
+ "additionalProperties": false
9013
9047
  }
9014
- },
9015
- "required": [
9016
- "campaignId",
9017
- "funnelType"
9018
9048
  ],
9019
- "additionalProperties": false,
9020
9049
  "$schema": "http://json-schema.org/draft-07/schema#"
9021
9050
  }
9022
9051
  },
@@ -13832,7 +13861,7 @@ var CLI_MANIFEST = {
13832
13861
  {
13833
13862
  "id": "getCreatorConversation",
13834
13863
  "domain": "creators",
13835
- "description": "One creator's full SMS thread, newest first \u2014 what the dashboard's creator chat view renders. userId comes from listCreatorConversations, which is the queue; this is the read you make before summarizing an exchange or drafting a reply for the human to send, because the queue only carries the last message. Each row has the body, direction (from/to the creator's number), and timestamps. Returns empty when the creator has no phone number on file. Read-only: sending the reply and marking the thread read stay in the dashboard.",
13864
+ "description": "One creator's full SMS thread, newest first \u2014 what the dashboard's creator chat view renders. userId comes from listCreatorConversations, which is the queue; this is the read you make before summarizing an exchange or drafting a reply for the human to send, because the queue only carries the last message. Each row has the body, direction (from/to the creator's number), and timestamps. Returns empty when the creator has no phone number on file. Read-only: send the reply with sendText and {type:'creator', userId}; marking the thread read stays in the dashboard.",
13836
13865
  "type": "query",
13837
13866
  "path": [
13838
13867
  "api",
@@ -13976,7 +14005,7 @@ var CLI_MANIFEST = {
13976
14005
  {
13977
14006
  "id": "getMemberConversation",
13978
14007
  "domain": "membersProgram",
13979
- "description": "One member's SMS thread and activity, newest first \u2014 what the dashboard's chat page renders, and the pair to searchUsers the way getCreatorConversation pairs with listCreatorConversations. serialNumber comes from searchUsers. eventTypes: ['sentText','receivedText'] is the conversation; adding scan, order, checkout, rewardAwarded or rewardRedeemed interleaves what happened between the messages. Unfiltered it fans out to every event source and returns the member's whole history unpaginated, so pass eventTypes unless you really want it all. Read-only: replying to a guest stays in the dashboard.",
14008
+ "description": "One member's SMS thread and activity, newest first \u2014 what the dashboard's chat page renders, and the pair to searchUsers the way getCreatorConversation pairs with listCreatorConversations. serialNumber comes from searchUsers. eventTypes: ['sentText','receivedText'] is the conversation; adding scan, order, checkout, rewardAwarded or rewardRedeemed interleaves what happened between the messages. Unfiltered it fans out to every event source and returns the member's whole history unpaginated, so pass eventTypes unless you really want it all. Read-only: reply to the member with sendText and {type:'guest', serialNumber}.",
13980
14009
  "type": "query",
13981
14010
  "path": [
13982
14011
  "api",
@@ -14452,7 +14481,7 @@ var CLI_MANIFEST = {
14452
14481
  {
14453
14482
  "id": "listCreatorConversations",
14454
14483
  "domain": "creators",
14455
- "description": "Every creator's SMS conversation with its unread state \u2014 the 'who is waiting on a reply' queue. `hasUnread` means their last message came in after ours and nobody has marked it read; those need a human. Each row carries the last message body, time and direction, the creator's handles, and `visitStatus`, a derived stage that is more reliable than reading raw columns off creatorVisitApplication. Read the full thread behind a row with getCreatorConversation and its userId. Read-only: replying to a creator and marking a conversation read both stay in the dashboard.",
14484
+ "description": "Every creator's SMS conversation with its unread state \u2014 the 'who is waiting on a reply' queue. `hasUnread` means their last message came in after ours and nobody has marked it read; those need a human. Each row carries the last message body, time and direction, the creator's handles, and `visitStatus`, a derived stage that is more reliable than reading raw columns off creatorVisitApplication. Read the full thread behind a row with getCreatorConversation and its userId, and reply with sendText and {type:'creator', userId}. Read-only: marking a conversation read stays in the dashboard.",
14456
14485
  "type": "query",
14457
14486
  "path": [
14458
14487
  "api",
@@ -14464,7 +14493,7 @@ var CLI_MANIFEST = {
14464
14493
  {
14465
14494
  "id": "listCreatorSubmissions",
14466
14495
  "domain": "creators",
14467
- "description": "Creator content submissions, newest first. 'submitted' and 'revision_requested' together are the review queue. Each row carries the `submissionId` for decideCreatorSubmission. Submissions are outside the queryData catalog, so this is the only way to read them.",
14496
+ "description": "Creator content submissions, newest first. 'submitted' is the review queue; 'revision_requested' is waiting on the creator to resubmit. Each row carries the `submissionId` for decideCreatorSubmission. Submissions are outside the queryData catalog, so this is the only way to read them.",
14468
14497
  "type": "query",
14469
14498
  "path": [
14470
14499
  "api",
@@ -14567,6 +14596,31 @@ var CLI_MANIFEST = {
14567
14596
  "$schema": "http://json-schema.org/draft-07/schema#"
14568
14597
  }
14569
14598
  },
14599
+ {
14600
+ "id": "listFunnelTemplates",
14601
+ "domain": "campaigns",
14602
+ "description": "Lists the funnel templates that can be applied to a campaign, with per-template eligibility for this campaign and a recommended template. Each template describes the guest journey (ordered screens) and whether it collects payment. Prepay templates insert a Stripe payment screen and are only eligible when the campaign's promotion has canPrePay: true and a price; the promotion's canPrePay flag does NOT change what a template builds \u2014 pick a non-payment template (offer-basic) for offers redeemed in person. Use before applyFunnelTemplate.",
14603
+ "type": "query",
14604
+ "path": [
14605
+ "api",
14606
+ "campaigns",
14607
+ "app",
14608
+ "listFunnelTemplates"
14609
+ ],
14610
+ "inputJsonSchema": {
14611
+ "type": "object",
14612
+ "properties": {
14613
+ "campaignId": {
14614
+ "type": "string"
14615
+ }
14616
+ },
14617
+ "required": [
14618
+ "campaignId"
14619
+ ],
14620
+ "additionalProperties": false,
14621
+ "$schema": "http://json-schema.org/draft-07/schema#"
14622
+ }
14623
+ },
14570
14624
  {
14571
14625
  "id": "listIgMedia",
14572
14626
  "domain": "ads",
@@ -15387,6 +15441,98 @@ Example - opted-in members with more than 5 visits, newest first:
15387
15441
  "$schema": "http://json-schema.org/draft-07/schema#"
15388
15442
  }
15389
15443
  },
15444
+ {
15445
+ "id": "sendText",
15446
+ "domain": "messaging",
15447
+ "description": "Send one SMS to one person from the organization's texting number \u2014 the reply you would otherwise type into the dashboard chat. `to` names who by id, never by phone number: {type:'creator', userId} for a creator, whose userId comes from listCreatorConversations or getCreatorConversation, or {type:'guest', serialNumber} for a loyalty member, whose serialNumber comes from searchUsers or getMemberConversation. The type is not cosmetic \u2014 the two are different people in different tables, and a creator who also holds a pass exists in both, so pass the type that matches the thread you are replying to. The third form, {type:'unknownSender', phoneNumber}, answers someone who texted in without being either \u2014 it is the only form that names a raw number, and it is refused unless that number has an inbound message to this organization on file. A creator must have a visit with this organization and a guest must belong to it, or the call is a 404 rather than a text to a stranger. Guest messages support the {{firstName}}-style handlebars text automations use and are rendered before sending; creator messages are sent verbatim. Sending as a creator's human handler also parks that creator's AI agent for five minutes so it does not talk over you. High priority, sent immediately \u2014 there is no scheduling, no undo, and no bulk form: call it once per recipient.",
15448
+ "type": "mutation",
15449
+ "path": [
15450
+ "api",
15451
+ "text",
15452
+ "sendMessage"
15453
+ ],
15454
+ "inputJsonSchema": {
15455
+ "type": "object",
15456
+ "properties": {
15457
+ "to": {
15458
+ "anyOf": [
15459
+ {
15460
+ "type": "object",
15461
+ "properties": {
15462
+ "type": {
15463
+ "type": "string",
15464
+ "const": "creator"
15465
+ },
15466
+ "userId": {
15467
+ "type": "string",
15468
+ "minLength": 1
15469
+ }
15470
+ },
15471
+ "required": [
15472
+ "type",
15473
+ "userId"
15474
+ ],
15475
+ "additionalProperties": false
15476
+ },
15477
+ {
15478
+ "type": "object",
15479
+ "properties": {
15480
+ "type": {
15481
+ "type": "string",
15482
+ "const": "guest"
15483
+ },
15484
+ "serialNumber": {
15485
+ "type": "string",
15486
+ "minLength": 1
15487
+ }
15488
+ },
15489
+ "required": [
15490
+ "type",
15491
+ "serialNumber"
15492
+ ],
15493
+ "additionalProperties": false
15494
+ },
15495
+ {
15496
+ "type": "object",
15497
+ "properties": {
15498
+ "type": {
15499
+ "type": "string",
15500
+ "const": "unknownSender"
15501
+ },
15502
+ "phoneNumber": {
15503
+ "type": "string",
15504
+ "minLength": 1
15505
+ }
15506
+ },
15507
+ "required": [
15508
+ "type",
15509
+ "phoneNumber"
15510
+ ],
15511
+ "additionalProperties": false
15512
+ }
15513
+ ]
15514
+ },
15515
+ "message": {
15516
+ "type": "string",
15517
+ "minLength": 1,
15518
+ "maxLength": 1600
15519
+ },
15520
+ "mediaUrls": {
15521
+ "type": "array",
15522
+ "items": {
15523
+ "type": "string"
15524
+ },
15525
+ "maxItems": 10
15526
+ }
15527
+ },
15528
+ "required": [
15529
+ "to",
15530
+ "message"
15531
+ ],
15532
+ "additionalProperties": false,
15533
+ "$schema": "http://json-schema.org/draft-07/schema#"
15534
+ }
15535
+ },
15390
15536
  {
15391
15537
  "id": "setAdCampaignStatus",
15392
15538
  "domain": "ads",
@@ -15424,7 +15570,7 @@ Example - opted-in members with more than 5 visits, newest first:
15424
15570
  {
15425
15571
  "id": "simulateAutomations",
15426
15572
  "domain": "automations",
15427
- "description": "Dry-run automations against a synthetic event timeline and see what would fire \u2014 no real sends or side effects. First call: omit `events` and pass `flowId`, and the server auto-seeds a timeline from that flow's triggers (a viewCampaign event when the flow has a campaign, then the first eligible trigger event 15s later; receiveAutomation, reply and viewCampaign are never seeded) and returns it as `eventsUsed`. To branch-test or continue the journey, copy `eventsUsed`, edit or append to it, and pass the whole array back as `events`. Either `events` or `flowId` is required. Pass a draft's operations as `edits` to preview unsaved changes. Returns `scheduledTexts` plus `eventsUsed`.",
15573
+ "description": "Dry-run automations against a synthetic event timeline and see what would fire \u2014 no real sends or side effects. Each event is `{type, at}` plus a few optional fields; `at` is an ISO 8601 timestamp and the server fills in the guest, organization and campaign. Valid types: signUp, importedCustomer, viewCampaign, addPass, visit, invalidScan, checkout, reply, buttonClick, offerRedemption, offerExpiration, formSubmission, formPropertySubmission, gotReferred, referred, subscriptionRenewal, receiveAutomation. First call: omit `events` and pass `flowId`, and the server auto-seeds a timeline from that flow's triggers (a viewCampaign event when the flow has a campaign, then the first eligible trigger event 15s later) and returns it as `eventsUsed`. To test another day or continue the journey, change `at` on those events or append more, and pass the array back as `events`. Either `events` or `flowId` is required. Pass a draft's operations as `edits` to preview unsaved changes. Returns `scheduledTexts` plus `eventsUsed`.",
15428
15574
  "type": "mutation",
15429
15575
  "path": [
15430
15576
  "api",
@@ -15474,8 +15620,413 @@ Example - opted-in members with more than 5 visits, newest first:
15474
15620
  "events": {
15475
15621
  "type": "array",
15476
15622
  "items": {
15477
- "type": "object",
15478
- "additionalProperties": {}
15623
+ "anyOf": [
15624
+ {
15625
+ "type": "object",
15626
+ "properties": {
15627
+ "type": {
15628
+ "type": "string",
15629
+ "const": "signUp"
15630
+ },
15631
+ "at": {
15632
+ "type": "string",
15633
+ "format": "date-time",
15634
+ "description": "When the event happens, as an ISO 8601 timestamp"
15635
+ }
15636
+ },
15637
+ "required": [
15638
+ "type",
15639
+ "at"
15640
+ ],
15641
+ "additionalProperties": false,
15642
+ "description": "The guest joins the loyalty program"
15643
+ },
15644
+ {
15645
+ "type": "object",
15646
+ "properties": {
15647
+ "type": {
15648
+ "type": "string",
15649
+ "const": "importedCustomer"
15650
+ },
15651
+ "at": {
15652
+ "$ref": "#/properties/events/items/anyOf/0/properties/at"
15653
+ }
15654
+ },
15655
+ "required": [
15656
+ "type",
15657
+ "at"
15658
+ ],
15659
+ "additionalProperties": false,
15660
+ "description": "The guest is added by an import rather than signing up"
15661
+ },
15662
+ {
15663
+ "type": "object",
15664
+ "properties": {
15665
+ "type": {
15666
+ "type": "string",
15667
+ "const": "viewCampaign"
15668
+ },
15669
+ "at": {
15670
+ "$ref": "#/properties/events/items/anyOf/0/properties/at"
15671
+ },
15672
+ "campaignId": {
15673
+ "type": "string"
15674
+ }
15675
+ },
15676
+ "required": [
15677
+ "type",
15678
+ "at"
15679
+ ],
15680
+ "additionalProperties": false,
15681
+ "description": "The guest opens a campaign page. Defaults to the flow's campaign"
15682
+ },
15683
+ {
15684
+ "type": "object",
15685
+ "properties": {
15686
+ "type": {
15687
+ "type": "string",
15688
+ "const": "addPass"
15689
+ },
15690
+ "at": {
15691
+ "$ref": "#/properties/events/items/anyOf/0/properties/at"
15692
+ },
15693
+ "platform": {
15694
+ "type": "string",
15695
+ "enum": [
15696
+ "apple",
15697
+ "google"
15698
+ ]
15699
+ }
15700
+ },
15701
+ "required": [
15702
+ "type",
15703
+ "at"
15704
+ ],
15705
+ "additionalProperties": false,
15706
+ "description": "The guest adds their pass to a wallet"
15707
+ },
15708
+ {
15709
+ "type": "object",
15710
+ "properties": {
15711
+ "type": {
15712
+ "type": "string",
15713
+ "const": "visit"
15714
+ },
15715
+ "at": {
15716
+ "$ref": "#/properties/events/items/anyOf/0/properties/at"
15717
+ },
15718
+ "locationId": {
15719
+ "type": "string"
15720
+ }
15721
+ },
15722
+ "required": [
15723
+ "type",
15724
+ "at"
15725
+ ],
15726
+ "additionalProperties": false,
15727
+ "description": "The guest scans their pass at a location"
15728
+ },
15729
+ {
15730
+ "type": "object",
15731
+ "properties": {
15732
+ "type": {
15733
+ "type": "string",
15734
+ "const": "invalidScan"
15735
+ },
15736
+ "at": {
15737
+ "$ref": "#/properties/events/items/anyOf/0/properties/at"
15738
+ }
15739
+ },
15740
+ "required": [
15741
+ "type",
15742
+ "at"
15743
+ ],
15744
+ "additionalProperties": false,
15745
+ "description": "The guest scans their pass but the scan does not count"
15746
+ },
15747
+ {
15748
+ "type": "object",
15749
+ "properties": {
15750
+ "type": {
15751
+ "type": "string",
15752
+ "const": "checkout"
15753
+ },
15754
+ "at": {
15755
+ "$ref": "#/properties/events/items/anyOf/0/properties/at"
15756
+ },
15757
+ "campaignId": {
15758
+ "type": "string"
15759
+ },
15760
+ "promoId": {
15761
+ "type": "string"
15762
+ },
15763
+ "amountTotal": {
15764
+ "type": "number"
15765
+ }
15766
+ },
15767
+ "required": [
15768
+ "type",
15769
+ "at"
15770
+ ],
15771
+ "additionalProperties": false,
15772
+ "description": "The guest prepays for an offer"
15773
+ },
15774
+ {
15775
+ "type": "object",
15776
+ "properties": {
15777
+ "type": {
15778
+ "type": "string",
15779
+ "const": "reply"
15780
+ },
15781
+ "at": {
15782
+ "$ref": "#/properties/events/items/anyOf/0/properties/at"
15783
+ },
15784
+ "body": {
15785
+ "type": "string"
15786
+ }
15787
+ },
15788
+ "required": [
15789
+ "type",
15790
+ "at"
15791
+ ],
15792
+ "additionalProperties": false,
15793
+ "description": "The guest texts back. `body` is what reply keywords match on"
15794
+ },
15795
+ {
15796
+ "type": "object",
15797
+ "properties": {
15798
+ "type": {
15799
+ "type": "string",
15800
+ "const": "buttonClick"
15801
+ },
15802
+ "at": {
15803
+ "$ref": "#/properties/events/items/anyOf/0/properties/at"
15804
+ },
15805
+ "eventName": {
15806
+ "type": "string"
15807
+ },
15808
+ "buttonId": {
15809
+ "type": "string"
15810
+ }
15811
+ },
15812
+ "required": [
15813
+ "type",
15814
+ "at"
15815
+ ],
15816
+ "additionalProperties": false,
15817
+ "description": "The guest clicks a tracked button"
15818
+ },
15819
+ {
15820
+ "type": "object",
15821
+ "properties": {
15822
+ "type": {
15823
+ "type": "string",
15824
+ "const": "offerRedemption"
15825
+ },
15826
+ "at": {
15827
+ "$ref": "#/properties/events/items/anyOf/0/properties/at"
15828
+ },
15829
+ "rewardId": {
15830
+ "type": "string"
15831
+ }
15832
+ },
15833
+ "required": [
15834
+ "type",
15835
+ "at"
15836
+ ],
15837
+ "additionalProperties": false,
15838
+ "description": "A reward is awarded to the guest"
15839
+ },
15840
+ {
15841
+ "type": "object",
15842
+ "properties": {
15843
+ "type": {
15844
+ "type": "string",
15845
+ "const": "offerExpiration"
15846
+ },
15847
+ "at": {
15848
+ "$ref": "#/properties/events/items/anyOf/0/properties/at"
15849
+ },
15850
+ "rewardId": {
15851
+ "type": "string"
15852
+ },
15853
+ "expirationDate": {
15854
+ "type": "string",
15855
+ "format": "date-time"
15856
+ }
15857
+ },
15858
+ "required": [
15859
+ "type",
15860
+ "at"
15861
+ ],
15862
+ "additionalProperties": false,
15863
+ "description": "A reward expires"
15864
+ },
15865
+ {
15866
+ "type": "object",
15867
+ "properties": {
15868
+ "type": {
15869
+ "type": "string",
15870
+ "const": "formSubmission"
15871
+ },
15872
+ "at": {
15873
+ "$ref": "#/properties/events/items/anyOf/0/properties/at"
15874
+ },
15875
+ "propertyId": {
15876
+ "type": "string"
15877
+ },
15878
+ "formId": {
15879
+ "type": "string"
15880
+ },
15881
+ "booleanValue": {
15882
+ "type": "boolean"
15883
+ },
15884
+ "numberValue": {
15885
+ "type": "number"
15886
+ },
15887
+ "stringValue": {
15888
+ "type": "string"
15889
+ },
15890
+ "localDayOfYear": {
15891
+ "type": "string"
15892
+ },
15893
+ "isDateValue": {
15894
+ "type": "string",
15895
+ "format": "date-time"
15896
+ }
15897
+ },
15898
+ "required": [
15899
+ "type",
15900
+ "at"
15901
+ ],
15902
+ "additionalProperties": false,
15903
+ "description": "The guest submits a form property"
15904
+ },
15905
+ {
15906
+ "type": "object",
15907
+ "properties": {
15908
+ "type": {
15909
+ "type": "string",
15910
+ "const": "formPropertySubmission"
15911
+ },
15912
+ "at": {
15913
+ "$ref": "#/properties/events/items/anyOf/0/properties/at"
15914
+ },
15915
+ "propertyId": {
15916
+ "$ref": "#/properties/events/items/anyOf/11/properties/propertyId"
15917
+ },
15918
+ "formId": {
15919
+ "$ref": "#/properties/events/items/anyOf/11/properties/formId"
15920
+ },
15921
+ "booleanValue": {
15922
+ "$ref": "#/properties/events/items/anyOf/11/properties/booleanValue"
15923
+ },
15924
+ "numberValue": {
15925
+ "$ref": "#/properties/events/items/anyOf/11/properties/numberValue"
15926
+ },
15927
+ "stringValue": {
15928
+ "$ref": "#/properties/events/items/anyOf/11/properties/stringValue"
15929
+ },
15930
+ "localDayOfYear": {
15931
+ "$ref": "#/properties/events/items/anyOf/11/properties/localDayOfYear"
15932
+ },
15933
+ "isDateValue": {
15934
+ "$ref": "#/properties/events/items/anyOf/11/properties/isDateValue"
15935
+ }
15936
+ },
15937
+ "required": [
15938
+ "type",
15939
+ "at"
15940
+ ],
15941
+ "additionalProperties": false,
15942
+ "description": "The guest submits a form property, evaluated as a condition"
15943
+ },
15944
+ {
15945
+ "type": "object",
15946
+ "properties": {
15947
+ "type": {
15948
+ "type": "string",
15949
+ "const": "gotReferred"
15950
+ },
15951
+ "at": {
15952
+ "$ref": "#/properties/events/items/anyOf/0/properties/at"
15953
+ },
15954
+ "referrerSerialNumber": {
15955
+ "type": "string"
15956
+ }
15957
+ },
15958
+ "required": [
15959
+ "type",
15960
+ "at"
15961
+ ],
15962
+ "additionalProperties": false,
15963
+ "description": "The guest signed up from someone else's referral link"
15964
+ },
15965
+ {
15966
+ "type": "object",
15967
+ "properties": {
15968
+ "type": {
15969
+ "type": "string",
15970
+ "const": "referred"
15971
+ },
15972
+ "at": {
15973
+ "$ref": "#/properties/events/items/anyOf/0/properties/at"
15974
+ },
15975
+ "referrerSerialNumber": {
15976
+ "type": "string"
15977
+ }
15978
+ },
15979
+ "required": [
15980
+ "type",
15981
+ "at"
15982
+ ],
15983
+ "additionalProperties": false,
15984
+ "description": "Someone else signed up from the guest's referral link"
15985
+ },
15986
+ {
15987
+ "type": "object",
15988
+ "properties": {
15989
+ "type": {
15990
+ "type": "string",
15991
+ "const": "subscriptionRenewal"
15992
+ },
15993
+ "at": {
15994
+ "$ref": "#/properties/events/items/anyOf/0/properties/at"
15995
+ }
15996
+ },
15997
+ "required": [
15998
+ "type",
15999
+ "at"
16000
+ ],
16001
+ "additionalProperties": false,
16002
+ "description": "The guest's subscription renews"
16003
+ },
16004
+ {
16005
+ "type": "object",
16006
+ "properties": {
16007
+ "type": {
16008
+ "type": "string",
16009
+ "const": "receiveAutomation"
16010
+ },
16011
+ "at": {
16012
+ "$ref": "#/properties/events/items/anyOf/0/properties/at"
16013
+ },
16014
+ "automationId": {
16015
+ "type": "string"
16016
+ },
16017
+ "campaignId": {
16018
+ "type": "string"
16019
+ }
16020
+ },
16021
+ "required": [
16022
+ "type",
16023
+ "at",
16024
+ "automationId"
16025
+ ],
16026
+ "additionalProperties": false,
16027
+ "description": "The guest already received an automation. The simulation generates these itself as texts send, so only pass one to replay history"
16028
+ }
16029
+ ]
15479
16030
  }
15480
16031
  },
15481
16032
  "serialNumber": {
package/feast/SKILL.md CHANGED
@@ -9,41 +9,7 @@ Drive the Feastalytics platform from the terminal. The `feast` CLI exposes the s
9
9
 
10
10
  The CLI is the source of truth for *which* tools exist and *what* they accept — always discover that at runtime rather than assuming, because the tool set grows as new endpoints are tagged. Your job is to pick the right tool, scope it to the right organization, and hand it valid input.
11
11
 
12
- ## Prerequisites
13
-
14
- The `feast` CLI must be installed and on PATH:
15
-
16
- ```bash
17
- npm install -g @feastalytics/cli # or run ad-hoc with: npx @feastalytics/cli <command>
18
- ```
19
-
20
- Authenticate once — tokens are cached in `~/.config/feast-cli/credentials.json` and refreshed automatically:
21
-
22
- ```bash
23
- feast login # opens a browser to authorize (default)
24
- feast login --password [username] # headless / CI: username + password prompt, no browser
25
- ```
26
-
27
- If a command reports you're not logged in or the session expired, re-run `feast login`.
28
-
29
- ## Staying current
30
-
31
- Neither the CLI nor this skill updates itself. When a command prints an update notice on stderr:
32
-
33
- ```
34
- Update available: feast 0.1.1 → 0.2.0
35
- ```
36
-
37
- update both, then tell the user in one line that you did:
38
-
39
- ```bash
40
- npm install -g @feastalytics/cli@latest # only if `feast` is already on PATH from a global install
41
- npx skills add feastalytics/cli -g -a '*' -y # refresh this skill from the repo
42
- ```
43
-
44
- If you've been invoking the CLI through `npx` rather than a global install, skip the `npm install -g` and use `npx @feastalytics/cli@latest <command>` for the rest of the session instead — `npx` reuses a cached copy otherwise.
45
-
46
- Update the skill whenever you update the CLI: the two ship from the same repo but on different triggers, so a new CLI version usually means this skill's guidance has moved too. If the global install fails on permissions, don't retry with `sudo` — tell the user and fall back to `npx @feastalytics/cli@latest`.
12
+ Some environments hand you the CLI already installed, already authenticated, and pinned to one organization. If `feast tools` runs, you're set — otherwise, installing, logging in, and staying current are in `references/setup.md`.
47
13
 
48
14
  ## The core loop: discover → describe → call
49
15
 
@@ -59,17 +25,9 @@ Always `describe` an unfamiliar tool before calling it — the schema tells you
59
25
 
60
26
  ## Organizations: never let the API guess
61
27
 
62
- Most tools act on one organization. A user often belongs to several, so which one you target matters and must be explicit.
63
-
64
- ```bash
65
- feast whoami # shows the logged-in user and every org (with names) they can act on
66
- ```
67
-
68
- Pass the target org with `--org <organizationId>`:
28
+ Most tools act on one organization, and which one must be explicit: pass it with `--org <organizationId>`. Acting on the wrong restaurant is worse than stopping to ask, so the CLI refuses rather than guessing when the target is ambiguous.
69
29
 
70
- - If the user names an org, resolve it to its id with `feast whoami` and pass that id.
71
- - If the user belongs to exactly one org, the CLI uses it automatically — no flag needed.
72
- - If they belong to more than one and you omit `--org`, the CLI refuses and lists the orgs rather than silently picking one. That's intentional: acting on the wrong org is worse than stopping to ask. When this happens, surface the list to the user and confirm which one they mean.
30
+ If you weren't given an organization id, or a command reports you belong to several, `references/setup.md` has how to resolve one.
73
31
 
74
32
  ## Reads vs. writes
75
33
 
@@ -129,7 +87,7 @@ That file has the dashboard routes with their panel and tab names, the guest-fac
129
87
  User: "add a Free Dessert reward members can redeem for 100 points in my Plum Vietnamese org."
130
88
 
131
89
  ```bash
132
- feast whoami # find the Plum Vietnamese org id
90
+ feast whoami # only if you weren't given the org id already
133
91
  feast describe createMembersProgramReward # learn the input shape (type: item vs name)
134
92
  feast call listMembersProgramRewards --org <orgId> # avoid duplicating an existing reward or catalog item
135
93
  feast call createMembersProgramReward --org <orgId> --input '{"type":"name","name":"Free Dessert","pointsCost":100}'
@@ -139,7 +97,7 @@ The pattern generalizes: identify the org, learn the tool, resolve any reference
139
97
 
140
98
  ## When something fails
141
99
 
142
- - "Not logged in / session expired" → `feast login` (or `feast login --password [username]` in a headless/CI context).
143
- - "You belong to multiple organizations" → pick one with `--org`, using `feast whoami` to get the id.
100
+ - "Not logged in / session expired", or `feast` isn't on PATH → `references/setup.md`.
101
+ - "You belong to multiple organizations" → pick one with `--org`; `references/setup.md` has how to find the id.
144
102
  - "Input does not match the tool schema" → re-read `feast describe <tool>` and fix the named fields.
145
103
  - A tool you expected isn't listed by `feast tools` → it may not be exposed yet; don't fabricate a call, tell the user.
@@ -0,0 +1,59 @@
1
+ # Setup, auth, and organizations
2
+
3
+ One-time and troubleshooting material: getting the `feast` CLI installed and logged in, keeping it and this skill current, and working out which organization to act on. The day-to-day loop lives in `SKILL.md`; you only need this file when something isn't working yet.
4
+
5
+ Some environments hand you a CLI that is already installed and authenticated, and pin you to a single organization. Nothing in this file applies there — if `feast tools` runs and your commands are going to the right restaurant, you are already set up.
6
+
7
+ ## Installing
8
+
9
+ The `feast` CLI must be installed and on PATH:
10
+
11
+ ```bash
12
+ npm install -g @feastalytics/cli # or run ad-hoc with: npx @feastalytics/cli <command>
13
+ ```
14
+
15
+ If the global install fails on permissions, don't retry with `sudo` — tell the user and fall back to `npx @feastalytics/cli@latest`.
16
+
17
+ ## Authenticating
18
+
19
+ Authenticate once — tokens are cached in `~/.config/feast-cli/credentials.json` and refreshed automatically:
20
+
21
+ ```bash
22
+ feast login # opens a browser to authorize (default)
23
+ feast login --password [username] # headless / CI: username + password prompt, no browser
24
+ ```
25
+
26
+ If a command reports you're not logged in or the session expired, re-run `feast login`.
27
+
28
+ ## Staying current
29
+
30
+ Neither the CLI nor this skill updates itself. When a command prints an update notice on stderr:
31
+
32
+ ```
33
+ Update available: feast 0.1.1 → 0.2.0
34
+ ```
35
+
36
+ update both, then tell the user in one line that you did:
37
+
38
+ ```bash
39
+ npm install -g @feastalytics/cli@latest # only if `feast` is already on PATH from a global install
40
+ npx skills add feastalytics/cli -g -a '*' -y # refresh this skill from the repo
41
+ ```
42
+
43
+ If you've been invoking the CLI through `npx` rather than a global install, skip the `npm install -g` and use `npx @feastalytics/cli@latest <command>` for the rest of the session instead — `npx` reuses a cached copy otherwise.
44
+
45
+ Update the skill whenever you update the CLI: the two ship from the same repo but on different triggers, so a new CLI version usually means this skill's guidance has moved too.
46
+
47
+ ## Which organization
48
+
49
+ Most tools act on one organization. A user often belongs to several, so which one you target matters and must be explicit.
50
+
51
+ ```bash
52
+ feast whoami # shows the logged-in user and every org (with names) they can act on
53
+ ```
54
+
55
+ Pass the target org with `--org <organizationId>`:
56
+
57
+ - If the user names an org, resolve it to its id with `feast whoami` and pass that id.
58
+ - If the user belongs to exactly one org, the CLI uses it automatically — no flag needed.
59
+ - If they belong to more than one and you omit `--org`, the CLI refuses and lists the orgs rather than silently picking one. That's intentional: acting on the wrong org is worse than stopping to ask. When this happens, surface the list to the user and confirm which one they mean.
@@ -13,7 +13,15 @@ Fully doable via the CLI. The server does the heavy lifting (id generation, defa
13
13
  - `"reservation"` — no extra config.
14
14
  - `"simpleRewards"` — needs `simpleRewardsConfig` with a `promotionName` and an image. Pass a public `imageUrl` string (the CLI can't do the app's file-upload path).
15
15
  - `"prepay"` — needs `prepayConfig` with `promotionName`, `price`, and an image (`imageUrl`).
16
- 4. (optional) `applyFunnelTemplate` — the **acquisition** half: the funnel screens a guest sees. Requires a fresh campaign whose funnel is unset; resolves the referrer from the campaign.
16
+ 4. (optional) the funnel — the **acquisition** half: the funnel screens a guest sees. Same list → pick → apply shape as automations:
17
+ - `listFunnelTemplates` with the `campaignId` — the template catalog with per-campaign `eligible`/`ineligibleReason`, a `recommended` id, and each template's guest `journey`. Read this before applying; never guess a template id.
18
+ - Pick by what the guest should experience, not by whether the offer has a price:
19
+ - `offer-basic` — Sign Up goes straight to the offer wallet. **No payment step.** The template for any offer redeemed in person, priced or not.
20
+ - `offer-prepay` / `offer-direct-prepay` — a Stripe payment screen is part of the funnel. Only eligible when the promotion has `canPrePay: true` **and** a `price`; anything else is rejected with `PRECONDITION_FAILED`.
21
+ - `reservation-offer-basic` / `reservation-offer-prepay` / `reservation-offer-direct-prepay` / `reservation-only` — the reservation variants of the same split.
22
+ - `applyFunnelTemplate` with `{ "campaignId": ..., "templateId": ... }`. Requires a fresh campaign whose funnel is unset; resolves the referrer from the campaign.
23
+ - The promotion's `canPrePay` flag does **not** change what a template builds — it only gates eligibility. A "no prepay" request means `offer-basic` (or another no-payment template), full stop.
24
+ - After applying, confirm with `listFunnelScreens` that the journey matches intent — for a no-prepay offer there must be no `payment` screen.
17
25
  5. (optional) `applyAutomationTemplate` — the **retention** half: the follow-up messaging. Provisions the campaign's flow *and* its automations in one call, so you don't hand-build a flow for this path. Preview options first with `listAutomationTemplates` / `listTemplateAutomations`.
18
26
 
19
27
  Steps 4 and 5 are the two independent halves of a working campaign — the funnel (what the guest sees) and the automations (the messaging that follows). A fully working campaign has a funnel with no screen errors and at least one automation flow.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feastalytics/cli",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "description": "Command-line client for the Feastalytics platform — list, create, and update campaigns, automations, offers, and members-program rewards from the terminal.",
5
5
  "type": "module",
6
6
  "bin": {