@wokuapp/sdk 0.2.1 → 0.3.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/README.md +206 -8
- package/dist/index.cjs +538 -109
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5255 -178
- package/dist/index.d.ts +5255 -178
- package/dist/index.js +536 -110
- package/dist/index.js.map +1 -1
- package/package.json +14 -13
package/README.md
CHANGED
|
@@ -12,15 +12,15 @@ Official **server-side** SDK for the [Woku](https://woku.app) management API.
|
|
|
12
12
|
|
|
13
13
|
## Why
|
|
14
14
|
|
|
15
|
-
Manage
|
|
15
|
+
Manage supported woku resources from your backend with one typed client:
|
|
16
16
|
trackers, VoC tools (NPS/CSAT/CES), wokus, forms, flows, action plans,
|
|
17
17
|
support tickets, delivery tracking and survey sends over the public `/v1` API.
|
|
18
18
|
|
|
19
19
|
- **Typed** request bodies (generated from the OpenAPI spec) and response models.
|
|
20
20
|
- **Automatic retries** with full-jitter backoff and `Retry-After` support.
|
|
21
|
-
- **
|
|
22
|
-
a
|
|
23
|
-
|
|
21
|
+
- **Protected writes**: tracker/VoC definitions, invitations and five journey operations use
|
|
22
|
+
a stable idempotency key for retries. Other writes and uploads are
|
|
23
|
+
attempted once, even when a caller provides a key. See the retry policy below.
|
|
24
24
|
- **Auto-paginated** lists: `for await (const item of await woku.tickets.list())`.
|
|
25
25
|
- **Typed errors** with the server `request_id` for support.
|
|
26
26
|
- **Zero runtime dependencies.**
|
|
@@ -50,18 +50,25 @@ const tracker = await woku.trackers.create({
|
|
|
50
50
|
system: 'retail',
|
|
51
51
|
});
|
|
52
52
|
|
|
53
|
-
// Create an NPS tool
|
|
53
|
+
// Create an NPS tool.
|
|
54
54
|
const tool = await woku.npsTools.create({
|
|
55
55
|
name: 'Post-purchase',
|
|
56
56
|
npsMessage: 'How likely are you to recommend us?',
|
|
57
57
|
});
|
|
58
|
+
|
|
59
|
+
// Tag the NPS tool with the tracker, so every response is grouped by store.
|
|
60
|
+
await woku.trackers.assignToEntity('nps', tool._id, {
|
|
61
|
+
name: tracker.name,
|
|
62
|
+
value: 'TX-42',
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Send it, then read delivery + response rate.
|
|
58
66
|
await woku.nps.sendInvitations({
|
|
59
67
|
channel: 'email',
|
|
60
68
|
npsToolId: tool._id,
|
|
61
69
|
recipients: ['ana@example.com'],
|
|
62
70
|
});
|
|
63
71
|
|
|
64
|
-
// Read delivery + response rate.
|
|
65
72
|
const stats = await woku.dispatches.stats({ channel: 'email' });
|
|
66
73
|
console.log(stats.responseRate);
|
|
67
74
|
```
|
|
@@ -69,9 +76,155 @@ console.log(stats.responseRate);
|
|
|
69
76
|
The key is read from `WOKU_API_KEY` when you omit `apiKey`. You can also pass
|
|
70
77
|
it directly: `new Woku('sk_live_...')`.
|
|
71
78
|
|
|
79
|
+
### Customer journeys
|
|
80
|
+
|
|
81
|
+
Use `authoringVersion: 2` for the business-form execution contract. Choose
|
|
82
|
+
`startMode: 'operator'`, `'response'`, or `'webhook'` for the first moment.
|
|
83
|
+
Only operator mode uses `enroll`. Response mode starts when the customer answers
|
|
84
|
+
the first tool through a shared link or QR, not when the link is opened.
|
|
85
|
+
Later moments use a delay or their own webhook. A webhook advances a timed moment
|
|
86
|
+
and cancels its wait. A webhook-primary moment can have a secondary fallback that
|
|
87
|
+
evaluates that same moment once.
|
|
88
|
+
|
|
89
|
+
Each moment creates its own CSAT, CES, NPS, or woku tool. `toolScope` defaults
|
|
90
|
+
to `'shared'` for new v2 moments, reusing the tool within that moment and
|
|
91
|
+
configuration. Use `'per_enrollment'` for one tool per participation. Later
|
|
92
|
+
moments receive a suggested 10-day wait in the authoring form; API callers must
|
|
93
|
+
specify the delay. In v2, `delayMs: 0` means one hour. Existing
|
|
94
|
+
tools cannot be assigned. Use `toolSpec` for question variables, or an uploaded
|
|
95
|
+
`fileId` for Woku. The example uses one initial send and no reminders.
|
|
96
|
+
For a bilingual Woku, set `toolSpec.descriptionEn` to its English title.
|
|
97
|
+
|
|
98
|
+
Agents with a local terminal can upload an image or MP4 to
|
|
99
|
+
`POST /v1/woku-media` using multipart and the company key. The response's
|
|
100
|
+
`fileId` can be used as `toolSpec.fileId` in a journey Woku moment or with the
|
|
101
|
+
MCP `create_woku` tool. The generated `Schemas` map includes the media upload
|
|
102
|
+
response type; media.upload wraps the binary upload endpoint.
|
|
103
|
+
The endpoint returns `400` for invalid media and `413` for multipart requests
|
|
104
|
+
over 25 MB.
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
const day = 86_400_000;
|
|
108
|
+
const sequence = {
|
|
109
|
+
attemptOffsetsMs: [0],
|
|
110
|
+
deadlineMs: 3 * day,
|
|
111
|
+
cooldownAfterResponseMs: 0,
|
|
112
|
+
};
|
|
113
|
+
const journey = await woku.journeys.create({
|
|
114
|
+
name: 'Purchase and delivery',
|
|
115
|
+
authoringVersion: 2,
|
|
116
|
+
startMode: 'webhook',
|
|
117
|
+
recipients: {
|
|
118
|
+
ticketsEnabled: true,
|
|
119
|
+
plansEnabled: true,
|
|
120
|
+
ticketEmails: ['support@example.com'],
|
|
121
|
+
planMembers: [
|
|
122
|
+
{ userId: '507f1f77bcf86cd799439011', role: 'admin' },
|
|
123
|
+
{ userId: '507f1f77bcf86cd799439012', role: 'assignee' },
|
|
124
|
+
],
|
|
125
|
+
},
|
|
126
|
+
moments: [
|
|
127
|
+
{
|
|
128
|
+
key: 'sale',
|
|
129
|
+
name: 'Purchase',
|
|
130
|
+
tool: 'csat',
|
|
131
|
+
enabled: true,
|
|
132
|
+
channel: 'email',
|
|
133
|
+
trigger: { type: 'webhook' },
|
|
134
|
+
webhook: { verification: { mode: 'url_token' } },
|
|
135
|
+
toolSpec: { subject: { es: 'tu compra', en: 'your purchase' } },
|
|
136
|
+
sequence,
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
key: 'delivery',
|
|
140
|
+
name: 'Delivery',
|
|
141
|
+
tool: 'ces',
|
|
142
|
+
enabled: true,
|
|
143
|
+
channel: 'email',
|
|
144
|
+
trigger: { type: 'webhook' },
|
|
145
|
+
webhook: { verification: { mode: 'url_token' } },
|
|
146
|
+
fallbackFromStage: 'sale',
|
|
147
|
+
fallbackAfterMs: 5 * day,
|
|
148
|
+
toolSpec: {
|
|
149
|
+
subject: { es: 'recibir tu pedido', en: 'receiving your order' },
|
|
150
|
+
},
|
|
151
|
+
sequence,
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Generate once and securely store each URL in its sending system.
|
|
157
|
+
// Calling mintMomentUrl again replaces the previous credential.
|
|
158
|
+
const sale = await woku.journeys.mintMomentUrl(journey.id, 'sale');
|
|
159
|
+
const delivery = await woku.journeys.mintMomentUrl(journey.id, 'delivery');
|
|
160
|
+
await woku.journeys.update(journey.id, { enabled: true });
|
|
161
|
+
|
|
162
|
+
// CRM and logistics use different URLs but the same purchase reference.
|
|
163
|
+
await fetch(sale.url, {
|
|
164
|
+
method: 'POST',
|
|
165
|
+
headers: {
|
|
166
|
+
'Content-Type': 'application/json',
|
|
167
|
+
'X-Woku-Event-Id': 'crm-order-123',
|
|
168
|
+
},
|
|
169
|
+
body: JSON.stringify({
|
|
170
|
+
subjectKey: 'order-123',
|
|
171
|
+
contact: { email: 'customer@example.com' },
|
|
172
|
+
}),
|
|
173
|
+
});
|
|
174
|
+
await fetch(delivery.url, {
|
|
175
|
+
method: 'POST',
|
|
176
|
+
headers: {
|
|
177
|
+
'Content-Type': 'application/json',
|
|
178
|
+
'X-Woku-Event-Id': 'delivery-order-123',
|
|
179
|
+
},
|
|
180
|
+
body: JSON.stringify({ subjectKey: 'order-123' }),
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
const page = await woku.journeys.listEnrollments(journey.id, { limit: 20 });
|
|
184
|
+
const evaluation = page.items.find(
|
|
185
|
+
(item) =>
|
|
186
|
+
item.subjectKey === 'order-123' &&
|
|
187
|
+
(item.lifecycle === 'pending' || item.lifecycle === 'running'),
|
|
188
|
+
);
|
|
189
|
+
if (evaluation) {
|
|
190
|
+
await woku.journeys.stopEnrollment(
|
|
191
|
+
journey.id,
|
|
192
|
+
evaluation.id,
|
|
193
|
+
{
|
|
194
|
+
reason: 'Customer requested no further evaluations',
|
|
195
|
+
},
|
|
196
|
+
{ idempotencyKey: `stop-${evaluation.id}` },
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
`getEnrollment` reads one case. `connections` reports credential readiness;
|
|
202
|
+
`previewMoment` checks a sample against the saved payload mapping without sending.
|
|
203
|
+
`setSenderSecret` configures senders that sign with their own secret.
|
|
204
|
+
Enrollment lists use `{ items, nextCursor }`, not `Page`: pass `nextCursor` as the
|
|
205
|
+
next call's `cursor`. Stop is durable and specific to the selected participation;
|
|
206
|
+
answers, tickets, plans, shared tools and other cases remain. A send already
|
|
207
|
+
accepted by its provider may still arrive. `stopping` means cleanup is in progress;
|
|
208
|
+
`dispatchOutcomeUncertain` identifies an interrupted in-flight send.
|
|
209
|
+
|
|
210
|
+
`pendingMoments` names the moments that have not yet sent an invitation. A v2
|
|
211
|
+
participation completes when the customer answers its final tool or 30 days
|
|
212
|
+
after that tool's first send. The same `subjectKey` can be enrolled again once
|
|
213
|
+
the previous participation is completed or stopped; each cycle has its own `id`.
|
|
214
|
+
|
|
215
|
+
Tickets go to email destinations. Plans go to an action-plan group made of existing
|
|
216
|
+
company users; `planMembers` controls its admins and assignees. Definitions remain
|
|
217
|
+
off until activated and required resources are ready.
|
|
218
|
+
Set `recipients.ticketsEnabled` or `recipients.plansEnabled` to `false` to stop that
|
|
219
|
+
journey action independently. Both default to enabled when omitted. Disabled
|
|
220
|
+
actions do not require completed recipients; their current settings remain
|
|
221
|
+
available if re-enabled later.
|
|
222
|
+
Existing definitions retain their contract; create a new v2 journey to adopt these
|
|
223
|
+
rules. Current participations retain their original definition version.
|
|
224
|
+
|
|
72
225
|
## Pagination
|
|
73
226
|
|
|
74
|
-
|
|
227
|
+
Paginated resource methods return a `Page` (journey enrollments use the cursor envelope described above). Iterate every item across pages, or walk pages:
|
|
75
228
|
|
|
76
229
|
```ts
|
|
77
230
|
for await (const ticket of await woku.tickets.list({ severity: 'high' })) {
|
|
@@ -131,8 +284,53 @@ await woku.npsTools.create(body, { idempotencyKey: 'my-key' });
|
|
|
131
284
|
|
|
132
285
|
`trackers`, `npsTools` / `csatTools` / `cesTools`, `nps` / `csat` / `ces`,
|
|
133
286
|
`wokus`, `forms`, `flows`, `actionPlans`, `actionPlanGroups`, `tickets`,
|
|
134
|
-
`ticketDestinations`, `dispatches`, `reports`, `company`, `quarantines
|
|
287
|
+
`ticketDestinations`, `dispatches`, `reports`, `company`, `quarantines`,
|
|
288
|
+
`journeys`.
|
|
135
289
|
|
|
136
290
|
## License
|
|
137
291
|
|
|
138
292
|
MIT
|
|
293
|
+
|
|
294
|
+
Advanced journey moments support `webhook.contentMode`, a bounded JSON `schema`,
|
|
295
|
+
`payload.clientFields`, conditional JavaScript text, localized variables, public
|
|
296
|
+
image URL paths, folders and tracker mappings. HTTP uses `sequence`; MCP uses
|
|
297
|
+
`cadence`. `Schemas` includes complete journey request and response shapes;
|
|
298
|
+
`JourneyMomentPreview` includes resolved content. A saved preview returns `200`
|
|
299
|
+
without verifying signatures or sending. Unknown HTTP and MCP moment fields now
|
|
300
|
+
return a validation error. `webhookSecret` is the legacy journey-wide signature,
|
|
301
|
+
separate from per-moment URL tokens and sender HMAC secrets.
|
|
302
|
+
|
|
303
|
+
`journeys.entryInfo` reads customer entry metadata; `journeys.prepareEntry`
|
|
304
|
+
prepares its first tool with a UUID request ID and identified contact. Neither
|
|
305
|
+
starts the journey. Pass the returned token as `dispatchToken` when capturing
|
|
306
|
+
its first valid answer. Journey reads, progress, previews, enrollment and event
|
|
307
|
+
results now use the generated public shapes directly. Server-side SDK keys
|
|
308
|
+
remain on the server; browser entry does not need a company secret key.
|
|
309
|
+
|
|
310
|
+
## Journey SDK v4
|
|
311
|
+
|
|
312
|
+
Upload local image/MP4 bytes with `woku.media.upload({ file, filename, contentType })`.
|
|
313
|
+
The result provides fileId for a Woku moment. Fetch supplies the multipart boundary;
|
|
314
|
+
uploads are not retried because this endpoint has no idempotency ledger. 413 is a
|
|
315
|
+
PayloadTooLargeError with the server requestId.
|
|
316
|
+
|
|
317
|
+
Iterate customer cases with `for await (const item of woku.journeys.iterEnrollments(id))`.
|
|
318
|
+
listEnrollments keeps its cursor envelope. Repeated cursors/pages raise code
|
|
319
|
+
pagination_error instead of looping.
|
|
320
|
+
|
|
321
|
+
Automatic write retries apply only to declared idempotent operations: tracker
|
|
322
|
+
creation, VoC-tool creation, invitations, journey creation/enrollment/stopping,
|
|
323
|
+
URL minting and journey events. Other writes are sent once. A key alone cannot
|
|
324
|
+
make an unsupported endpoint safe to retry. Errors expose idempotencyKey for
|
|
325
|
+
reconciling the original operation. Keep keys opaque and credential URLs out of logs.
|
|
326
|
+
Retry-After is honored; abort signals interrupt backoff.
|
|
327
|
+
|
|
328
|
+
Configure baseURL for staging. Absolute API paths are rejected and ids are encoded
|
|
329
|
+
as single segments. Company secret keys remain server-side. Tickets and Data Studio
|
|
330
|
+
are Corporate capabilities; API access remains available on every plan.
|
|
331
|
+
|
|
332
|
+
See [the example](../../examples/journey-hybrid.ts) for a typed four-moment journey with local media, conditional
|
|
333
|
+
webhook content and waits. It remains disabled and sends no evaluations.
|
|
334
|
+
|
|
335
|
+
Generate API types with `pnpm generate`; `pnpm check:generated` checks drift
|
|
336
|
+
against the vendored OpenAPI without requiring a sibling server checkout.
|