@graph8/sdk 0.3.0 → 0.4.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/dist/index.mjs CHANGED
@@ -323,18 +323,133 @@ var DEFAULT_API7 = "https://be.graph8.com";
323
323
  var createSequencesClient = (apiKey, apiUrl) => {
324
324
  const baseUrl = apiUrl || DEFAULT_API7;
325
325
  const headers = () => ({ "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` });
326
+ const toQuery = (params) => {
327
+ const qs = new URLSearchParams();
328
+ for (const [k, v] of Object.entries(params)) {
329
+ if (v != null) qs.set(k, String(v));
330
+ }
331
+ const s = qs.toString();
332
+ return s ? `?${s}` : "";
333
+ };
334
+ async function list(pageOrParams, limit) {
335
+ let params;
336
+ if (typeof pageOrParams === "number") {
337
+ params = { page: pageOrParams, limit: limit ?? 50 };
338
+ } else {
339
+ params = pageOrParams ?? {};
340
+ }
341
+ const resp = await fetch(`${baseUrl}/api/v1/sequences${toQuery(params)}`, { headers: headers() });
342
+ const data = await resp.json();
343
+ return data.data || data;
344
+ }
326
345
  return {
327
- async list(page = 1, limit = 50) {
328
- const resp = await fetch(`${baseUrl}/api/v1/sequences?page=${page}&limit=${limit}`, { headers: headers() });
346
+ /** List sequences with pagination + optional status filter. */
347
+ list,
348
+ /** Get full sequence details by ID. */
349
+ async get(sequenceId) {
350
+ const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}`, { headers: headers() });
329
351
  const data = await resp.json();
330
352
  return data.data || data;
331
353
  },
354
+ /** List contacts enrolled in a sequence. Filter by state (e.g. "active", "replied"). */
355
+ async contacts(sequenceId, params = {}) {
356
+ const resp = await fetch(
357
+ `${baseUrl}/api/v1/sequences/${sequenceId}/contacts${toQuery(params)}`,
358
+ { headers: headers() }
359
+ );
360
+ return resp.json();
361
+ },
362
+ /** Add contacts to a sequence (V2 queuing). Live or drafted sequences only. */
332
363
  async add(config) {
333
- await fetch(`${baseUrl}/api/v1/sequences/${config.sequenceId}/contacts`, {
364
+ const resp = await fetch(`${baseUrl}/api/v1/sequences/${config.sequenceId}/contacts`, {
334
365
  method: "POST",
335
366
  headers: headers(),
336
367
  body: JSON.stringify({ contact_ids: config.contactIds, list_id: config.listId })
337
368
  });
369
+ const data = await resp.json();
370
+ return data.data || data;
371
+ },
372
+ /** Create a new sequence with optional steps + channels. */
373
+ async create(payload) {
374
+ const resp = await fetch(`${baseUrl}/api/v1/sequences`, {
375
+ method: "POST",
376
+ headers: headers(),
377
+ body: JSON.stringify(payload)
378
+ });
379
+ const data = await resp.json();
380
+ return data.data || data;
381
+ },
382
+ /** Update sequence metadata. Rejected (409) if sequence is in a transitional status. */
383
+ async update(sequenceId, fields) {
384
+ const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}`, {
385
+ method: "PATCH",
386
+ headers: headers(),
387
+ body: JSON.stringify(fields)
388
+ });
389
+ const data = await resp.json();
390
+ return data.data || data;
391
+ },
392
+ /** Update a single step within a sequence. */
393
+ async updateStep(sequenceId, stepId, fields) {
394
+ const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/steps/${stepId}`, {
395
+ method: "PATCH",
396
+ headers: headers(),
397
+ body: JSON.stringify(fields)
398
+ });
399
+ const data = await resp.json();
400
+ return data.data || data;
401
+ },
402
+ /** Soft-delete (archive) a sequence. */
403
+ async delete(sequenceId) {
404
+ const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}`, {
405
+ method: "DELETE",
406
+ headers: headers()
407
+ });
408
+ const data = await resp.json();
409
+ return data.data || data;
410
+ },
411
+ /** Run/start a DRAFTED sequence (V2 orchestration). */
412
+ async run(sequenceId) {
413
+ const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/run`, {
414
+ method: "POST",
415
+ headers: headers()
416
+ });
417
+ const data = await resp.json();
418
+ return data.data || data;
419
+ },
420
+ /** Pause a live sequence. */
421
+ async pause(sequenceId) {
422
+ const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/pause`, {
423
+ method: "POST",
424
+ headers: headers()
425
+ });
426
+ const data = await resp.json();
427
+ return data.data || data;
428
+ },
429
+ /** Resume a paused sequence. */
430
+ async resume(sequenceId) {
431
+ const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/resume`, {
432
+ method: "POST",
433
+ headers: headers()
434
+ });
435
+ const data = await resp.json();
436
+ return data.data || data;
437
+ },
438
+ /** Read-only sequence preview with all steps + channels (no enrollment). */
439
+ async preview(sequenceId) {
440
+ const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/preview`, {
441
+ headers: headers()
442
+ });
443
+ const data = await resp.json();
444
+ return data.data || data;
445
+ },
446
+ /** Comprehensive analytics for a sequence. */
447
+ async analytics(sequenceId) {
448
+ const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/analytics`, {
449
+ headers: headers()
450
+ });
451
+ const data = await resp.json();
452
+ return data.data || data;
338
453
  }
339
454
  };
340
455
  };
@@ -459,8 +574,116 @@ var createVoiceClient = (apiKey, apiUrl) => {
459
574
  const baseUrl = apiUrl || DEFAULT_API12;
460
575
  const headers = () => ({ "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` });
461
576
  const listeners = /* @__PURE__ */ new Map();
577
+ const toQuery = (params) => {
578
+ const qs = new URLSearchParams();
579
+ for (const [k, v] of Object.entries(params)) {
580
+ if (v != null) qs.set(k, String(v));
581
+ }
582
+ const s = qs.toString();
583
+ return s ? `?${s}` : "";
584
+ };
585
+ const dialer = {
586
+ /** List parallel-dialer sessions with filters + pagination. */
587
+ async listSessions(params = {}) {
588
+ const resp = await fetch(
589
+ `${baseUrl}/api/v1/voice/dialer/sessions${toQuery(params)}`,
590
+ { headers: headers() }
591
+ );
592
+ const data = await resp.json();
593
+ return data.data || data;
594
+ },
595
+ /** Create a parallel-dialer session in PAUSED state. SDR opens UI to start dialing. */
596
+ async createSession(payload) {
597
+ const resp = await fetch(`${baseUrl}/api/v1/voice/dialer/sessions`, {
598
+ method: "POST",
599
+ headers: headers(),
600
+ body: JSON.stringify(payload)
601
+ });
602
+ const data = await resp.json();
603
+ return data.data || data;
604
+ },
605
+ /** Pause / resume / stop a dialer session via status flip. */
606
+ async updateSessionStatus(sessionId, status) {
607
+ const resp = await fetch(
608
+ `${baseUrl}/api/v1/voice/dialer/sessions/${sessionId}/status`,
609
+ {
610
+ method: "PATCH",
611
+ headers: headers(),
612
+ body: JSON.stringify({ status })
613
+ }
614
+ );
615
+ const data = await resp.json();
616
+ return data.data || data;
617
+ },
618
+ /**
619
+ * Resume a PAUSED dialer session. Auto-fetches the next batch from the source list,
620
+ * filters already-called + phoneless rows, and forwards to voice's start-session.
621
+ * @param maxContacts 1-4 (voice caps parallel dialing at 4). Default 4.
622
+ */
623
+ async resumeSession(sessionId, maxContacts = 4) {
624
+ const resp = await fetch(
625
+ `${baseUrl}/api/v1/voice/dialer/sessions/${sessionId}/resume`,
626
+ {
627
+ method: "POST",
628
+ headers: headers(),
629
+ body: JSON.stringify({ max_contacts: maxContacts })
630
+ }
631
+ );
632
+ const data = await resp.json();
633
+ return data.data || data;
634
+ },
635
+ /** Aggregated dialer analytics (daily breakdown or total). */
636
+ async stats(params = {}) {
637
+ const resp = await fetch(
638
+ `${baseUrl}/api/v1/voice/dialer/stats${toQuery(params)}`,
639
+ { headers: headers() }
640
+ );
641
+ const data = await resp.json();
642
+ return data.data || data;
643
+ },
644
+ /** List dialer-eligible phone numbers with 7-day stats + daily limits. */
645
+ async numbers(userEmail) {
646
+ const params = userEmail ? { user_email: userEmail } : {};
647
+ const resp = await fetch(
648
+ `${baseUrl}/api/v1/voice/dialer/numbers${toQuery(params)}`,
649
+ { headers: headers() }
650
+ );
651
+ const data = await resp.json();
652
+ return data.data || data;
653
+ },
654
+ /** List missed inbound callbacks with caller / contact info. */
655
+ async missedCallbacks(limit = 50) {
656
+ const resp = await fetch(
657
+ `${baseUrl}/api/v1/voice/dialer/missed-callbacks${toQuery({ limit })}`,
658
+ { headers: headers() }
659
+ );
660
+ const data = await resp.json();
661
+ return data.data || data;
662
+ },
663
+ /** AI grading for a single dialer call (returns "pending" while in progress). */
664
+ async callGrading(roomName) {
665
+ const resp = await fetch(
666
+ `${baseUrl}/api/v1/voice/dialer/calls/${encodeURIComponent(roomName)}/grading`,
667
+ { headers: headers() }
668
+ );
669
+ const data = await resp.json();
670
+ return data.data || data;
671
+ },
672
+ /** List voice agents available for dialer sessions (capped at 100; no pagination). */
673
+ async agents(params = {}) {
674
+ const resp = await fetch(
675
+ `${baseUrl}/api/v1/voice/dialer/agents${toQuery(params)}`,
676
+ { headers: headers() }
677
+ );
678
+ const data = await resp.json();
679
+ return data.data || data;
680
+ }
681
+ };
462
682
  return {
463
- /** Start an AI voice session. */
683
+ /**
684
+ * Start an AI voice session.
685
+ * @deprecated Preview surface — for parallel-dialer flows use `voice.dialer.createSession()`.
686
+ */
464
687
  async start(config) {
465
688
  const resp = await fetch(`${baseUrl}/api/v1/voice/sessions`, {
466
689
  method: "POST",
@@ -470,7 +693,10 @@ var createVoiceClient = (apiKey, apiUrl) => {
470
693
  const data = await resp.json();
471
694
  return data.data || data;
472
695
  },
473
- /** Get call analysis for a completed session. */
696
+ /**
697
+ * Get call analysis for a completed session.
698
+ * @deprecated Preview surface — for dialer-call grading use `voice.dialer.callGrading(roomName)`.
699
+ */
474
700
  async analysis(sessionId) {
475
701
  const resp = await fetch(`${baseUrl}/api/v1/voice/sessions/${sessionId}/analysis`, { headers: headers() });
476
702
  const data = await resp.json();
@@ -480,7 +706,9 @@ var createVoiceClient = (apiKey, apiUrl) => {
480
706
  on(event, callback) {
481
707
  if (!listeners.has(event)) listeners.set(event, []);
482
708
  listeners.get(event).push(callback);
483
- }
709
+ },
710
+ /** Parallel-dialer session control + analytics. */
711
+ dialer
484
712
  };
485
713
  };
486
714
 
@@ -1009,9 +1237,88 @@ var createDealsClient = (apiKey, apiUrl) => {
1009
1237
  };
1010
1238
  };
1011
1239
 
1240
+ // src/inbox.ts
1241
+ var DEFAULT_API22 = "https://be.graph8.com";
1242
+ var createInboxClient = (apiKey, apiUrl) => {
1243
+ const baseUrl = apiUrl || DEFAULT_API22;
1244
+ const headers = () => ({ "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` });
1245
+ const toQuery = (params) => {
1246
+ const qs = new URLSearchParams();
1247
+ for (const [k, v] of Object.entries(params)) {
1248
+ if (v != null) qs.set(k, String(v));
1249
+ }
1250
+ const s = qs.toString();
1251
+ return s ? `?${s}` : "";
1252
+ };
1253
+ return {
1254
+ /** List inbox threads across email, SMS, and LinkedIn. */
1255
+ async list(params = {}) {
1256
+ const resp = await fetch(`${baseUrl}/api/v1/inbox${toQuery(params)}`, { headers: headers() });
1257
+ return resp.json();
1258
+ },
1259
+ /** Get a single inbox thread. Defaults to email channel. */
1260
+ async get(replyId, channel = "email") {
1261
+ const resp = await fetch(
1262
+ `${baseUrl}/api/v1/inbox/${replyId}${toQuery({ channel })}`,
1263
+ { headers: headers() }
1264
+ );
1265
+ const data = await resp.json();
1266
+ return data.data || data;
1267
+ },
1268
+ /** Assign a user to an inbox thread. */
1269
+ async assign(replyId, assigneeEmail, channel = "email") {
1270
+ const resp = await fetch(
1271
+ `${baseUrl}/api/v1/inbox/${replyId}/assign${toQuery({ channel })}`,
1272
+ {
1273
+ method: "POST",
1274
+ headers: headers(),
1275
+ body: JSON.stringify({ assignee_email: assigneeEmail })
1276
+ }
1277
+ );
1278
+ const data = await resp.json();
1279
+ return data.data || data;
1280
+ },
1281
+ /** Attach tag IDs to an inbox thread. */
1282
+ async tag(replyId, tagIds, channel = "email") {
1283
+ const resp = await fetch(
1284
+ `${baseUrl}/api/v1/inbox/${replyId}/tag${toQuery({ channel })}`,
1285
+ {
1286
+ method: "POST",
1287
+ headers: headers(),
1288
+ body: JSON.stringify({ tag_ids: tagIds })
1289
+ }
1290
+ );
1291
+ const data = await resp.json();
1292
+ return data.data || data;
1293
+ },
1294
+ /**
1295
+ * Generate an AI draft reply for a thread.
1296
+ * Charges credits — server returns 402 if balance is insufficient.
1297
+ */
1298
+ async draft(replyId, channel = "email") {
1299
+ const resp = await fetch(
1300
+ `${baseUrl}/api/v1/inbox/${replyId}/draft${toQuery({ channel })}`,
1301
+ { headers: headers() }
1302
+ );
1303
+ const data = await resp.json();
1304
+ return data.data || data;
1305
+ },
1306
+ /** Send a reply through email, SMS, or LinkedIn. */
1307
+ async send(replyId, payload) {
1308
+ const resp = await fetch(`${baseUrl}/api/v1/inbox/${replyId}/send`, {
1309
+ method: "POST",
1310
+ headers: headers(),
1311
+ body: JSON.stringify(payload)
1312
+ });
1313
+ const data = await resp.json();
1314
+ return data.data || data;
1315
+ }
1316
+ };
1317
+ };
1318
+
1012
1319
  // src/core.ts
1013
1320
  var DEFAULT_HOST = "https://t.graph8.com";
1014
- var DEFAULT_API22 = "https://be.graph8.com";
1321
+ var DEFAULT_API23 = "https://be.graph8.com";
1015
1322
  var G8 = class {
1016
1323
  constructor() {
1017
1324
  /** @internal */
@@ -1060,6 +1367,8 @@ var G8 = class {
1060
1367
  this._fields = null;
1061
1368
  /** @internal */
1062
1369
  this._deals = null;
1370
+ /** @internal */
1371
+ this._inbox = null;
1063
1372
  }
1064
1373
  /**
1065
1374
  * Initialize the graph8 SDK. Must be called before any other method.
@@ -1074,7 +1383,7 @@ var G8 = class {
1074
1383
  debug: config.debug
1075
1384
  });
1076
1385
  }
1077
- const apiUrl = config.apiUrl || DEFAULT_API22;
1386
+ const apiUrl = config.apiUrl || DEFAULT_API23;
1078
1387
  const writeKey = config.writeKey || "";
1079
1388
  const apiKey = config.apiKey || "";
1080
1389
  if (writeKey) {
@@ -1101,6 +1410,7 @@ var G8 = class {
1101
1410
  this._tasks = createTasksClient(apiKey, apiUrl);
1102
1411
  this._fields = createFieldsClient(apiKey, apiUrl);
1103
1412
  this._deals = createDealsClient(apiKey, apiUrl);
1413
+ this._inbox = createInboxClient(apiKey, apiUrl);
1104
1414
  this._signals = createSignalsClient(apiKey, true, apiUrl);
1105
1415
  }
1106
1416
  }
@@ -1227,6 +1537,11 @@ var G8 = class {
1227
1537
  this._assertKey("deals");
1228
1538
  return this._deals;
1229
1539
  }
1540
+ /** Multi-channel inbox — read + reply across email, SMS, LinkedIn (requires API key). */
1541
+ get inbox() {
1542
+ this._assertKey("inbox");
1543
+ return this._inbox;
1544
+ }
1230
1545
  /** Whether the SDK has been initialized. */
1231
1546
  get initialized() {
1232
1547
  return this.config !== null;