@hellotext/hellotext 1.8.7 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/dist/hellotext.js +1 -1
  2. package/lib/api/businesses.cjs +37 -0
  3. package/lib/api/businesses.js +3 -9
  4. package/lib/api/events.cjs +45 -0
  5. package/lib/api/events.js +8 -14
  6. package/lib/api/forms.cjs +51 -0
  7. package/lib/api/forms.js +10 -17
  8. package/lib/api/index.cjs +51 -0
  9. package/lib/api/index.js +10 -23
  10. package/lib/api/response.cjs +76 -0
  11. package/lib/api/response.js +1 -7
  12. package/lib/api/submissions.cjs +47 -0
  13. package/lib/api/submissions.js +9 -17
  14. package/lib/api/webchat/messages.cjs +70 -0
  15. package/lib/api/webchat/messages.js +10 -18
  16. package/lib/api/webchats.cjs +44 -0
  17. package/lib/api/webchats.js +8 -16
  18. package/lib/builders/input_builder.cjs +61 -0
  19. package/lib/builders/input_builder.js +4 -11
  20. package/lib/builders/logo_builder.cjs +50 -0
  21. package/lib/builders/logo_builder.js +5 -12
  22. package/lib/channels/application_channel.cjs +67 -0
  23. package/lib/channels/application_channel.js +1 -8
  24. package/lib/channels/{web_chat_channel.js → webchat_channel.cjs} +35 -20
  25. package/lib/channels/webchat_channel.js +3 -11
  26. package/lib/controllers/form_controller.cjs +141 -0
  27. package/lib/controllers/form_controller.js +15 -22
  28. package/lib/controllers/mixins/usePopover.cjs +59 -0
  29. package/lib/controllers/mixins/usePopover.js +6 -14
  30. package/lib/controllers/{web_chat/pagination_controller.js → webchat/emoji_picker_controller.cjs} +74 -29
  31. package/lib/controllers/webchat/emoji_picker_controller.js +11 -17
  32. package/lib/controllers/webchat_controller.cjs +433 -0
  33. package/lib/controllers/webchat_controller.js +49 -51
  34. package/lib/core/configuration/forms.cjs +49 -0
  35. package/lib/core/configuration/forms.js +2 -8
  36. package/lib/core/configuration/{web_chat.js → webchat.cjs} +53 -38
  37. package/lib/core/configuration/webchat.js +2 -9
  38. package/lib/core/configuration.cjs +65 -0
  39. package/lib/core/configuration.js +8 -13
  40. package/lib/core/event.cjs +71 -0
  41. package/lib/core/event.js +5 -11
  42. package/lib/core/index.cjs +20 -0
  43. package/lib/core/index.js +2 -20
  44. package/lib/errors/index.cjs +19 -0
  45. package/lib/errors/index.js +2 -19
  46. package/lib/errors/invalid_event.cjs +34 -0
  47. package/lib/errors/invalid_event.js +1 -7
  48. package/lib/errors/not_initialized_error.cjs +34 -0
  49. package/lib/errors/not_initialized_error.js +1 -7
  50. package/lib/hellotext.cjs +152 -0
  51. package/lib/hellotext.js +18 -27
  52. package/lib/index.cjs +20 -0
  53. package/lib/index.js +12 -20
  54. package/lib/locales/en.cjs +22 -0
  55. package/lib/locales/en.js +2 -9
  56. package/lib/locales/es.cjs +22 -0
  57. package/lib/locales/es.js +2 -9
  58. package/lib/locales/index.cjs +14 -0
  59. package/lib/locales/index.js +6 -14
  60. package/lib/models/business.cjs +65 -0
  61. package/lib/models/business.js +5 -12
  62. package/lib/models/cookies.cjs +40 -0
  63. package/lib/models/cookies.js +3 -10
  64. package/lib/models/form.cjs +166 -0
  65. package/lib/models/form.js +10 -17
  66. package/lib/models/form_collection.cjs +119 -0
  67. package/lib/models/form_collection.js +15 -22
  68. package/lib/models/index.cjs +54 -0
  69. package/lib/models/index.js +7 -54
  70. package/lib/models/query.cjs +51 -0
  71. package/lib/models/query.js +3 -9
  72. package/lib/models/session.cjs +58 -0
  73. package/lib/models/session.js +12 -17
  74. package/lib/models/{web_chat.js → webchat.cjs} +14 -22
  75. package/lib/models/webchat.js +6 -13
  76. package/lib/vanilla.cjs +13 -0
  77. package/lib/vanilla.js +1 -13
  78. package/package.json +34 -7
  79. package/src/controllers/webchat_controller.js +72 -47
  80. package/src/hellotext.js +3 -4
  81. package/src/models/session.js +4 -3
  82. package/.babelrc +0 -24
  83. package/.github/workflows/ci.yml +0 -24
  84. package/.prettierignore +0 -3
  85. package/.prettierrc.json +0 -18
  86. package/MIT-LICENSE +0 -20
  87. package/__tests__/builders/input_builder_test.js +0 -117
  88. package/__tests__/core/configuration/forms_test.js +0 -30
  89. package/__tests__/core/configuration/webchat_test.js +0 -163
  90. package/__tests__/core/configuration_test.js +0 -28
  91. package/__tests__/core/event_test.js +0 -56
  92. package/__tests__/hellotext_test.js +0 -197
  93. package/__tests__/models/business_test.js +0 -26
  94. package/__tests__/models/cookies_test.js +0 -25
  95. package/__tests__/models/form_collection_test.js +0 -103
  96. package/__tests__/models/form_test.js +0 -131
  97. package/__tests__/models/query_test.js +0 -97
  98. package/docs/forms.md +0 -176
  99. package/docs/tracking.md +0 -203
  100. package/docs/webchat.md +0 -101
  101. package/jest.config.js +0 -4
  102. package/jest.setup.js +0 -16
  103. package/lib/api/web_chat/messages.js +0 -74
  104. package/lib/api/web_chats.js +0 -53
  105. package/styles/index.css +0 -38
  106. package/webpack.config.js +0 -24
@@ -1,131 +0,0 @@
1
- /**
2
- * @jest-environment jsdom
3
- */
4
-
5
- import Hellotext from '../../src/hellotext'
6
- import { Form } from '../../src/models'
7
-
8
- describe('id', () => {
9
- it('is the form id', () => {
10
- const form = new Form({ id: 1 })
11
- expect(form.id).toEqual(1)
12
- })
13
- })
14
-
15
- describe('mount', () => {
16
- const data = {
17
- id: 1,
18
- steps: [
19
- {
20
- header: { content: 'Header' },
21
- inputs: [],
22
- button: { content: 'Button' },
23
- footer: { content: 'Footer' },
24
- },
25
- ]
26
- }
27
-
28
- const form = new Form(data)
29
-
30
- beforeEach(() => {
31
- Hellotext.business = {
32
- locale: {
33
- white_label: {
34
- powered_by: 'Powered by Hellotext',
35
- }
36
- },
37
- features: {
38
- white_label: false,
39
- }
40
- }
41
-
42
- document.body.innerHTML = ''
43
- })
44
-
45
- it('mounts the form', () => {
46
- form.mount()
47
- expect(document.body.querySelector('form')).not.toBeNull()
48
- })
49
-
50
- describe('when form has been completed', () => {
51
- beforeEach(() => {
52
- localStorage.setItem('hello-form-1', 'true')
53
- })
54
-
55
- it('does not mount the form automatically', () => {
56
- form.mount()
57
- expect(document.body.querySelector('form')).toBeNull()
58
- })
59
-
60
- it('mounts the form when ifCompleted is false', () => {
61
- form.mount({ ifCompleted: false })
62
- expect(document.body.querySelector('form')).not.toBeNull()
63
- })
64
- })
65
- })
66
-
67
- describe('markAsCompleted', () => {
68
- it('saves the form as completed in localStorage', () => {
69
- const form = new Form({ id: 1 })
70
- form.markAsCompleted()
71
- expect(localStorage.getItem('hello-form-1')).not.toBeNull()
72
- })
73
-
74
- it('emits a form:completed event', () => {
75
- const form = new Form({ id: 1 })
76
- const emit = jest.spyOn(Hellotext.eventEmitter, 'dispatch')
77
-
78
- form.markAsCompleted()
79
- expect(emit).toHaveBeenCalled()
80
- })
81
- })
82
-
83
- describe('localeAuthKey', () => {
84
- it('is email when the first step has an email input', () => {
85
- const form = new Form({
86
- steps: [
87
- {
88
- inputs: [{ kind: 'email' }]
89
- }
90
- ]
91
- })
92
-
93
- expect(form.localeAuthKey).toBe('email')
94
- })
95
-
96
- it('is phone when the first step has a phone input', () => {
97
- const form = new Form({
98
- steps: [
99
- {
100
- inputs: [{ kind: 'phone' }]
101
- }
102
- ]
103
- })
104
-
105
- expect(form.localeAuthKey).toBe('phone')
106
- })
107
-
108
- it('is phone_and_email when the first step has both email and phone inputs', () => {
109
- const form = new Form({
110
- steps: [
111
- {
112
- inputs: [{ kind: 'email' }, { kind: 'phone' }]
113
- }
114
- ]
115
- })
116
-
117
- expect(form.localeAuthKey).toBe('phone_and_email')
118
- })
119
-
120
- it('is none when the first step has neither email nor phone inputs', () => {
121
- const form = new Form({
122
- steps: [
123
- {
124
- inputs: [{ kind: 'first_name' }]
125
- }
126
- ]
127
- })
128
-
129
- expect(form.localeAuthKey).toBe('none')
130
- })
131
- })
@@ -1,97 +0,0 @@
1
- /**
2
- * @jest-environment jsdom
3
- */
4
-
5
- import { Query } from '../../src/models'
6
-
7
- describe('get', () => {
8
- beforeEach(() => {
9
- const windowMock = {
10
- location: { search: "?hello_session=session&hello_preview=1" },
11
- }
12
-
13
- jest.spyOn(global, 'window', 'get').mockImplementation(() => windowMock)
14
- })
15
-
16
- it('gets the value of a query parameter', () => {
17
- const query = new Query()
18
-
19
- expect(query.get("session")).toEqual("session")
20
- expect(query.get("preview")).toEqual("1")
21
- })
22
- })
23
-
24
- describe('has', () => {
25
- beforeEach(() => {
26
- const windowMock = {
27
- location: { search: "?hello_session=session" },
28
- }
29
-
30
- jest.spyOn(global, 'window', 'get').mockImplementation(() => windowMock)
31
- })
32
-
33
- it('is true when the query parameter is present', () => {
34
- const query = new Query()
35
- expect(query.has("session")).toEqual(true)
36
- })
37
-
38
- it('is false when the query parameter is not present', () => {
39
- const query = new Query()
40
- expect(query.has("preview")).toEqual(false)
41
- })
42
- })
43
-
44
- describe('inPreviewMode', () => {
45
- it('is true when the preview query parameter is present', () => {
46
- const windowMock = {
47
- location: { search: "?hello_preview" },
48
- }
49
-
50
- jest.spyOn(global, 'window', 'get').mockImplementation(() => windowMock)
51
-
52
- expect(Query.inPreviewMode).toEqual(true)
53
- })
54
-
55
- it('is false when the preview query parameter is not present', () => {
56
- const windowMock = {
57
- location: { search: "" },
58
- }
59
-
60
- jest.spyOn(global, 'window', 'get').mockImplementation(() => windowMock)
61
-
62
- expect(Query.inPreviewMode).toEqual(false)
63
- })
64
- })
65
-
66
- describe('session', () => {
67
- it('gets the session from the query parameter when present in query', () => {
68
- const windowMock = {
69
- location: { search: "?hello_session=session" },
70
- }
71
-
72
- jest.spyOn(global, 'window', 'get').mockImplementation(() => windowMock)
73
-
74
- const query = new Query()
75
- expect(query.session).toEqual("session")
76
- })
77
-
78
- it('gets the session from the cookie when not present in query', () => {
79
- const windowMock = {
80
- location: { search: "" },
81
- }
82
-
83
- jest.spyOn(global, 'window', 'get').mockImplementation(() => windowMock)
84
-
85
- document.cookie = "hello_session=session"
86
-
87
- const query = new Query()
88
- expect(query.session).toEqual("session")
89
- })
90
- })
91
-
92
- describe("#toHellotextParameter", () => {
93
- it("prefixes the argument with hello_", () => {
94
- const query = new Query()
95
- expect(query.toHellotextParam("preview")).toEqual("hello_preview")
96
- });
97
- })
package/docs/forms.md DELETED
@@ -1,176 +0,0 @@
1
- ## Forms
2
-
3
- Create dynamic forms based on built-in subscriber attributes such as name, email, phone or other custom properties unique to your business.
4
- Then let Hellotext handle building the form, collecting, validating and authenticating the data users submit.
5
-
6
- For more information on how to create a form from the dashboard, view this [guide](https://help.hellotext.com/forms).
7
-
8
- ### Configuration
9
-
10
- Hellotext forms have a default configuration that can be overridden by passing an object when initializing the library. It has the following attributes by default:
11
-
12
- - `autoMount`: Automatically mount forms to the DOM when a `form` element with the `data-hello-form` attribute is found. Default is `true`.
13
- - `successMessage`: Display a contextual success message when a form is submitted successfully. Default is `true`.
14
- You can turn this off by setting it to `false`, or provide your custom success message by setting it to a string.
15
-
16
- ```javascript
17
- Hellotext.initialize('HELLOTEXT_BUSINESS_ID', {
18
- forms: {
19
- autoMount: true,
20
- successMessage: 'Thank you for submitting the form'
21
- }
22
- })
23
- ```
24
-
25
- ### Collection Phase
26
-
27
- Hellotext uses the `MutationObserver` API to listen for changes in the DOM, specifically new form elements being added that have the `data-hello-form` attribute.
28
-
29
- You can access the forms object to also trigger the form collection phase manually.
30
- This is useful if you have a Single Page Application(SPA) and cannot hardcode the `data-hello-form` element on the rendered page.
31
-
32
- To manually collect forms, do the following.
33
-
34
- ```javascript
35
- Hellotext.initialize('HELLOTEXT_BUSINESS_ID')
36
- Hellotext.forms.collect()
37
- ```
38
-
39
- Once loaded, you can access the `FormCollection` object by calling `Hellotext.forms`.
40
-
41
- Make sure you have initialized with `Hellotext.initialize` otherwise an error is reported.
42
-
43
- Form collection finishes once Hellotext has fetched the data for the form elements present on the page from the Hellotext API.
44
- Afterwards, it dispatches a `forms:collected` event that you can subscribe to.
45
-
46
- ```javascript
47
- Hellotext.on('forms:collected', forms => {
48
- console.log(forms) // Instance of FormCollection
49
- })
50
- ```
51
-
52
- The `FormCollection` class is a wrapper around the forms, which providers other useful methods.
53
-
54
- - `getById(id: string): Form` - Get a form by it's id
55
- - `getByIndex(index: number): Form` - Get a form by it's index
56
- - `includes(id: string): boolean` - Check if a form is included in the collection
57
- - `excludes(id: string): boolean` - Check if a form is not included in the collection
58
- - `length` - Get the number of forms in the collection
59
- - `forEach`, `map` are also supported.
60
-
61
- ### Mounting forms
62
-
63
- Hellotext.js by default automatically mounts forms collected to the DOM. You can disable this behaviour by passing the `autoMountForms` option as `false` when initializing the library.
64
-
65
- ```javascript
66
- Hellotext.initialize('HELLOTEXT_BUSINESS_ID', { autoMountForms: false })
67
- ```
68
-
69
- If form mounting is disabled, Hellotext does not automatically mount form elements,
70
- you will have total control on when and where to mount the form elements. To mount a form object, you call the `mount` method on the form object.
71
-
72
- ```javascript
73
- Hellotext.on('forms:collected', forms => {
74
- forms.getByIndex(0).mount()
75
- })
76
- ```
77
-
78
- Mounting a form creates the form and it's components that are associated to it, and attaches it to the DOM.
79
- Hellotext looks for a `form` element with the `data-hello-form` attribute and mounts the form inside it.
80
- If this condition is not met, Hellotext creates the form manually and appends it to the body of the document.
81
- We recommend to make the criteria met to ensure the form is loaded into an expected place in your page.
82
-
83
- Hellotext provides seamless integration with your website, once forms are completed, they are stored in the `localStorage`,
84
- this ensures that the form is not displayed again to the user if they have already completed it. Of course, sometimes it may be desired to
85
- show the form to a user regardless if they have completed the form or not, in these cases, you can adjust the way you call `Form.mount()`,
86
-
87
- ```javascript
88
- Hellotext.on('forms:collected', forms => {
89
- forms.getByIndex(0).mount({ ifCompleted: false })
90
- })
91
- ```
92
-
93
- This will mount the form regardless if the user has completed the form or not.
94
-
95
- ### Validation
96
-
97
- Hellotext automatically validates the form inputs based on how they were configured on the dashboard
98
- using browser's native [checkValidity()](https://developer.mozilla.org/en-US/docs/Web/API/HTMLSelectElement/checkValidity).
99
-
100
- Once the user tries to submit the form and there are missing required fields,
101
- the submission is halted and we display default browser's error message using [HTMLObjectElement.validationMessage](https://developer.mozilla.org/en-US/docs/Web/API/HTMLObjectElement/validationMessage) property.
102
-
103
- If the form contains a unique property and the client enters a value that is already taken,
104
- the submission will not be complete. Instead, an error will be reported to the client and the form will not be submitted.
105
-
106
- Uniqueness errors use the browser's native [setCustomValidity()](https://developer.mozilla.org/en-US/docs/Web/API/HTMLObjectElement/setCustomValidity)
107
- method which are reported as normal form validation errors, they can be styled with the `input:invalid` selector.
108
-
109
- ### Understanding Verification
110
-
111
- Hellotext protects you from bot submissions and protects your customers from identity theft and impersonation.
112
-
113
- When a subscriber fills the form with an email and/or phone number, Hellotext sends a verification link to the value the submission had at the time of creation.
114
- When the customer clicks the link in their email and/or the SMS they receive, the attribute is verified.
115
- Once the email/phone number were verified, the data is considered to be _verified_.
116
-
117
- Once verification of a submission is complete, Hellotext performs an automatic merge (if needed) of all profiles
118
- that have the same email and/or phone number. After automatic merging, if a property has multiple values,
119
- it will be visible in the Audience page and the user can see the values that were merged and they can decide which one
120
- to ignore and which one to accept.
121
-
122
- ### Form Completion
123
-
124
- Once the form is considered to be complete, it will be sent to the Hellotext API to create(or update) a profile from the submission information.
125
- The library also dispatches a `form:completed` event that you can subscribe to. In addition, a Session object is set and stored on the browser's cookies.
126
- Additionally, the `Hellotext.session` is also set if no session was present already, you can listen for the session events by subscribing to `session-set` event.
127
-
128
- ```javascript
129
- Hellotext.on('form:completed', (form) => {
130
- console.log(form) // An object from FormData
131
- })
132
-
133
- {
134
- state: 'completed',
135
- completedAt: 1730114734999, // Timestamp when the form was completed
136
- id: "xxxxx", // Id of the form that has been completed
137
- data: {
138
- first_name: "Billy",
139
- last_name: "Butcher",
140
- email: "theboys@hellotext.com",
141
- phone: "+1234567890",
142
- property_by_id[xxxxx]: "value"
143
- }
144
- }
145
- ```
146
-
147
- The data in the from will differ based on the inputs you have configured on the dashboard.
148
-
149
- ### Understanding form's layout
150
-
151
- Hellotext assumes a fixed layout for forms, which are in order of Header, Inputs, Button and Notice.
152
-
153
- But you can override this layout if you want. Overriding a form's layout can be achieved
154
- by moving the placement of the form's components. For example, if you want to display the Button component after the Footer, here's how you can do that
155
-
156
- ```html
157
- <form data-hello-form=":id">
158
- <footer data-form-notice></footer>
159
-
160
- <button data-form-button></button>
161
- </form>
162
- ```
163
-
164
- Hellotext would simply load the contents inside the respective elements without creating the default layout.
165
- If these elements were not defined, Hellotext would render the button then the notice component.
166
-
167
- ### Customizing the Form's styles
168
-
169
- Generated form elements have minimum styles to make them display correctly. No colors, borders or padding are applied.
170
- You can style the form elements to match your brand guidelines. Hellotext.js ships with a few lines of CSS to apply layout on the components.
171
- See `styles/index.css` for the default styles applied to the form elements.
172
-
173
- ### White Labels
174
-
175
- As a subscriber to Hellotext, if your package does not support white labels, a `powered by Hellotext` logo is displayed at the bottom-right of the form.
176
- You are free to move the position of this element if you want, but it should be visible to your subscribers.
package/docs/tracking.md DELETED
@@ -1,203 +0,0 @@
1
- ## Tracking Events
2
-
3
- Track subscriber events as they happen on your website and let Hellotext report them back to your business.
4
-
5
- Tracking events is straightforward and perhaps the simplest example is tracking a page view:
6
-
7
- ```javascript
8
- Hellotext.track('page.viewed')
9
- ```
10
-
11
- In the example above only the name of the action is required.
12
-
13
- ### Handling Responses
14
-
15
- The `track` method returns a Promise that can be `await`ed using the async/await syntax. Or using `.then` on the returned Promise.
16
-
17
- ```javascript
18
- const response = await Hellotext.track('page.viewed')
19
- ```
20
-
21
- The return of the `Hellotext.track` method is an instance of a `Response` object that ships with the package. You can check the status of the response via methods, like:
22
-
23
- ```javascript
24
- if (response.failed) {
25
- console.log('failed because', response.data)
26
- }
27
-
28
- if (response.succeeded) {
29
- console.log('success')
30
- console.log(response.data) // { status: "received" }
31
- }
32
- ```
33
-
34
- ### Parameters
35
-
36
- The parameters passed to the action must be a valid set of parameters as described in
37
- [Tracking Actions](https://www.hellotext.com/api#tracking).
38
-
39
- #### URL Parameter
40
-
41
- The library takes care of handling the `url` parameter with the current URL automatically and is not required to specify it explicitly.
42
- If you want to provide another url, you can pass a `url` key in the params object when tracking an event.
43
-
44
- ```javascript
45
- Hellotext.track('page.viewed', {
46
- url: 'www.example.org',
47
- })
48
- ```
49
-
50
- ### Headers
51
-
52
- You can add any custom or additional headers to the request by passing a `headers` object in the parameter.
53
-
54
- ```javascript
55
- Hellotext.track('page.viewed', {
56
- headers: {
57
- 'X-Custom-Header': 'value',
58
- },
59
- // event related parameters
60
- })
61
- ```
62
-
63
- ### Errors
64
-
65
- Failing to provide valid set of parameters will result in an error object being returned, describing the parameters that did not satisfy the rules.
66
-
67
- ```javascript
68
- const response = await Hellotext.track('app.installed', { object_parameters: { name: null } })
69
-
70
- console.log(response.data)
71
- ```
72
-
73
- yields
74
-
75
- ```javascript
76
- {
77
- errors: [
78
- {
79
- type: 'parameter_invalid_empty',
80
- parameter: 'name',
81
- description: 'This required parameter has an empty value. Provide a valid value for the parameter.',
82
- },
83
- ]
84
- }
85
- ```
86
-
87
- For a complete list of errors types. See [Error Types](https://www.hellotext.com/api#errors)
88
-
89
- ### Event parameters
90
-
91
- Every tracked event has the following parameters which you can pass to the `track` method:
92
-
93
-
94
- | Property | Description | Type | Default |
95
- | -------------- |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -------- | ------- |
96
- | **amount** | Monetary amount that represents the revenue associated to this tracked event. | float | `0` |
97
- | **currency** | Currency for the `amount` given in ISO 4217 format. If not specified, the currency default to the business' configured reporting currency. | currency | `USD` |
98
- | **metadata** | Set of key-value pairs that you can attach to an event. This can be useful for storing additional information about the object in a structured format. | hash | `{}` |
99
- | **tracked_at** | Original date when the event happened. This is useful if you want to record an event that happened in the past. If no value is provided its value will be the same from `created_at`. | epoch | `null` |
100
-
101
- ### Associated object parameters
102
-
103
- Generally, most actions also require an associated object. These can be of type [`app`](https://www.hellotext.com/api#apps), [`coupon`](https://www.hellotext.com/api#coupons), [`form`](https://www.hellotext.com/api#forms), [`order`](https://www.hellotext.com/api#orders), [`product`](https://www.hellotext.com/api#products) and [`refund`](https://www.hellotext.com/api#refunds), these events require an existing object to be present
104
- in order to be tracked aside from [Custom Actions](https://www.hellotext.com/api#create_an_action), which don't require the trackable to be present.
105
-
106
- Associated objects are represented by three possible parameters:
107
-
108
- - `object`: An ID of an existing object of the same type. For example, when tracking app events, the `object` must be a previously created app object.
109
- - `object_parameters`: A set of parameters for creating a new object of the same type. For example, when tracking app events, the `object_parameters` must be a set of parameters for creating a new app object.
110
- - `object_type`: An ID or `name` of an existing custom object. Only required when tracking custom objects. Lets Hellotext know which type of object is being tracked. Learn more about [Objects](https://www.hellotext.com/api#objects).
111
-
112
- You can create the associated object directly by defining its parameters in a hash:
113
-
114
- ```javascript
115
- Hellotext.track('order.placed', {
116
- amount: 395.0,
117
- currency: 'USD',
118
- object_parameters: {
119
- reference: '654321',
120
- source: 'myshop',
121
- items: [
122
- {
123
- product: 'erA2RAXE',
124
- quantity: 2,
125
- }
126
- ]
127
- },
128
- })
129
- ```
130
-
131
- If you want to reuse existing objects, you must pass the identifier of an existing associated object. For example, to track a product purchase the identifier of a previously created product object as the `product`.
132
- For more information about identifiers, view the [Tracking API](https://www.hellotext.com/api#tracking)
133
-
134
- ```javascript
135
- Hellotext.track('product.purchased', {
136
- amount: 395.0,
137
- currency: 'USD',
138
- object: 'erA2RAXE',
139
- })
140
- ```
141
-
142
- ## List of actions
143
-
144
- The following is a complete list of built-in actions and their required associated objects. Each associated action accepts a possible set of two parameters
145
-
146
-
147
-
148
- | Action | Description | Required Parameter |
149
- | --------------------- | -------------------------------------------------------- |---------------------------------------------------------------------------|
150
- | **app.installed** | An app was installed. | `object` or [object_parameters](https://www.hellotext.com/api#app) |
151
- | **app.removed** | An app was removed. | `object` or [object_parameters](https://www.hellotext.com/api#app) |
152
- | **app.spent** | A customer spent on an app. | `object` or [object_parameters](https://www.hellotext.com/api#app) |
153
- | **cart.abandoned** | A cart was abandoned. | `object` or [object_parameters](https://www.hellotext.com/api#products) |
154
- | **cart.added** | Added an item to the cart. | `object` or [object_parameters](https://www.hellotext.com/api#products) |
155
- | **cart.removed** | Removed an item from the cart. | `object` or [object_parameters](https://www.hellotext.com/api#products) |
156
- | **coupon.redeemed** | A coupon was redeem by a customer. | `object` or [object_parameters](https://www.hellotext.com/api#coupons) |
157
- | **form.completed** | A form was completed by the customer. | `object` or [object_parameters](https://www.hellotext.com/api#forms) |
158
- | **order.placed** | Order has been placed. | `object` or [object_parameters](https://www.hellotext.com/api#orders) |
159
- | **order.confirmed** | Order has been confirmed by you. | `object` or [object_parameters](https://www.hellotext.com/api#orders) |
160
- | **order.cancelled** | Order has been cancelled either by you or your customer. | `object` or [object_parameters](https://www.hellotext.com/api#orders) |
161
- | **order.shipped** | Order has been shipped to your customer. | `object` or [object_parameters](https://www.hellotext.com/api#orders) |
162
- | **order.delivered** | Order has been delivered to your customer. | `object` or [object_parameters](https://www.hellotext.com/api#orders) |
163
- | **page.viewed** | A page was viewed by a customer. | `url` |
164
- | **product.purchased** | A product has been purchased. | `object` or [object_parameters](https://www.hellotext.com/api#products) |
165
- | **product.viewed** | A product page has been viewed. | `object` or [object_parameters](https://www.hellotext.com/api#products) |
166
- | **refund.requested** | A customer requested a refund. | `object` or [object_parameters](https://www.hellotext.com/api#refunds) |
167
- | **refund.received** | A refund was issued by you to your customer. | `object` or [object_parameters](https://www.hellotext.com/api#refunds) |
168
-
169
- You can also create your **[own defined actions](https://www.hellotext.com/api#actions)**.
170
-
171
- ### Tracking Custom Actions
172
-
173
- Once you have created a custom action, you track it by specifying the action's name. Custom actions do not require an associated object to be present in order to be tracked.
174
- However, it's possible to track custom actions alongside existing Objects, or new objects you introduce.
175
-
176
- ```javascript
177
- // Custom Action without an associated object
178
-
179
- Hellotext.track('appointment.booked')
180
-
181
- // Custom Action with an existing custom object instance.
182
-
183
- Hellotext.track('appointment.booked', {
184
- object_type: 'appointment',
185
- object: 'erA2RAXE',
186
- })
187
-
188
- // Custom Action with a new custom object instance.
189
- Hellotext.track('appointment.booked', {
190
- object_type: 'appointment',
191
- object_parameters: {
192
- room: 'AA-101',
193
- booked_at: 1632313200,
194
- }
195
- })
196
-
197
- // Custom Action with a builtin object instance.
198
-
199
- Hellotext.track('appointment.booked', {
200
- object_type: 'product',
201
- object: 'erA2RAXE',
202
- })
203
- ```