@wokuapp/sdk 0.6.0 → 0.8.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 CHANGED
@@ -363,6 +363,8 @@ for await (const tool of page) {
363
363
  console.log(tool.type, tool.name, tool.reviewCount, tool.headline.value);
364
364
  }
365
365
  const found = await woku.tools.list({ q: 'delivery', scope: 'company' });
366
+ // Tools with the most recent responses first (tools without any go last).
367
+ const recent = await woku.tools.list({ sort: '-lastResponse' });
366
368
  // Tracker chips (`name:value`): a tool matches when it has every one. Company-wide.
367
369
  const tagged = await woku.tools.list({ trackers: ['TRR:00482'] });
368
370
 
@@ -390,6 +392,11 @@ await woku.npsTools.move('nps_tool_id', { folderId: null });
390
392
  // AI report and CSV over every tool of the folder and its subfolders.
391
393
  const report = await woku.folders.report(folder.id, { lang: 'en' });
392
394
  const csv: string = await woku.folders.csv(folder.id);
395
+ // Only the responses of a period (`from` inclusive, `to` exclusive).
396
+ const septemberCsv = await woku.folders.csv(folder.id, {
397
+ from: '2026-09-01T00:00:00.000Z',
398
+ to: '2026-10-01T00:00:00.000Z',
399
+ });
393
400
  // PDF of that report with a breakdown per type (bytes, e.g. write them to a file).
394
401
  const pdf: Uint8Array = await woku.folders.pdf(folder.id, { lang: 'en' });
395
402
 
@@ -411,9 +418,13 @@ for (const tool of metrics.tools) {
411
418
  }
412
419
 
413
420
  // WhatsApp numbers alerted when a comment on any tool of the folder is an
414
- // improvement (wokus, NPS, CSAT and CES). The list replaces the previous one.
421
+ // improvement (wokus, NPS, CSAT and CES). `allReviews: true` alerts every
422
+ // review, recognitions too. The list replaces the previous one.
415
423
  await woku.folders.update(folder.id, {
416
- whatsappAlertRecipients: [{ whatsappId: '56912345678', name: 'Ana' }],
424
+ whatsappAlertRecipients: [
425
+ { whatsappId: '56912345678', name: 'Ana' },
426
+ { whatsappId: '56987654321', name: 'Luis', allReviews: true },
427
+ ],
417
428
  });
418
429
 
419
430
  // Deleting a folder never deletes its content: it moves up to the parent.
@@ -436,6 +447,86 @@ Folders created by woku (journeys, moments, integrations) report their `origin`
436
447
  tools (`npsTools`, `csatTools`, `cesTools`) accept `folderId` on `create`, expose
437
448
  `folderId` on every tool and have `move(id, { folderId })`.
438
449
 
450
+ NPS, CSAT and CES tools also take two response settings on `create` and
451
+ `update`: `anonymousDisabled` (only identified answers) and
452
+ `onlyOneResponsePerClient` (each identified client answers once; a repeat
453
+ answer is rejected with HTTP 409 and the code `ALREADY_RESPONDED`). Both come
454
+ back on every tool and can change even after the tool has responses, while its
455
+ copy stays frozen:
456
+
457
+ ```ts
458
+ await woku.npsTools.update('nps_tool_id', { onlyOneResponsePerClient: true });
459
+ ```
460
+
461
+ ### Scheduled reports
462
+
463
+ Email the PDF report of a folder (every tool of its subtree, optionally with
464
+ the CSV) or of one tool to up to 10 people, also outside woku. `monthly` goes
465
+ out on the 1st, `biweekly` on the 1st and 16th and `weekly` on Mondays, at
466
+ 12:00 UTC with the previous complete period; `once` goes out at `sendAt` with
467
+ its own period. Each email goes as the company with a one-click unsubscribe.
468
+
469
+ ```ts
470
+ const monthly = await woku.reportSubscriptions.create({
471
+ scopeType: 'folder',
472
+ scopeId: folder.id,
473
+ frequency: 'monthly',
474
+ recipients: ['board@acme.com'],
475
+ includeCsv: true,
476
+ });
477
+ console.log(monthly.nextRunAt);
478
+
479
+ // One report of a chosen period, sent right away.
480
+ const once = await woku.reportSubscriptions.create({
481
+ scopeType: 'nps',
482
+ scopeId: 'nps_tool_id',
483
+ frequency: 'once',
484
+ recipients: ['ana@acme.com'],
485
+ periodFrom: '2026-09-01T00:00:00.000Z',
486
+ periodTo: '2026-10-01T00:00:00.000Z',
487
+ });
488
+ await woku.reportSubscriptions.send(once.id); // { status: 'sent', sent: 1 }
489
+
490
+ await woku.reportSubscriptions.update(monthly.id, { active: false }); // pause
491
+ const all = await woku.reportSubscriptions.list({
492
+ scopeType: 'folder',
493
+ scopeId: folder.id,
494
+ });
495
+ await woku.reportSubscriptions.delete(monthly.id);
496
+ ```
497
+
498
+ ### Folder participants
499
+
500
+ Keep the list of people expected to answer the tools of a folder (a program's
501
+ enrolled, for example) and see, per tool, who answered, who is missing and who
502
+ answered more than once. One person per line: a name, an email and/or a phone
503
+ with its country code; a CSV goes as is. Only identified responses can be
504
+ matched, so each tool says whether it takes anonymous ones.
505
+
506
+ ```ts
507
+ await woku.folderParticipants.replace(
508
+ folder.id,
509
+ 'Ana, ana@acme.com\nLuis, +56 9 8765 4321',
510
+ );
511
+ await woku.folderParticipants.add(folder.id, 'carla@acme.com'); // skips people already listed
512
+
513
+ const { participants, tools } = await woku.folderParticipants.get(folder.id);
514
+ for (const tool of tools) {
515
+ const missing = tool.missing.map((i) => participants[i].name);
516
+ console.log(tool.name, tool.responded.length, missing, tool.anonymousAllowed);
517
+ }
518
+
519
+ // Send a tool again to the people who have not answered it.
520
+ await woku.folderParticipants.resendToMissing(folder.id, {
521
+ toolType: 'nps',
522
+ toolId: 'nps_tool_id',
523
+ channel: 'email',
524
+ }); // { missing, sent, withoutContact, failed }
525
+
526
+ await woku.folderParticipants.remove(folder.id, participants[0].id);
527
+ await woku.folderParticipants.clear(folder.id);
528
+ ```
529
+
439
530
  ## Pagination
440
531
 
441
532
  Paginated resource methods return a `Page` (journey enrollments and `tools.list` use a cursor envelope). Iterate every item across pages, or walk pages:
@@ -499,7 +590,8 @@ await woku.npsTools.create(body, { idempotencyKey: 'my-key' });
499
590
  `trackers`, `npsTools` / `csatTools` / `cesTools`, `nps` / `csat` / `ces`,
500
591
  `wokus`, `forms`, `flows`, `actionPlans`, `actionPlanGroups`, `tickets`,
501
592
  `ticketDestinations`, `dispatches`, `reports`, `company`, `journeys`,
502
- `folders`, `tools`, `journeyFolders`.
593
+ `folders`, `folderParticipants`, `tools`, `journeyFolders`,
594
+ `reportSubscriptions`.
503
595
 
504
596
  ## License
505
597
 
package/dist/index.cjs CHANGED
@@ -38,7 +38,7 @@ var FollowUps = class {
38
38
  };
39
39
 
40
40
  // package.json
41
- var version = "0.6.0";
41
+ var version = "0.8.0";
42
42
 
43
43
  // src/core/errors.ts
44
44
  var WokuError = class extends Error {
@@ -1663,12 +1663,15 @@ var Folders = class {
1663
1663
  { ...opts, query: { ...params, ...opts?.query } }
1664
1664
  );
1665
1665
  }
1666
- /** CSV text with one row per response of every tool of the folder subtree. */
1667
- csv(id, opts) {
1666
+ /**
1667
+ * CSV text with one row per response of every tool of the folder subtree,
1668
+ * or only those of a period (`from` inclusive, `to` exclusive, ISO 8601).
1669
+ */
1670
+ csv(id, params, opts) {
1668
1671
  return this.client.request(
1669
1672
  "get",
1670
1673
  `/v1/folders/${encodeURIComponent(id)}/csv`,
1671
- opts
1674
+ { ...opts, query: { ...params, ...opts?.query } }
1672
1675
  );
1673
1676
  }
1674
1677
  /**
@@ -1812,6 +1815,109 @@ var JourneyFolders = class {
1812
1815
  }
1813
1816
  };
1814
1817
 
1818
+ // src/resources/report-subscriptions.ts
1819
+ var ReportSubscriptions = class {
1820
+ constructor(client) {
1821
+ this.client = client;
1822
+ }
1823
+ /** Every scheduled report, or those of one folder or tool. */
1824
+ list(params, opts) {
1825
+ return this.client.request(
1826
+ "get",
1827
+ "/v1/report-subscriptions",
1828
+ { ...opts, query: { ...params, ...opts?.query } }
1829
+ );
1830
+ }
1831
+ create(body, opts) {
1832
+ return this.client.request(
1833
+ "post",
1834
+ "/v1/report-subscriptions",
1835
+ { ...opts, body }
1836
+ );
1837
+ }
1838
+ /** Edit, pause or resume a recurring scheduled report. */
1839
+ update(id, body, opts) {
1840
+ return this.client.request(
1841
+ "patch",
1842
+ `/v1/report-subscriptions/${encodeURIComponent(id)}`,
1843
+ { ...opts, body }
1844
+ );
1845
+ }
1846
+ delete(id, opts) {
1847
+ return this.client.request(
1848
+ "delete",
1849
+ `/v1/report-subscriptions/${encodeURIComponent(id)}`,
1850
+ opts
1851
+ );
1852
+ }
1853
+ /**
1854
+ * Send it now: the last complete period of a recurring report (its
1855
+ * schedule stays) or the period of a one-off (which then closes).
1856
+ */
1857
+ send(id, opts) {
1858
+ return this.client.request(
1859
+ "post",
1860
+ `/v1/report-subscriptions/${encodeURIComponent(id)}/send`,
1861
+ opts
1862
+ );
1863
+ }
1864
+ };
1865
+
1866
+ // src/resources/folder-participants.ts
1867
+ var FolderParticipants = class {
1868
+ constructor(client) {
1869
+ this.client = client;
1870
+ }
1871
+ /** The list and, per tool of the folder subtree, who answered. */
1872
+ get(folderId, opts) {
1873
+ return this.client.request(
1874
+ "get",
1875
+ `/v1/folders/${encodeURIComponent(folderId)}/participants`,
1876
+ opts
1877
+ );
1878
+ }
1879
+ /** Replace the whole list. */
1880
+ replace(folderId, text, opts) {
1881
+ return this.client.request(
1882
+ "put",
1883
+ `/v1/folders/${encodeURIComponent(folderId)}/participants`,
1884
+ { ...opts, body: { text } }
1885
+ );
1886
+ }
1887
+ /** Add people; the ones already on the list are skipped. */
1888
+ add(folderId, text, opts) {
1889
+ return this.client.request(
1890
+ "post",
1891
+ `/v1/folders/${encodeURIComponent(folderId)}/participants`,
1892
+ { ...opts, body: { text } }
1893
+ );
1894
+ }
1895
+ /** Remove one person (the `id` from `get`). */
1896
+ remove(folderId, participantId, opts) {
1897
+ return this.client.request(
1898
+ "delete",
1899
+ `/v1/folders/${encodeURIComponent(folderId)}/participants/${encodeURIComponent(participantId)}`,
1900
+ opts
1901
+ );
1902
+ }
1903
+ /** Delete the whole list. Responses are never touched. */
1904
+ clear(folderId, opts) {
1905
+ return this.client.request(
1906
+ "delete",
1907
+ `/v1/folders/${encodeURIComponent(folderId)}/participants`,
1908
+ opts
1909
+ );
1910
+ }
1911
+ /** Send a tool again to the people of the list who have not answered it. */
1912
+ resendToMissing(folderId, body, opts) {
1913
+ return this.client.request(
1914
+ "post",
1915
+ `/v1/folders/${encodeURIComponent(folderId)}/participants/resend`,
1916
+ { ...opts, body }
1917
+ );
1918
+ }
1919
+ };
1920
+
1815
1921
  // src/woku.ts
1816
1922
  var Woku = class {
1817
1923
  constructor(options) {
@@ -1838,6 +1944,8 @@ var Woku = class {
1838
1944
  this.company = new Company(this.client);
1839
1945
  this.journeys = new Journeys(this.client);
1840
1946
  this.folders = new Folders(this.client);
1947
+ this.folderParticipants = new FolderParticipants(this.client);
1948
+ this.reportSubscriptions = new ReportSubscriptions(this.client);
1841
1949
  this.tools = new Tools(this.client);
1842
1950
  this.journeyFolders = new JourneyFolders(this.client);
1843
1951
  }
@@ -1846,6 +1954,7 @@ var Woku = class {
1846
1954
  exports.AuthenticationError = AuthenticationError;
1847
1955
  exports.BadRequestError = BadRequestError;
1848
1956
  exports.ConflictError = ConflictError;
1957
+ exports.FolderParticipants = FolderParticipants;
1849
1958
  exports.Folders = Folders;
1850
1959
  exports.FollowUps = FollowUps;
1851
1960
  exports.InternalServerError = InternalServerError;
@@ -1857,6 +1966,7 @@ exports.Page = Page;
1857
1966
  exports.PayloadTooLargeError = PayloadTooLargeError;
1858
1967
  exports.PermissionDeniedError = PermissionDeniedError;
1859
1968
  exports.RateLimitError = RateLimitError;
1969
+ exports.ReportSubscriptions = ReportSubscriptions;
1860
1970
  exports.Tools = Tools;
1861
1971
  exports.ToolsPage = ToolsPage;
1862
1972
  exports.UnprocessableEntityError = UnprocessableEntityError;