@unboundcx/sdk 4.11.1 → 4.13.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.
- package/index.js +11 -0
- package/package.json +1 -1
- package/services/ai/email.js +116 -0
- package/services/ai.js +2 -0
- package/services/brand.js +166 -0
- package/services/content.js +299 -0
- package/services/messaging/EmailTemplatesService.js +182 -21
package/index.js
CHANGED
|
@@ -15,6 +15,8 @@ import { WorkflowsService } from './services/workflows.js';
|
|
|
15
15
|
import { NotesService } from './services/notes.js';
|
|
16
16
|
import { StorageService } from './services/storage.js';
|
|
17
17
|
import { BrandingService } from './services/branding.js';
|
|
18
|
+
import { BrandService } from './services/brand.js';
|
|
19
|
+
import { ContentService } from './services/content.js';
|
|
18
20
|
import { VerificationService } from './services/verification.js';
|
|
19
21
|
import { PortalsService } from './services/portals.js';
|
|
20
22
|
import { SipEndpointsService } from './services/sipEndpoints.js';
|
|
@@ -93,6 +95,8 @@ class UnboundSDK extends BaseSDK {
|
|
|
93
95
|
this.notes = new NotesService(this);
|
|
94
96
|
this.storage = new StorageService(this);
|
|
95
97
|
this.branding = new BrandingService(this);
|
|
98
|
+
this.brand = new BrandService(this);
|
|
99
|
+
this.content = new ContentService(this);
|
|
96
100
|
this.verification = new VerificationService(this);
|
|
97
101
|
this.portals = new PortalsService(this);
|
|
98
102
|
this.sipEndpoints = new SipEndpointsService(this);
|
|
@@ -275,6 +279,13 @@ export { WorkflowsService } from './services/workflows.js';
|
|
|
275
279
|
export { NotesService } from './services/notes.js';
|
|
276
280
|
export { StorageService } from './services/storage.js';
|
|
277
281
|
export { BrandingService, BrandingEmailTemplatesService } from './services/branding.js';
|
|
282
|
+
export { BrandService, BrandKitsService } from './services/brand.js';
|
|
283
|
+
export {
|
|
284
|
+
ContentService,
|
|
285
|
+
ContentBlocksService,
|
|
286
|
+
ContentLibraryService,
|
|
287
|
+
ContentStockService,
|
|
288
|
+
} from './services/content.js';
|
|
278
289
|
export { VerificationService } from './services/verification.js';
|
|
279
290
|
export { PortalsService } from './services/portals.js';
|
|
280
291
|
export { SipEndpointsService } from './services/sipEndpoints.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unboundcx/sdk",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.13.0",
|
|
4
4
|
"description": "Official JavaScript SDK for the Unbound API - A comprehensive toolkit for integrating with Unbound's communication, AI, and data management services",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { internalRequest } from '../../base.js';
|
|
2
|
+
|
|
3
|
+
function pickDefined(fields) {
|
|
4
|
+
const body = {};
|
|
5
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
6
|
+
if (value !== undefined) body[key] = value;
|
|
7
|
+
}
|
|
8
|
+
return body;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Email generation via SpaceXAI. `sdk.ai.email.*`.
|
|
13
|
+
*
|
|
14
|
+
* @see app1-api src/services/ai/routes/email.js
|
|
15
|
+
*/
|
|
16
|
+
export class EmailService {
|
|
17
|
+
constructor(sdk) {
|
|
18
|
+
this.sdk = sdk;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Generate or revise an email as a block-tree document.
|
|
23
|
+
*
|
|
24
|
+
* @param {Object} params
|
|
25
|
+
* @param {string} params.prompt - Generation prompt (required)
|
|
26
|
+
* @param {string} [params.brandKitId] - Brand kit to apply
|
|
27
|
+
* @param {string} [params.appearance] - `client` or `marketing`
|
|
28
|
+
* @param {Object|Array} [params.tree] - Existing block tree to revise
|
|
29
|
+
* @returns {Promise<Object>} `{ tree }`
|
|
30
|
+
*/
|
|
31
|
+
async generate({ prompt, brandKitId, appearance, tree } = {}) {
|
|
32
|
+
this.sdk.validateParams(
|
|
33
|
+
{ prompt, brandKitId, appearance, tree },
|
|
34
|
+
{
|
|
35
|
+
prompt: { type: 'string', required: true },
|
|
36
|
+
brandKitId: { type: 'string', required: false },
|
|
37
|
+
appearance: { type: 'string', required: false },
|
|
38
|
+
tree: { type: 'object', required: false },
|
|
39
|
+
},
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
return internalRequest(this.sdk, '/ai/email/generate', 'POST', {
|
|
43
|
+
body: pickDefined({ prompt, brandKitId, appearance, tree }),
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Rewrite email copy.
|
|
49
|
+
*
|
|
50
|
+
* @param {Object} params
|
|
51
|
+
* @param {string} params.text - Source copy (required)
|
|
52
|
+
* @param {string} params.instruction - `tone` | `shorten` | `expand` | `translate`
|
|
53
|
+
* @param {string} [params.lang] - Target language (required when `translate`)
|
|
54
|
+
* @returns {Promise<Object>} `{ text }`
|
|
55
|
+
*/
|
|
56
|
+
async rewrite({ text, instruction, lang } = {}) {
|
|
57
|
+
this.sdk.validateParams(
|
|
58
|
+
{ text, instruction, lang },
|
|
59
|
+
{
|
|
60
|
+
text: { type: 'string', required: true },
|
|
61
|
+
instruction: { type: 'string', required: true },
|
|
62
|
+
lang: { type: 'string', required: false },
|
|
63
|
+
},
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
return internalRequest(this.sdk, '/ai/email/rewrite', 'POST', {
|
|
67
|
+
body: pickDefined({ text, instruction, lang }),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Generate subject + preheader variants.
|
|
73
|
+
*
|
|
74
|
+
* @param {Object} params
|
|
75
|
+
* @param {string} [params.html] - Rendered email HTML
|
|
76
|
+
* @param {string} [params.prompt] - Prompt describing the email
|
|
77
|
+
* @param {number} [params.n] - Variant count (1–5, default 3)
|
|
78
|
+
* @returns {Promise<Object>} `{ variants: [{ subject, preheader }] }`
|
|
79
|
+
*/
|
|
80
|
+
async subjects({ html, prompt, n } = {}) {
|
|
81
|
+
this.sdk.validateParams(
|
|
82
|
+
{ html, prompt, n },
|
|
83
|
+
{
|
|
84
|
+
html: { type: 'string', required: false },
|
|
85
|
+
prompt: { type: 'string', required: false },
|
|
86
|
+
n: { type: 'number', required: false },
|
|
87
|
+
},
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
return internalRequest(this.sdk, '/ai/email/subjects', 'POST', {
|
|
91
|
+
body: pickDefined({ html, prompt, n }),
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Generate image alt text.
|
|
97
|
+
*
|
|
98
|
+
* @param {Object} params
|
|
99
|
+
* @param {string} [params.imageUrl] - https or data:image URL
|
|
100
|
+
* @param {string} [params.description] - Image description
|
|
101
|
+
* @returns {Promise<Object>} `{ alt }`
|
|
102
|
+
*/
|
|
103
|
+
async altText({ imageUrl, description } = {}) {
|
|
104
|
+
this.sdk.validateParams(
|
|
105
|
+
{ imageUrl, description },
|
|
106
|
+
{
|
|
107
|
+
imageUrl: { type: 'string', required: false },
|
|
108
|
+
description: { type: 'string', required: false },
|
|
109
|
+
},
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
return internalRequest(this.sdk, '/ai/email/alt-text', 'POST', {
|
|
113
|
+
body: pickDefined({ imageUrl, description }),
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
package/services/ai.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { internalRequest } from '../base.js';
|
|
2
2
|
import { PlaybooksService } from './ai/playbooks.js';
|
|
3
3
|
import { VocabularyService } from './ai/vocabulary.js';
|
|
4
|
+
import { EmailService } from './ai/email.js';
|
|
4
5
|
import { translate as translateItems } from './ai/translate.js';
|
|
5
6
|
import {
|
|
6
7
|
getSettings as getAiSettings,
|
|
@@ -16,6 +17,7 @@ export class AIService {
|
|
|
16
17
|
this.extract = new ExtractService(sdk);
|
|
17
18
|
this.playbooks = new PlaybooksService(sdk);
|
|
18
19
|
this.vocabulary = new VocabularyService(sdk);
|
|
20
|
+
this.email = new EmailService(sdk);
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
/**
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { internalRequest } from '../base.js';
|
|
2
|
+
|
|
3
|
+
function pickDefined(fields) {
|
|
4
|
+
const body = {};
|
|
5
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
6
|
+
if (value !== undefined) body[key] = value;
|
|
7
|
+
}
|
|
8
|
+
return body;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Tenant brand kits used in content we send/host (emails, later landing
|
|
13
|
+
* pages). Distinct from white-label `sdk.branding`.
|
|
14
|
+
*
|
|
15
|
+
* @see app1-api src/services/brand/routes.js
|
|
16
|
+
*/
|
|
17
|
+
export class BrandService {
|
|
18
|
+
constructor(sdk) {
|
|
19
|
+
this.sdk = sdk;
|
|
20
|
+
this.kits = new BrandKitsService(sdk);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Brand kit CRUD + URL extract. `sdk.brand.kits.*`.
|
|
26
|
+
*/
|
|
27
|
+
export class BrandKitsService {
|
|
28
|
+
constructor(sdk) {
|
|
29
|
+
this.sdk = sdk;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* List brand kits for the account. First call may seed a Default kit.
|
|
34
|
+
*
|
|
35
|
+
* @returns {Promise<Array>} Kits
|
|
36
|
+
*/
|
|
37
|
+
async list() {
|
|
38
|
+
const result = await internalRequest(this.sdk, '/brand/kits', 'GET', {});
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Create a brand kit.
|
|
44
|
+
*
|
|
45
|
+
* @param {Object} params
|
|
46
|
+
* @param {string} params.name - Kit name (required)
|
|
47
|
+
* @param {string} [params.logoStorageId]
|
|
48
|
+
* @param {string} [params.logoDarkStorageId]
|
|
49
|
+
* @param {string} [params.faviconStorageId]
|
|
50
|
+
* @param {Object} [params.colors]
|
|
51
|
+
* @param {Object} [params.fonts]
|
|
52
|
+
* @param {Object} [params.styles]
|
|
53
|
+
* @param {Object} [params.socialLinks]
|
|
54
|
+
* @param {string} [params.legalFooter]
|
|
55
|
+
* @param {string} [params.websiteUrl]
|
|
56
|
+
* @param {boolean} [params.isDefault]
|
|
57
|
+
* @returns {Promise<Object>} Created kit
|
|
58
|
+
*/
|
|
59
|
+
async create(params = {}) {
|
|
60
|
+
this.sdk.validateParams(
|
|
61
|
+
{ name: params.name },
|
|
62
|
+
{ name: { type: 'string', required: true } },
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const result = await internalRequest(this.sdk, '/brand/kits', 'POST', {
|
|
66
|
+
body: pickDefined(params),
|
|
67
|
+
});
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Get a brand kit by id.
|
|
73
|
+
*
|
|
74
|
+
* @param {string} id - Kit id
|
|
75
|
+
* @returns {Promise<Object>} Kit
|
|
76
|
+
*/
|
|
77
|
+
async get(id) {
|
|
78
|
+
this.sdk.validateParams(
|
|
79
|
+
{ id },
|
|
80
|
+
{ id: { type: 'string', required: true } },
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
const result = await internalRequest(this.sdk, `/brand/kits/${id}`, 'GET', {});
|
|
84
|
+
return result;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Update a brand kit. Only defined fields are sent.
|
|
89
|
+
*
|
|
90
|
+
* @param {string} id - Kit id
|
|
91
|
+
* @param {Object} params - Fields to update
|
|
92
|
+
* @returns {Promise<Object>} Updated kit
|
|
93
|
+
*/
|
|
94
|
+
async update(id, params = {}) {
|
|
95
|
+
this.sdk.validateParams(
|
|
96
|
+
{ id },
|
|
97
|
+
{ id: { type: 'string', required: true } },
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
const result = await internalRequest(this.sdk, `/brand/kits/${id}`, 'PUT', {
|
|
101
|
+
body: pickDefined(params),
|
|
102
|
+
});
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Delete a brand kit.
|
|
108
|
+
*
|
|
109
|
+
* @param {string} id - Kit id
|
|
110
|
+
* @returns {Promise<Object>} Confirmation
|
|
111
|
+
*/
|
|
112
|
+
async delete(id) {
|
|
113
|
+
this.sdk.validateParams(
|
|
114
|
+
{ id },
|
|
115
|
+
{ id: { type: 'string', required: true } },
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
const result = await internalRequest(
|
|
119
|
+
this.sdk,
|
|
120
|
+
`/brand/kits/${id}`,
|
|
121
|
+
'DELETE',
|
|
122
|
+
{},
|
|
123
|
+
);
|
|
124
|
+
return result;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Make this kit the account default. Exactly one kit is default.
|
|
129
|
+
*
|
|
130
|
+
* @param {string} id - Kit id
|
|
131
|
+
* @returns {Promise<Object>} Updated kit
|
|
132
|
+
*/
|
|
133
|
+
async setDefault(id) {
|
|
134
|
+
this.sdk.validateParams(
|
|
135
|
+
{ id },
|
|
136
|
+
{ id: { type: 'string', required: true } },
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
const result = await internalRequest(
|
|
140
|
+
this.sdk,
|
|
141
|
+
`/brand/kits/${id}/default`,
|
|
142
|
+
'POST',
|
|
143
|
+
{},
|
|
144
|
+
);
|
|
145
|
+
return result;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Extract a brand-kit proposal from a public https URL. Does not save.
|
|
150
|
+
*
|
|
151
|
+
* @param {Object} params
|
|
152
|
+
* @param {string} params.url - https URL to scrape
|
|
153
|
+
* @returns {Promise<Object>} Proposal (`logoCandidates`, `colors`, `fonts`)
|
|
154
|
+
*/
|
|
155
|
+
async extract({ url } = {}) {
|
|
156
|
+
this.sdk.validateParams(
|
|
157
|
+
{ url },
|
|
158
|
+
{ url: { type: 'string', required: true } },
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
const result = await internalRequest(this.sdk, '/brand/kits/extract', 'POST', {
|
|
162
|
+
body: { url },
|
|
163
|
+
});
|
|
164
|
+
return result;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import { internalRequest } from '../base.js';
|
|
2
|
+
|
|
3
|
+
function pickDefined(fields) {
|
|
4
|
+
const body = {};
|
|
5
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
6
|
+
if (value !== undefined) body[key] = value;
|
|
7
|
+
}
|
|
8
|
+
return body;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Content blocks, template library, and stock imagery.
|
|
13
|
+
*
|
|
14
|
+
* @see app1-api src/services/content/routes.js
|
|
15
|
+
*/
|
|
16
|
+
export class ContentService {
|
|
17
|
+
constructor(sdk) {
|
|
18
|
+
this.sdk = sdk;
|
|
19
|
+
this.blocks = new ContentBlocksService(sdk);
|
|
20
|
+
this.library = new ContentLibraryService(sdk);
|
|
21
|
+
this.stock = new ContentStockService(sdk);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Saved / synced content blocks. `sdk.content.blocks.*`.
|
|
27
|
+
*/
|
|
28
|
+
export class ContentBlocksService {
|
|
29
|
+
constructor(sdk) {
|
|
30
|
+
this.sdk = sdk;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* List content blocks.
|
|
35
|
+
*
|
|
36
|
+
* @param {Object} [filters]
|
|
37
|
+
* @param {string} [filters.channel] - `email` or `web`
|
|
38
|
+
* @param {string} [filters.category]
|
|
39
|
+
* @param {boolean} [filters.isSynced]
|
|
40
|
+
* @param {number} [filters.limit]
|
|
41
|
+
* @returns {Promise<Object>} `{ results }`
|
|
42
|
+
*/
|
|
43
|
+
async list({ channel, category, isSynced, limit } = {}) {
|
|
44
|
+
const query = pickDefined({ channel, category, isSynced, limit });
|
|
45
|
+
const options = Object.keys(query).length ? { query } : {};
|
|
46
|
+
const result = await internalRequest(
|
|
47
|
+
this.sdk,
|
|
48
|
+
'/content/blocks',
|
|
49
|
+
'GET',
|
|
50
|
+
options,
|
|
51
|
+
);
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Create a content block.
|
|
57
|
+
*
|
|
58
|
+
* @param {Object} params
|
|
59
|
+
* @param {string} params.name - Block name (required)
|
|
60
|
+
* @param {string} [params.channel]
|
|
61
|
+
* @param {boolean} [params.isSynced]
|
|
62
|
+
* @param {string} [params.category]
|
|
63
|
+
* @param {Object|Array} [params.design] - Block-tree JSON
|
|
64
|
+
* @param {string} [params.designStorageId]
|
|
65
|
+
* @param {string} [params.thumbnailStorageId]
|
|
66
|
+
* @returns {Promise<Object>} Created block
|
|
67
|
+
*/
|
|
68
|
+
async create(params = {}) {
|
|
69
|
+
this.sdk.validateParams(
|
|
70
|
+
{ name: params.name },
|
|
71
|
+
{ name: { type: 'string', required: true } },
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const result = await internalRequest(this.sdk, '/content/blocks', 'POST', {
|
|
75
|
+
body: pickDefined(params),
|
|
76
|
+
});
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Get a content block by id.
|
|
82
|
+
*
|
|
83
|
+
* @param {string} id
|
|
84
|
+
* @returns {Promise<Object>} Block
|
|
85
|
+
*/
|
|
86
|
+
async get(id) {
|
|
87
|
+
this.sdk.validateParams(
|
|
88
|
+
{ id },
|
|
89
|
+
{ id: { type: 'string', required: true } },
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const result = await internalRequest(
|
|
93
|
+
this.sdk,
|
|
94
|
+
`/content/blocks/${id}`,
|
|
95
|
+
'GET',
|
|
96
|
+
{},
|
|
97
|
+
);
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Update a content block. Only defined fields are sent.
|
|
103
|
+
*
|
|
104
|
+
* @param {string} id
|
|
105
|
+
* @param {Object} params
|
|
106
|
+
* @returns {Promise<Object>} Updated block
|
|
107
|
+
*/
|
|
108
|
+
async update(id, params = {}) {
|
|
109
|
+
this.sdk.validateParams(
|
|
110
|
+
{ id },
|
|
111
|
+
{ id: { type: 'string', required: true } },
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
const result = await internalRequest(this.sdk, `/content/blocks/${id}`, 'PUT', {
|
|
115
|
+
body: pickDefined(params),
|
|
116
|
+
});
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Delete a content block.
|
|
122
|
+
*
|
|
123
|
+
* @param {string} id
|
|
124
|
+
* @returns {Promise<Object>} Confirmation
|
|
125
|
+
*/
|
|
126
|
+
async delete(id) {
|
|
127
|
+
this.sdk.validateParams(
|
|
128
|
+
{ id },
|
|
129
|
+
{ id: { type: 'string', required: true } },
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
const result = await internalRequest(
|
|
133
|
+
this.sdk,
|
|
134
|
+
`/content/blocks/${id}`,
|
|
135
|
+
'DELETE',
|
|
136
|
+
{},
|
|
137
|
+
);
|
|
138
|
+
return result;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Platform template library. `sdk.content.library.*`.
|
|
144
|
+
* No `/use` route exists yet — list / get / create only.
|
|
145
|
+
*/
|
|
146
|
+
export class ContentLibraryService {
|
|
147
|
+
constructor(sdk) {
|
|
148
|
+
this.sdk = sdk;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* List library templates.
|
|
153
|
+
*
|
|
154
|
+
* @param {Object} [filters]
|
|
155
|
+
* @param {string} [filters.channel]
|
|
156
|
+
* @param {string} [filters.category]
|
|
157
|
+
* @param {string} [filters.appearance] - `client` or `marketing`
|
|
158
|
+
* @param {boolean} [filters.isPublished]
|
|
159
|
+
* @param {number} [filters.limit]
|
|
160
|
+
* @returns {Promise<Object>} `{ results }`
|
|
161
|
+
*/
|
|
162
|
+
async list({ channel, category, appearance, isPublished, limit } = {}) {
|
|
163
|
+
const query = pickDefined({
|
|
164
|
+
channel,
|
|
165
|
+
category,
|
|
166
|
+
appearance,
|
|
167
|
+
isPublished,
|
|
168
|
+
limit,
|
|
169
|
+
});
|
|
170
|
+
const options = Object.keys(query).length ? { query } : {};
|
|
171
|
+
const result = await internalRequest(
|
|
172
|
+
this.sdk,
|
|
173
|
+
'/content/library',
|
|
174
|
+
'GET',
|
|
175
|
+
options,
|
|
176
|
+
);
|
|
177
|
+
return result;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Get a library template by id.
|
|
182
|
+
*
|
|
183
|
+
* @param {string} id
|
|
184
|
+
* @returns {Promise<Object>} Library item
|
|
185
|
+
*/
|
|
186
|
+
async get(id) {
|
|
187
|
+
this.sdk.validateParams(
|
|
188
|
+
{ id },
|
|
189
|
+
{ id: { type: 'string', required: true } },
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
const result = await internalRequest(
|
|
193
|
+
this.sdk,
|
|
194
|
+
`/content/library/${id}`,
|
|
195
|
+
'GET',
|
|
196
|
+
{},
|
|
197
|
+
);
|
|
198
|
+
return result;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Create a library template.
|
|
203
|
+
*
|
|
204
|
+
* @param {Object} params
|
|
205
|
+
* @param {string} params.name - Name (required)
|
|
206
|
+
* @returns {Promise<Object>} Created item
|
|
207
|
+
*/
|
|
208
|
+
async create(params = {}) {
|
|
209
|
+
this.sdk.validateParams(
|
|
210
|
+
{ name: params.name },
|
|
211
|
+
{ name: { type: 'string', required: true } },
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
const result = await internalRequest(this.sdk, '/content/library', 'POST', {
|
|
215
|
+
body: pickDefined(params),
|
|
216
|
+
});
|
|
217
|
+
return result;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Stock / GIF search + import-to-storage. `sdk.content.stock.*`.
|
|
223
|
+
*/
|
|
224
|
+
export class ContentStockService {
|
|
225
|
+
constructor(sdk) {
|
|
226
|
+
this.sdk = sdk;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Search Unsplash.
|
|
231
|
+
*
|
|
232
|
+
* @param {Object} [params]
|
|
233
|
+
* @param {string} [params.q]
|
|
234
|
+
* @param {number} [params.page]
|
|
235
|
+
* @returns {Promise<Object>} `{ items }`
|
|
236
|
+
*/
|
|
237
|
+
async searchUnsplash({ q, page } = {}) {
|
|
238
|
+
return this._search('unsplash', { q, page });
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Search Pexels.
|
|
243
|
+
*
|
|
244
|
+
* @param {Object} [params]
|
|
245
|
+
* @param {string} [params.q]
|
|
246
|
+
* @param {number} [params.page]
|
|
247
|
+
* @returns {Promise<Object>} `{ items }`
|
|
248
|
+
*/
|
|
249
|
+
async searchPexels({ q, page } = {}) {
|
|
250
|
+
return this._search('pexels', { q, page });
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Search GIPHY.
|
|
255
|
+
*
|
|
256
|
+
* @param {Object} [params]
|
|
257
|
+
* @param {string} [params.q]
|
|
258
|
+
* @param {number} [params.page]
|
|
259
|
+
* @returns {Promise<Object>} `{ items }`
|
|
260
|
+
*/
|
|
261
|
+
async searchGiphy({ q, page } = {}) {
|
|
262
|
+
return this._search('giphy', { q, page });
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Import a stock asset into account storage.
|
|
267
|
+
*
|
|
268
|
+
* @param {Object} params
|
|
269
|
+
* @param {string} params.source - `unsplash` | `pexels` | `giphy`
|
|
270
|
+
* @param {string} [params.remoteId]
|
|
271
|
+
* @param {string} [params.url]
|
|
272
|
+
* @returns {Promise<Object>} `{ storageId, url }`
|
|
273
|
+
*/
|
|
274
|
+
async import(params = {}) {
|
|
275
|
+
this.sdk.validateParams(
|
|
276
|
+
{ source: params.source },
|
|
277
|
+
{ source: { type: 'string', required: true } },
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
const result = await internalRequest(
|
|
281
|
+
this.sdk,
|
|
282
|
+
'/content/stock/import',
|
|
283
|
+
'POST',
|
|
284
|
+
{ body: pickDefined(params) },
|
|
285
|
+
);
|
|
286
|
+
return result;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
async _search(source, { q, page } = {}) {
|
|
290
|
+
const query = pickDefined({ q, page });
|
|
291
|
+
const options = Object.keys(query).length ? { query } : {};
|
|
292
|
+
return internalRequest(
|
|
293
|
+
this.sdk,
|
|
294
|
+
`/content/stock/${source}`,
|
|
295
|
+
'GET',
|
|
296
|
+
options,
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import { internalRequest } from '../../base.js';
|
|
2
|
+
|
|
3
|
+
function pickDefined(fields) {
|
|
4
|
+
const body = {};
|
|
5
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
6
|
+
if (value !== undefined) body[key] = value;
|
|
7
|
+
}
|
|
8
|
+
return body;
|
|
9
|
+
}
|
|
10
|
+
|
|
2
11
|
export class EmailTemplatesService {
|
|
3
12
|
constructor(sdk) {
|
|
4
13
|
this.sdk = sdk;
|
|
@@ -11,6 +20,12 @@ export class EmailTemplatesService {
|
|
|
11
20
|
* @param {string} params.subject - Template subject (required)
|
|
12
21
|
* @param {string} [params.html] - HTML template body
|
|
13
22
|
* @param {string} [params.text] - Plain text template body
|
|
23
|
+
* @param {Object} [params.design] - Block-tree design JSON (compiled server-side)
|
|
24
|
+
* @param {string} [params.appearance] - `client` or `marketing` (immutable after create)
|
|
25
|
+
* @param {boolean} [params.allowOneOff] - Usable as a one-off / compose send
|
|
26
|
+
* @param {boolean} [params.allowCampaign] - Usable in campaigns / journeys
|
|
27
|
+
* @param {string} [params.brandKitId] - Brand kit to apply
|
|
28
|
+
* @param {string} [params.category] - Template category
|
|
14
29
|
* @param {Array<Object>} [params.variables] - Variable metadata definitions
|
|
15
30
|
* @param {string} params.variables[].key - Variable key (unique, alphanumeric + underscores)
|
|
16
31
|
* @param {string} params.variables[].label - Human-readable display name
|
|
@@ -22,6 +37,10 @@ export class EmailTemplatesService {
|
|
|
22
37
|
* const template = await sdk.messaging.email.templates.create({
|
|
23
38
|
* name: 'Welcome Email',
|
|
24
39
|
* subject: 'Welcome {{firstName}}!',
|
|
40
|
+
* appearance: 'marketing',
|
|
41
|
+
* allowOneOff: true,
|
|
42
|
+
* allowCampaign: true,
|
|
43
|
+
* design: { type: 'email', children: [] },
|
|
25
44
|
* html: '<h1>Hello {{firstName}}</h1><p>{{body}}</p>',
|
|
26
45
|
* text: 'Hello {{firstName}}',
|
|
27
46
|
* variables: [
|
|
@@ -30,7 +49,19 @@ export class EmailTemplatesService {
|
|
|
30
49
|
* ],
|
|
31
50
|
* });
|
|
32
51
|
*/
|
|
33
|
-
async create({
|
|
52
|
+
async create({
|
|
53
|
+
name,
|
|
54
|
+
subject,
|
|
55
|
+
html,
|
|
56
|
+
text,
|
|
57
|
+
variables,
|
|
58
|
+
design,
|
|
59
|
+
appearance,
|
|
60
|
+
allowOneOff,
|
|
61
|
+
allowCampaign,
|
|
62
|
+
brandKitId,
|
|
63
|
+
category,
|
|
64
|
+
}) {
|
|
34
65
|
this.sdk.validateParams(
|
|
35
66
|
{ name, subject },
|
|
36
67
|
{
|
|
@@ -39,16 +70,29 @@ export class EmailTemplatesService {
|
|
|
39
70
|
html: { type: 'string', required: false },
|
|
40
71
|
text: { type: 'string', required: false },
|
|
41
72
|
variables: { type: 'array', required: false },
|
|
73
|
+
design: { type: 'object', required: false },
|
|
74
|
+
appearance: { type: 'string', required: false },
|
|
75
|
+
allowOneOff: { type: 'boolean', required: false },
|
|
76
|
+
allowCampaign: { type: 'boolean', required: false },
|
|
77
|
+
brandKitId: { type: 'string', required: false },
|
|
78
|
+
category: { type: 'string', required: false },
|
|
42
79
|
},
|
|
43
80
|
);
|
|
44
81
|
|
|
45
|
-
const templateData = { name, subject };
|
|
46
|
-
if (html) templateData.html = html;
|
|
47
|
-
if (text) templateData.text = text;
|
|
48
|
-
if (variables) templateData.variables = variables;
|
|
49
|
-
|
|
50
82
|
const options = {
|
|
51
|
-
body:
|
|
83
|
+
body: pickDefined({
|
|
84
|
+
name,
|
|
85
|
+
subject,
|
|
86
|
+
html,
|
|
87
|
+
text,
|
|
88
|
+
variables,
|
|
89
|
+
design,
|
|
90
|
+
appearance,
|
|
91
|
+
allowOneOff,
|
|
92
|
+
allowCampaign,
|
|
93
|
+
brandKitId,
|
|
94
|
+
category,
|
|
95
|
+
}),
|
|
52
96
|
};
|
|
53
97
|
|
|
54
98
|
const result = await internalRequest(this.sdk,
|
|
@@ -60,13 +104,20 @@ export class EmailTemplatesService {
|
|
|
60
104
|
}
|
|
61
105
|
|
|
62
106
|
/**
|
|
63
|
-
* Update email template
|
|
107
|
+
* Update email template. Only defined fields are sent. Appearance is
|
|
108
|
+
* immutable after create and should usually be omitted.
|
|
64
109
|
* @param {string} id - Template ID (required)
|
|
65
110
|
* @param {Object} params - Update parameters
|
|
66
111
|
* @param {string} [params.name] - Template name
|
|
67
112
|
* @param {string} [params.subject] - Template subject
|
|
68
113
|
* @param {string} [params.html] - HTML template body
|
|
69
114
|
* @param {string} [params.text] - Plain text template body
|
|
115
|
+
* @param {Object} [params.design] - Block-tree design JSON (compiled server-side)
|
|
116
|
+
* @param {string} [params.appearance] - Usually omit; immutable after create
|
|
117
|
+
* @param {boolean} [params.allowOneOff] - Usable as a one-off / compose send
|
|
118
|
+
* @param {boolean} [params.allowCampaign] - Usable in campaigns / journeys
|
|
119
|
+
* @param {string} [params.brandKitId] - Brand kit to apply
|
|
120
|
+
* @param {string} [params.category] - Template category
|
|
70
121
|
* @param {Array<Object>} [params.variables] - Variable metadata definitions
|
|
71
122
|
* @param {string} params.variables[].key - Variable key (unique, alphanumeric + underscores)
|
|
72
123
|
* @param {string} params.variables[].label - Human-readable display name
|
|
@@ -77,13 +128,31 @@ export class EmailTemplatesService {
|
|
|
77
128
|
* @example
|
|
78
129
|
* const updated = await sdk.messaging.email.templates.update('tpl_123', {
|
|
79
130
|
* subject: 'Hi {{firstName}}, welcome to {{companyName}}!',
|
|
131
|
+
* allowOneOff: true,
|
|
132
|
+
* allowCampaign: false,
|
|
133
|
+
* design: { type: 'email', children: [] },
|
|
80
134
|
* variables: [
|
|
81
135
|
* { key: 'firstName', label: 'First Name', type: 'text', required: true },
|
|
82
136
|
* { key: 'companyName', label: 'Company Name', type: 'text' },
|
|
83
137
|
* ],
|
|
84
138
|
* });
|
|
85
139
|
*/
|
|
86
|
-
async update(
|
|
140
|
+
async update(
|
|
141
|
+
id,
|
|
142
|
+
{
|
|
143
|
+
name,
|
|
144
|
+
subject,
|
|
145
|
+
html,
|
|
146
|
+
text,
|
|
147
|
+
variables,
|
|
148
|
+
design,
|
|
149
|
+
appearance,
|
|
150
|
+
allowOneOff,
|
|
151
|
+
allowCampaign,
|
|
152
|
+
brandKitId,
|
|
153
|
+
category,
|
|
154
|
+
},
|
|
155
|
+
) {
|
|
87
156
|
this.sdk.validateParams(
|
|
88
157
|
{ id },
|
|
89
158
|
{
|
|
@@ -93,18 +162,29 @@ export class EmailTemplatesService {
|
|
|
93
162
|
html: { type: 'string', required: false },
|
|
94
163
|
text: { type: 'string', required: false },
|
|
95
164
|
variables: { type: 'array', required: false },
|
|
165
|
+
design: { type: 'object', required: false },
|
|
166
|
+
appearance: { type: 'string', required: false },
|
|
167
|
+
allowOneOff: { type: 'boolean', required: false },
|
|
168
|
+
allowCampaign: { type: 'boolean', required: false },
|
|
169
|
+
brandKitId: { type: 'string', required: false },
|
|
170
|
+
category: { type: 'string', required: false },
|
|
96
171
|
},
|
|
97
172
|
);
|
|
98
173
|
|
|
99
|
-
const updateData = {};
|
|
100
|
-
if (name) updateData.name = name;
|
|
101
|
-
if (subject) updateData.subject = subject;
|
|
102
|
-
if (html) updateData.html = html;
|
|
103
|
-
if (text) updateData.text = text;
|
|
104
|
-
if (variables) updateData.variables = variables;
|
|
105
|
-
|
|
106
174
|
const options = {
|
|
107
|
-
body:
|
|
175
|
+
body: pickDefined({
|
|
176
|
+
name,
|
|
177
|
+
subject,
|
|
178
|
+
html,
|
|
179
|
+
text,
|
|
180
|
+
variables,
|
|
181
|
+
design,
|
|
182
|
+
appearance,
|
|
183
|
+
allowOneOff,
|
|
184
|
+
allowCampaign,
|
|
185
|
+
brandKitId,
|
|
186
|
+
category,
|
|
187
|
+
}),
|
|
108
188
|
};
|
|
109
189
|
|
|
110
190
|
const result = await internalRequest(this.sdk,
|
|
@@ -156,11 +236,22 @@ export class EmailTemplatesService {
|
|
|
156
236
|
}
|
|
157
237
|
|
|
158
238
|
/**
|
|
159
|
-
* List
|
|
239
|
+
* List email templates, optionally filtered by appearance / usage flags.
|
|
240
|
+
* @param {Object} [filters]
|
|
241
|
+
* @param {string} [filters.appearance] - `client` or `marketing`
|
|
242
|
+
* @param {boolean} [filters.allowOneOff]
|
|
243
|
+
* @param {boolean} [filters.allowCampaign]
|
|
160
244
|
* @returns {Promise<Array>} List of email templates
|
|
161
245
|
*/
|
|
162
|
-
async list() {
|
|
163
|
-
const
|
|
246
|
+
async list({ appearance, allowOneOff, allowCampaign } = {}) {
|
|
247
|
+
const query = pickDefined({ appearance, allowOneOff, allowCampaign });
|
|
248
|
+
const options = Object.keys(query).length ? { query } : {};
|
|
249
|
+
const result = await internalRequest(
|
|
250
|
+
this.sdk,
|
|
251
|
+
'/messaging/email/template',
|
|
252
|
+
'GET',
|
|
253
|
+
options,
|
|
254
|
+
);
|
|
164
255
|
return result;
|
|
165
256
|
}
|
|
166
257
|
|
|
@@ -173,13 +264,15 @@ export class EmailTemplatesService {
|
|
|
173
264
|
* @param {string} [body.html] - Draft HTML body
|
|
174
265
|
* @param {string} [body.text] - Draft plain-text body
|
|
175
266
|
* @param {Object} [body.variables] - Variable substitution values
|
|
176
|
-
* @returns {Promise<Object>} Rendered preview
|
|
267
|
+
* @returns {Promise<Object>} Rendered preview. May include `unresolvedTags`
|
|
268
|
+
* (string[]) for merge tags that were not substituted.
|
|
177
269
|
* @example
|
|
178
270
|
* const preview = await sdk.messaging.email.templates.preview('tpl_123', {
|
|
179
271
|
* subject: 'Welcome {{firstName}}',
|
|
180
272
|
* html: '<p>Hello {{firstName}}</p>',
|
|
181
273
|
* variables: { firstName: 'Jane' },
|
|
182
274
|
* });
|
|
275
|
+
* // preview.unresolvedTags → [] or ['companyName', ...]
|
|
183
276
|
*/
|
|
184
277
|
async preview(id, { subject, html, text, variables } = {}) {
|
|
185
278
|
this.sdk.validateParams(
|
|
@@ -240,4 +333,72 @@ export class EmailTemplatesService {
|
|
|
240
333
|
);
|
|
241
334
|
return result;
|
|
242
335
|
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Autosave a draft design tree without compiling or snapshotting a version.
|
|
339
|
+
* @param {string} id - Template ID (required)
|
|
340
|
+
* @param {Object} body
|
|
341
|
+
* @param {Object} body.design - Block-tree design JSON (required)
|
|
342
|
+
* @returns {Promise<Object>} `{ id, draftDesignStorageId }`
|
|
343
|
+
*/
|
|
344
|
+
async autosave(id, { design } = {}) {
|
|
345
|
+
this.sdk.validateParams(
|
|
346
|
+
{ id, design },
|
|
347
|
+
{
|
|
348
|
+
id: { type: 'string', required: true },
|
|
349
|
+
design: { type: 'object', required: true },
|
|
350
|
+
},
|
|
351
|
+
);
|
|
352
|
+
|
|
353
|
+
const result = await internalRequest(this.sdk,
|
|
354
|
+
`/messaging/email/template/${id}/autosave`,
|
|
355
|
+
'POST',
|
|
356
|
+
{ body: { design } },
|
|
357
|
+
);
|
|
358
|
+
return result;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* List saved versions of a template.
|
|
363
|
+
* @param {string} id - Template ID (required)
|
|
364
|
+
* @returns {Promise<Array>} Version rows
|
|
365
|
+
*/
|
|
366
|
+
async listVersions(id) {
|
|
367
|
+
this.sdk.validateParams(
|
|
368
|
+
{ id },
|
|
369
|
+
{
|
|
370
|
+
id: { type: 'string', required: true },
|
|
371
|
+
},
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
const result = await internalRequest(this.sdk,
|
|
375
|
+
`/messaging/email/template/${id}/versions`,
|
|
376
|
+
'GET',
|
|
377
|
+
);
|
|
378
|
+
return result;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Restore a previously saved version as the current template.
|
|
383
|
+
* @param {string} id - Template ID (required)
|
|
384
|
+
* @param {number|string} version - Version number (required)
|
|
385
|
+
* @returns {Promise<Object>} Restored template
|
|
386
|
+
*/
|
|
387
|
+
async restoreVersion(id, version) {
|
|
388
|
+
this.sdk.validateParams(
|
|
389
|
+
{ id },
|
|
390
|
+
{
|
|
391
|
+
id: { type: 'string', required: true },
|
|
392
|
+
},
|
|
393
|
+
);
|
|
394
|
+
if (version === undefined || version === null || version === '') {
|
|
395
|
+
throw new Error('Missing required parameter version');
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
const result = await internalRequest(this.sdk,
|
|
399
|
+
`/messaging/email/template/${id}/versions/${version}/restore`,
|
|
400
|
+
'POST',
|
|
401
|
+
);
|
|
402
|
+
return result;
|
|
403
|
+
}
|
|
243
404
|
}
|