@scout9/admin 1.0.0-alpha.0.0.3 → 1.0.0-alpha.0.0.30

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/README.md CHANGED
@@ -1,252 +1,226 @@
1
1
  # Scout9's Pocket Scout API
2
2
 
3
- Pocket Scout Node.js API for [Scout9](https://pocket-guide.vercel.app/) - mimics you over your personal phone and email
3
+ Pocket Scout Node.js API for [Scout9](https://scout9.vercel.app/) - mimics you over your personal phone and email
4
4
  for common tasks like scheduling meetings, answering questions, and more.
5
5
 
6
- **⚠️ Danger**: Avoid using a Pocket Scout to autopilot your job and personal relationships, this tool is designed for
6
+ **⚠️ Danger**: Avoid using Scout9 autopilot your job and personal relationships, this tool is designed for
7
7
  workplace productivity and not a substitute for human interaction.
8
8
 
9
9
  ## Setup
10
10
 
11
- 1. Register and grab an API key from [Scout9](https://pocket-guide.vercel.app/)
12
- 2. (Optional) [Purchase](https://pocket-guide.vercel.app/) a Scout9 email or phone number if you prefer
11
+ 1. Register and grab an API key from [Scout9](https://scout9.vercel.app/)
12
+ 2. (Optional) [Purchase](https://scout9.vercel.app/) a Scout9 email or phone number if you prefer
13
13
 
14
14
  ```bash
15
15
  npm install @scout9/admin --save
16
16
  ```
17
17
 
18
18
  ```typescript
19
- import { Configuration, PocketScoutApi } from '@scout9/admin';
19
+ import Scout9Admin from '@scout9/admin';
20
20
 
21
- const configuration = new Configuration({
22
- apiKey: '', // Your API key
21
+ const scout9 = Scout9Admin('s9_api_key');
22
+ ```
23
+ ## Example: Start a conversation
24
+
25
+ ```typescript
26
+ await scout9.message.send({
27
+ to: '+12345678900',
28
+ message: 'Hey, bob, do you need that ladder?'
23
29
  });
30
+ ```
31
+
32
+ ## Example: Programmatically register an agent
33
+
34
+ ```typescript
35
+ const {id: agentId} = await scout9.agents.create({
36
+ firstName: 'Tony',
37
+ lastName: 'Soprano',
38
+ forwardPhone: '+14327650098'
39
+ });
40
+
41
+ // Use Tony instead of the default agent (owner of account)
42
+ await scout9.message({
43
+ to: '+12345678900',
44
+ from: agentId,
45
+ message: 'Hey, bob, do you need that ladder?'
46
+ });
47
+
24
48
 
25
- const pocketScout = new PocketScoutApi(configuration);
26
49
  ```
27
50
 
28
- ## Step 1: Register yourself as an agent
51
+ ## Example: Programmatically purchase a phone number
52
+
53
+ You can purchase and assign a masked phone number to your agent entity. You will have to have a [default payment method attached](https://scout9.com/b) to your account.
54
+
55
+ ```typescript
56
+ // Purchase a phone - assuming I have a payment method at https://scout9.com/b
57
+ const {phoneNumber} = await scout9.agents.purchasePhone(agentId, {areaCode: 206});
58
+ console.log(`Purchased phone number: ${phoneNumber}`);
59
+ ```
60
+
61
+ Purchasing a phone number will assign your phone number and its aid to your agent's `.programmablePhoneNumber`.
62
+
63
+ ## Example: Programmatically add agent audio and conversation files
64
+
65
+ You can programmatically upload audio and text files to your agent registry to improve Persona Model Transformer (PMT) performance (responses sound more like you).
29
66
 
30
67
  ```typescript
31
68
  import fs from 'fs/promises';
32
69
  import path from 'path';
33
70
 
34
- // Registered your self as an agent within the Pocket Scout context
35
- const agentId = await pocketScout
36
- .agentRegister({
37
- firstName: 'Tony',
38
- lastName: 'Sopranos',
39
- title: 'Boss',
40
-
41
- // A brief description of yourself to set the tone
42
- context: 'I\'m Tony. Look, this life, it ain\'t for the faint-hearted. I got responsibilities - to my family and my crew. Loyalty, respect, that\'s everything. When I deal with my associates, I\'m direct. I expect them to come to me straight, no BS. Some might call me tough, even ruthless, but it\'s the world we\'re in. You show weakness, you\'re done. I\'ve got a code, though. If you\'re loyal to me, I\'ll have your back. But cross me? That\'s something you\'ll regret. It\'s business, but it\'s also personal. We\'re a family.',
43
-
44
- // Must provide one of the following...
45
- forwardPhone: '+15555555544', // my personal phone number to get notified of in coming messages
46
- forwardEmail: 'tonyboss@gmail.com', // my personal email to get notified of in coming messages
47
-
48
-
49
- /**
50
- * (optional) either a provided Scout9 phone number or your personal
51
- * ⚠️ Note: If a personal number, you'll be asked to download the Pocket Scout app to enable Pocket Scout auto responses
52
- */
53
- programmablePhoneNumber: '+15555555555',
54
-
55
- /**
56
- * (optional) either a provided Scout9 email or your personal
57
- * ⚠️ Note: If a personal email, you'll be asked to authenticate your Pocket Scout onto your email account
58
- */
59
- programmableEmail: `tonyboss@gmail.com`,
60
-
61
-
62
- /**
63
- * Optional conversation data to help your Pocket Scout capture your tone
64
- * See ../examples/samples to see coversation text format or enter JSON manually
65
- */
66
- conversations: [
67
- await pocketScout.fileCreate(await fs.readFile('./conversation1.txt'), 'conversation with Chris')
68
- .then(res => res.data.id),
69
- await pocketScout.fileCreate(await fs.readFile('./conversation2.txt'), 'conversation with Paulie')
70
- .then(res => res.data.id),
71
- ],
72
-
73
- /**
74
- * (optional) audio data to help your Pocket Scout capture your tone
75
- * (Eventually your Pocket Scout can use this to generate voice responses, but for now its more of a way to capture your tone/character)
76
- */
77
- audio: [
78
- await pocketScout.fileCreate(await fs.readFile('./audio.mp3'),
79
- 'Secret Audio of me talking to Dr. Melfi (no one can know about this)').then(res => res.data.id),
80
- ]
81
- })
82
- .then((res) => res.data.id);
71
+ const textConvo1 = await scout9.agents.transcripts.upload(
72
+ agentId,
73
+ await fs.readFile('./conversation1.txt'),
74
+ 'conversation with Chris'
75
+ );
76
+ const textConvo2 = await scout9.agents.transcripts.upload(
77
+ agentId,
78
+ await fs.readFile('./conversation2.txt'),
79
+ 'conversation with Paulie'
80
+ );
81
+ const audioConvo1 = await scout9.agents.audio.upload(
82
+ agentId,
83
+ await fs.readFile('./audio.mp3'),
84
+ 'Secret Audio of me talking to Dr. Melfi (no one can know about this)'
85
+ );
83
86
  ```
84
87
 
85
- ## Step 2: Register customers
86
88
 
87
- You can register customers by adding their email or phone.
88
89
 
89
- **⚠️ Note:** If you are using a provided Scout9 email or phone number, customers must opt-in to receive messages or
90
- initiate conversations with you.
90
+ ## Step 2: Register customers
91
+
92
+ Customers are automatically added in your account when they contact any of your masked contacts. However, you can programmatically register customers by adding their email or phone.
91
93
 
92
94
  ```typescript
93
- // Create 1 customer
94
- const customerId = await pocketScout.createCustomer({
95
+ await scout9.customers.create({
95
96
  firstName: 'Hi',
96
97
  lastName: 'Jack',
97
98
  email: 'hi@example.com',
98
- phone: '+15555555555',
99
- })
100
- .then((res) => res.data.id);
101
-
102
- // Or create multiple customers
103
- const customers: Customer[] = [
104
- {
105
- // scout9 internal values
106
- name: 'Tony Soprano',
107
- phone: null,
108
- email: null,
109
-
110
- // customer properties
111
- role: 'boss',
112
- customId: 'tony-soprano',
113
- fun_fact: 'I love my ducks'
114
- },
115
- {
116
- // scout9 internal values
117
- name: 'Carmela Soprano',
118
- phone: null,
119
- email: null,
120
-
121
- // customer properties
122
- favorite_drink: 'lillet blanc',
123
- },
124
- {
125
- name: 'Salvatore Bonpensiero',
126
- firstName: 'Salvatore',
127
- phone: null,
128
- email: null,
129
- $agent: 'skip_lipari', // agent id
130
-
131
- // customer properties
132
- rat: true,
133
- location: 'New Jersey coast',
134
- nickname: 'Big 🐈',
135
- lastSeason: 'season 1'
136
- }
137
- ];
138
- await pocketScout.customersCreate({customers});
99
+ phone: '+15555555555'
100
+ });
139
101
  ```
140
102
 
141
- ## Step 3: Initiate a conversation
142
-
143
- Initiate a default generic conversation with an existing customer, use the optional **initialMessage** to provide some
144
- guidance.
145
-
103
+ You can also add multiple customers
146
104
  ```typescript
147
- const initialMessage = `Hey there, would you like a free pizza?`;
105
+ // Add multiple customers
106
+ await scout9.customers.bulkCreate(
107
+ [
108
+ {
109
+ // scout9 internal values
110
+ name: 'Tony Soprano',
111
+ phone: null,
112
+ email: null,
113
+
114
+ // customer properties
115
+ role: 'boss',
116
+ customId: 'tony-soprano',
117
+ fun_fact: 'I love my ducks'
118
+ },
119
+ {
120
+ // scout9 internal values
121
+ name: 'Carmela Soprano',
122
+ phone: null,
123
+ email: null,
124
+
125
+ // customer properties
126
+ favorite_drink: 'lillet blanc'
127
+ },
128
+ {
129
+ name: 'Salvatore Bonpensiero',
130
+ firstName: 'Salvatore',
131
+ phone: null,
132
+ email: null,
133
+ $agent: 'skip_lipari', // agent id
134
+
135
+ // customer properties
136
+ rat: true,
137
+ location: 'New Jersey coast',
138
+ nickname: 'Big 🐈',
139
+ lastSeason: 'season 1'
140
+ }
141
+ ]
142
+ );
143
+ ```
148
144
 
149
- const conversation = await pocketScout.conversationCreate({
150
- customer: customerId,
151
- agent: agentId,
152
- environment: 'phone', // This will attempt to contact via SMS
153
- // Add some initial contexts to the conversation to help the agent get started
154
- initialContexts: [
155
- 'We are offering free pizzas to the first 100 hundred customers for today only',
156
- 'We have pepperoni, cheese, meat lovers, and vegan pizzas available',
157
- 'We do not have gluten free pizzas available at this time',
158
- 'We are only offering free pizzas, nothing else',
159
- 'You must pick up the free pizza at 255 W Alameda St, Tucson, AZ 85701',
160
- 'We close at 10pm tonight',
161
- 'If the customer is not interested or serious in receiving a free pizza, disengage and direct them to our website (https://azpizzatime.com) for future orders'
162
- ]
163
- });
145
+ ## Example: Schedule a conversation
146
+ ```typescript
147
+ import moment from 'moment';
164
148
 
165
- // Send a message
166
- await pocketScout.message({convo: conversation.data.id, message: initialMessage});
167
- ```
168
149
 
169
- ## Step 4: Test your conversation
150
+ // Schedule a new conversation with Bob at 9:00am tomorrow
151
+ await scout9.message({
152
+ to: '+12345678900',
153
+ message: 'Hey, Bob, good morning!',
154
+ scheduled: moment()
155
+ .add(1, 'day')
156
+ .set({hour: 9, minutes: 0, seconds: 0})
157
+ .unix()
158
+ });
170
159
 
171
- Test your conversation before you send a message.
172
160
 
173
- ```typescript
174
- const initialMessage = `Hey there, would you like a free pizza?`;
161
+ // Schedule a message to an existing conversation with Bob at 9:00am tomorrow
162
+ scout9.message({
163
+ convo: 'convo_122343332',
164
+ message: 'Hey, Bob, good morning!',
165
+ scheduled: moment()
166
+ .add(1, 'day')
167
+ .set({hour: 9, minutes: 0, seconds: 0})
168
+ .unix()
169
+ });
175
170
 
176
- const conversationId = await pocketScout.conversationCreate({
177
- customer: customerId,
178
- agent: agentId,
179
- environment: 'phone', // This will attempt to contact via SMS
180
- // Add some initial contexts to the conversation to help the agent get started
181
- initialContexts: [
182
- 'We are offering free pizzas to the first 100 hundred customers for today only',
183
- 'We have pepperoni, cheese, meat lovers, and vegan pizzas available',
184
- 'We do not have gluten free pizzas available at this time',
185
- 'We are only offering free pizzas, nothing else',
186
- 'You must pick up the free pizza at 255 W Alameda St, Tucson, AZ 85701',
187
- 'We close at 10pm tonight',
188
- 'If the customer is not interested or serious in receiving a free pizza, disengage and direct them to our website (https://azpizzatime.com) for future orders'
189
- ]
190
- }).then((res) => res.data.id);
191
-
192
- const anticipatedCustomerResponses = [
193
- 'Yes please!',
194
- 'No thanks',
195
- 'What kind of pizza are we talking about?',
196
- 'I\'m vegan, do you have vegan pizza?',
197
- 'I hate you, stop texting me',
198
- 'I love you, keep texting me',
199
- ];
200
- for (const customerResponse of anticipatedCustomerResponses) {
201
- const generatedResponse = await pocketScout.generate({
202
- convo: conversationId,
203
- mocks: {
204
- messages: [
205
- {
206
- role: 'customer',
207
- content: customerResponse
208
- }
209
- ]
210
- }
211
- })
212
- .then((res) => res.data.content);
213
-
214
- console.log(`\n\tCustomer: "${customerResponse}"\n\tAgent: "${generatedResponse}"\n`);
215
- }
216
171
 
217
- console.log(`Looks good 👍 - sending messing to customer`);
218
- await pocketScout.message({convo: conversation.data.id, message: initialMessage});
172
+ // Or delay a message to an existing conversation with Bob 1 minute from now
173
+ scout9.message({
174
+ convo: 'convo_122343332',
175
+ message: 'Hey, Bob, good morning!',
176
+ secondsDelay: 60
177
+ });
219
178
  ```
220
179
 
221
- ## Step 5: View your conversation
180
+ ## Example view messages
222
181
 
223
- Messages and customer responses can be viewed in the [Scout9 UI](https://pocket-guide.vercel.app/). You can also
182
+ Messages and customer responses can be viewed in the [Scout9 UI](https://scout9.vercel.app/). You can also
224
183
  configure webhooks in the account portal to listen to incoming messages on your own server.
225
184
 
226
185
  ```typescript
227
- const messages = await pocketScout.messages(conversationId);
186
+ const conversationId = 'convo_122343332';
187
+ const messages = await scout9.conversation.messages(conversationId);
228
188
  console.log(`Retrieved ${messages.data.length} messages from the conversation`);
229
189
 
230
190
  for (const message of messages.data) {
231
191
  console.log(`\t${message.role}: ${message.content}`);
232
192
  }
233
193
  ```
194
+ ```json
195
+ [
196
+ {
197
+ "role": "customer",
198
+ "content": "Hey, Tony, good morning! I need that 'ladder' you mentioned."
199
+ },
200
+ {
201
+ "role": "agent",
202
+ "content": "Hey, Paulie, I know exactly what you mean... Chris will have that ladder for you. Standby for his call."
203
+ },
204
+ {
205
+ "role": "customer",
206
+ "content": "Yeah, thanks Ton"
207
+ }
208
+ ]
209
+ ```
234
210
 
235
- ## Advanced Examples
236
-
237
- ### Schedule a conversation
211
+ ## Example: Schedule an advanced conversation
238
212
 
239
- See [simple-schedule-conversation.ts](../../examples/simple-schedule-conversation.ts) on how to test a conversation before its
240
- created.
213
+ If you need the conversation to have some additional context, you can add initial contexts to a newly created conversation. Otherwise sending the message and automatically creating a conversation to the default context.
241
214
 
242
215
  ```typescript
243
- const initialMessage = `Hey there, would you like a free pizza?`;
244
-
245
- const conversation = await pocketScout.scheduleConversation({
246
- customer: customerId,
247
- agent: agentId,
248
- environment: 'phone', // This will attempt to contact via SMS
249
- // Add some initial contexts to the conversation to help the agent get started
216
+ const customerId = '1233993';
217
+ const agentId = '10029292';
218
+
219
+ // Create a conversation with some additional context to drive the conversation
220
+ const {id: convoId} = await scout9.conversation.create({
221
+ $customer: customerId,
222
+ $agent: agentId,
223
+ environment: "phone",
250
224
  initialContexts: [
251
225
  'We are offering free pizzas to the first 100 hundred customers for today only',
252
226
  'We have pepperoni, cheese, meat lovers, and vegan pizzas available',
@@ -257,118 +231,55 @@ const conversation = await pocketScout.scheduleConversation({
257
231
  'If the customer is not interested or serious in receiving a free pizza, disengage and direct them to our website (https://azpizzatime.com) for future orders'
258
232
  ]
259
233
  });
260
- ```
261
234
 
262
- ### Define workflows
235
+ // Schedule message to 9:00 am tomorrow
236
+ await scout9.message({
237
+ convo: convoId,
238
+ message: 'Hey Bob, would you like a free pizza?',
239
+ scheduled: moment()
240
+ .add(1, 'day')
241
+ .set({hour: 9, minutes: 0, seconds: 0})
242
+ .unix()
243
+ });
244
+ ```
263
245
 
264
- Conversations by default use a generic **workflow** procedure that has a stated goal to guide your Pocket Scout in a
265
- conversation. Initiate a conversation with a clear specific objective using the **workflow** api.
246
+ ## Example: Respond to a customer
266
247
 
267
- See [full workflow example](../../examples/create-workflow.ts)
248
+ If a customer triggers one of your active **workflows**, then by default your application will respond to the customer.
249
+ If you respond manually, then the Application will stop responding to the customer for the entire conversation.
268
250
 
269
251
  ```typescript
270
- const workflow: CreateWorkflowRequest = {
271
- name: 'Order Pizza',
252
+ await scout9.message.send({convo: conversationId, message: 'Hey there, would you like a free pizza?'});
253
+ ```
272
254
 
273
- // Define the goal of the workflow
274
- context: 'When a customer wants to order a pizza, I will determine what pizza they need and when, then determine if it needs to be picked up or delivered to their address.',
255
+ [//]: # (## Available Platforms)
275
256
 
276
- // fields follow this boolean structure -> (a || b) && (c || d), if true then the context will be inserted into the conversation.
277
- fields: contextFields,
257
+ [//]: # ()
258
+ [//]: # (Customers can interact with you on any of the connected platforms)
278
259
 
279
- // Custom context and how this workflow will be triggered from a customer conversation
280
- initiators: {
281
- // We need to describe the fields that we want to collect from the customer in this workflow
282
- // entity fields such as firstName, address, location, etc are built in and will be provided by default
283
- entities: customEntities,
284
- documents: workflowTriggerStatements
285
- },
286
- };
260
+ [//]: # ()
261
+ [//]: # (| Platforms | Supported | |)
287
262
 
288
- const workflowId = await pocketScout.workflowCreate(workflow).then(res => res.data.id);
263
+ [//]: # (|--------------|--------------|----------------------------------------------------------------------------------------------------------------------------------------------------|)
289
264
 
290
- console.log(`Created workflow with id: ${workflowId}`);
291
- ```
265
+ [//]: # (| Android | ⚠️ (pending) | (free) An android app is in development to enable your Pocket Scout to respond to SMS text |)
292
266
 
293
- Use the `.fields` property to guide the conversation to accomplish the goal. In this example we ask the user for the
294
- pizza size, sauce, toppings, when, delivery or take out, and if delivery then the address.
267
+ [//]: # (| iOS | ⚠️ (pending) | (free) An ios app is in development to enable your Pocket Scout to respond to iMessages |)
295
268
 
296
- ```typescript
297
- const contextFields: ConversationContextField[] = [
298
- {
299
- id: 'determineSize',
300
- context: 'Determine what size pizza the customer wants, we have small, medium, and large',
301
- conditions: [
302
- {
303
- conditions: [
304
- {
305
- key: 'pizzaSize', // If we don't know the pizzaSize, then insert this context
306
- operator: 'notExists',
307
- value: true
308
- }
309
- ]
310
- }
311
- ]
312
- },
313
- // ... other field definitions
314
- ]
315
- ```
316
-
317
- In the `.initiators.entities` we can define custom fields that the Pocket Scout can search for and store in the
318
- conversation
269
+ [//]: # (| Web | ✅ | (free) We generate conversation links for you and your customers to quickly connect, conversations expire in 1 day |)
319
270
 
320
- In this example we include a custom entity field `pizzaSize` which can be described as a small or personal pizza.
271
+ [//]: # (| Gmail | ✅ | (free) Provide Scout9 authorization access to your gmail account for read/write capabilities so your Pocket Scout can respond to emails |)
321
272
 
322
- ```typescript
323
- const customEntities = [
324
- {
325
- utteranceId: 'pizzaSize',
326
- option: 'small',
327
- languages: ['en'],
328
- text: [
329
- 'small',
330
- 'personal',
331
- 'individual',
332
- '6-inch',
333
- '8-inch',
334
- ]
335
- },
336
- // ... other custom entities or pizza sizes
337
- ]
338
- ```
273
+ [//]: # (| Outlook | ⚠️ (pending) | (free) Provide Scout9 authorization access to your outlook account for read/write capabilities so your Pocket Scout can respond to emails |)
339
274
 
340
- Then we need some statements that can trigger the workflow and context fields that get stored in `.initiators.documents`.
275
+ [//]: # (| Native Email | ❌ | For security and privacy concerns we currently cannot support native email systems at this time |)
341
276
 
342
- ```typescript
343
- const workflowTriggerStatements = [
344
- {text: 'I would like to order a %pizzaSize% %pizzaType%', id: 'request'},
345
- // ... more examples that might trigger the workflow
346
- ];
347
- ```
277
+ [//]: # (| Discord | ⚠️ (pending) | (free) Download the Pocket Scout Discord bot and configure workflows to respond to messages accordingly |)
348
278
 
349
- ### Respond to a customer
279
+ [//]: # (| Slack | ⚠️ (pending) | (free) Download the Pocket Scout Slack agent and configure workflows to respond to messages accordingly |)
350
280
 
351
- If a customer triggers one of your active **workflows**, then by default your Pocket Scout will respond to the customer.
352
- If you respond manually, then the Pocket Scout will stop responding to the customer for the entire conversation.
281
+ [//]: # (| Teams | ⚠️ (pending) | (free) Download the Pocket Scout Teams add-on |)
353
282
 
354
- ```typescript
355
- await pocketScout.message({convo: conversationId, message: 'Hey there, would you like a free pizza?'});
356
- ```
283
+ [//]: # (| Scout9 Phone | ✅ | $5/month we provide a generated phone number you can use for your Pocket Scout to text, messages will be relayed back to your personal phone number |)
357
284
 
358
- ## Available Platforms
359
-
360
- Customers can interact with you (and your Pocket Scout) on any of the connected platforms
361
-
362
- | Platforms | Supported | |
363
- |--------------|--------------|----------------------------------------------------------------------------------------------------------------------------------------------------|
364
- | Android | ⚠️ (pending) | (free) An android app is in development to enable your Pocket Scout to respond to SMS text |
365
- | iOS | ⚠️ (pending) | (free) An ios app is in development to enable your Pocket Scout to respond to iMessages |
366
- | Web | ✅ | (free) We generate conversation links for you and your customers to quickly connect, conversations expire in 1 day |
367
- | Gmail | ✅ | (free) Provide Scout9 authorization access to your gmail account for read/write capabilities so your Pocket Scout can respond to emails |
368
- | Outlook | ⚠️ (pending) | (free) Provide Scout9 authorization access to your outlook account for read/write capabilities so your Pocket Scout can respond to emails |
369
- | Native Email | ❌ | For security and privacy concerns we currently cannot support native email systems at this time |
370
- | Discord | ⚠️ (pending) | (free) Download the Pocket Scout Discord bot and configure workflows to respond to messages accordingly |
371
- | Slack | ⚠️ (pending) | (free) Download the Pocket Scout Slack agent and configure workflows to respond to messages accordingly |
372
- | Teams | ⚠️ (pending) | (free) Download the Pocket Scout Teams add-on |
373
- | Scout9 Phone | ✅ | $5/month we provide a generated phone number you can use for your Pocket Scout to text, messages will be relayed back to your personal phone number |
374
- | Scout9 Email | ✅ | $5/month We provide a generated email with your name (e.g. patrick.opie@scout9.com) you can use for your Pocket Scout |
285
+ [//]: # (| Scout9 Email | ✅ | $5/month We provide a generated email with your name (e.g. patrick.opie@scout9.com) you can use for your Pocket Scout |)