@graph8/sdk 0.5.1 → 0.7.1

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/react.js CHANGED
@@ -275,11 +275,11 @@ var createCalendarClient = (apiUrl) => {
275
275
  return data.slots || data.data || [];
276
276
  },
277
277
  /** Book a meeting programmatically. */
278
- async book(request) {
278
+ async book(request2) {
279
279
  const resp = await fetch(`${baseUrl}/appointments/public/bookings`, {
280
280
  method: "POST",
281
281
  headers: { "Content-Type": "application/json" },
282
- body: JSON.stringify(request)
282
+ body: JSON.stringify(request2)
283
283
  });
284
284
  if (!resp.ok) return null;
285
285
  const data = await resp.json();
@@ -295,54 +295,164 @@ var createCalendarClient = (apiUrl) => {
295
295
  };
296
296
  };
297
297
 
298
+ // src/http.ts
299
+ var G8Error = class _G8Error extends Error {
300
+ constructor(args) {
301
+ super(args.message);
302
+ this.name = "G8Error";
303
+ this.status = args.status;
304
+ this.type = args.type;
305
+ this.code = args.code;
306
+ this.requestId = args.requestId;
307
+ this.detail = args.detail;
308
+ this.retryable = args.retryable ?? false;
309
+ Object.setPrototypeOf(this, _G8Error.prototype);
310
+ }
311
+ };
312
+ function isRetryableStatus(status) {
313
+ return status === 429 || status >= 500 && status <= 599;
314
+ }
315
+ function parseRetryAfter(header, nowMs = Date.now()) {
316
+ if (!header) return null;
317
+ const secs = Number(header);
318
+ if (Number.isFinite(secs) && secs >= 0) return Math.round(secs * 1e3);
319
+ const dateMs = Date.parse(header);
320
+ if (!Number.isNaN(dateMs)) {
321
+ const delta = dateMs - nowMs;
322
+ return delta > 0 ? delta : 0;
323
+ }
324
+ return null;
325
+ }
326
+ function backoffDelayMs(attempt, baseMs = 200, rand = Math.random) {
327
+ const expo = Math.min(baseMs * 2 ** attempt, 1e4);
328
+ return Math.floor(expo / 2 + rand() * (expo / 2));
329
+ }
330
+ function defaultSleep(ms) {
331
+ return new Promise((resolve) => setTimeout(resolve, ms));
332
+ }
333
+ function buildQuery(query) {
334
+ if (!query) return "";
335
+ const qs = new URLSearchParams();
336
+ for (const [k, v] of Object.entries(query)) {
337
+ if (v != null) qs.set(k, String(v));
338
+ }
339
+ const s = qs.toString();
340
+ return s ? `?${s}` : "";
341
+ }
342
+ async function toG8Error(resp) {
343
+ let body = void 0;
344
+ try {
345
+ const text = await resp.text();
346
+ body = text ? JSON.parse(text) : void 0;
347
+ } catch {
348
+ body = void 0;
349
+ }
350
+ const env = body ?? {};
351
+ const message = typeof env.message === "string" && env.message || typeof env.error === "string" && env.error || `Request failed with status ${resp.status}`;
352
+ return new G8Error({
353
+ message,
354
+ status: resp.status,
355
+ type: typeof env.type === "string" && env.type || typeof env.error === "string" && env.error || "api_error",
356
+ code: typeof env.code === "string" ? env.code : String(resp.status),
357
+ requestId: typeof env.request_id === "string" && env.request_id || resp.headers.get("x-request-id") || void 0,
358
+ detail: env.detail,
359
+ retryable: isRetryableStatus(resp.status)
360
+ });
361
+ }
362
+ async function request(baseUrl, path, apiKey, opts = {}) {
363
+ const {
364
+ method = "GET",
365
+ body,
366
+ headers = {},
367
+ query,
368
+ idempotencyKey,
369
+ maxRetries = 2,
370
+ retryBaseMs = 200,
371
+ signal,
372
+ fetchImpl = fetch,
373
+ sleepImpl = defaultSleep
374
+ } = opts;
375
+ const url = baseUrl.replace(/\/+$/, "") + path + buildQuery(query);
376
+ const finalHeaders = {
377
+ "Content-Type": "application/json",
378
+ Authorization: `Bearer ${apiKey}`,
379
+ ...headers
380
+ };
381
+ if (idempotencyKey) finalHeaders["Idempotency-Key"] = idempotencyKey;
382
+ let attempt = 0;
383
+ for (; ; ) {
384
+ let resp;
385
+ try {
386
+ resp = await fetchImpl(url, {
387
+ method,
388
+ headers: finalHeaders,
389
+ body: body == null ? void 0 : JSON.stringify(body),
390
+ signal
391
+ });
392
+ } catch (err) {
393
+ if (attempt < maxRetries) {
394
+ await sleepImpl(backoffDelayMs(attempt, retryBaseMs));
395
+ attempt++;
396
+ continue;
397
+ }
398
+ throw new G8Error({
399
+ message: `Network error: ${err?.message ?? "request failed"}`,
400
+ status: 0,
401
+ type: "network_error",
402
+ retryable: true
403
+ });
404
+ }
405
+ if (resp.ok) {
406
+ const text = await resp.text();
407
+ return text ? JSON.parse(text) : void 0;
408
+ }
409
+ if (isRetryableStatus(resp.status) && attempt < maxRetries) {
410
+ const retryAfter = parseRetryAfter(resp.headers.get("retry-after"));
411
+ const delay = retryAfter != null ? retryAfter : backoffDelayMs(attempt, retryBaseMs);
412
+ await sleepImpl(delay);
413
+ attempt++;
414
+ continue;
415
+ }
416
+ throw await toG8Error(resp);
417
+ }
418
+ }
419
+
298
420
  // src/enrich.ts
299
421
  var DEFAULT_API6 = "https://be.graph8.com";
300
422
  var createEnrichClient = (apiKey, apiUrl) => {
301
423
  const baseUrl = apiUrl || DEFAULT_API6;
302
- const headers = () => ({
303
- "Content-Type": "application/json",
304
- Authorization: `Bearer ${apiKey}`
305
- });
306
424
  return {
307
425
  /** Look up a person by email, LinkedIn, or name + company. Costs 1 credit. */
308
426
  async person(params) {
309
- const resp = await fetch(`${baseUrl}/api/v1/enrichment/lookup/person`, {
427
+ const resp = await request(baseUrl, "/api/v1/enrichment/lookup/person", apiKey, {
310
428
  method: "POST",
311
- headers: headers(),
312
- body: JSON.stringify(params)
429
+ body: params
313
430
  });
314
- const data = await resp.json();
315
- return data.data || data;
431
+ return resp.data ?? resp;
316
432
  },
317
433
  /** Look up a company by domain or name. Costs 1 credit. */
318
434
  async company(params) {
319
- const resp = await fetch(`${baseUrl}/api/v1/enrichment/lookup/company`, {
435
+ const resp = await request(baseUrl, "/api/v1/enrichment/lookup/company", apiKey, {
320
436
  method: "POST",
321
- headers: headers(),
322
- body: JSON.stringify(params)
437
+ body: params
323
438
  });
324
- const data = await resp.json();
325
- return data.data || data;
439
+ return resp.data ?? resp;
326
440
  },
327
441
  /** Verify an email address. Costs 1 credit. */
328
442
  async verifyEmail(email) {
329
- const resp = await fetch(`${baseUrl}/api/v1/enrichment/verify-email`, {
443
+ const resp = await request(baseUrl, "/api/v1/enrichment/verify-email", apiKey, {
330
444
  method: "POST",
331
- headers: headers(),
332
- body: JSON.stringify({ email })
445
+ body: { email }
333
446
  });
334
- const data = await resp.json();
335
- return data.data || data;
447
+ return resp.data ?? resp;
336
448
  },
337
449
  /** Search 300M+ contacts with filters. Credits charged per result. */
338
450
  async search(filters, page = 1, limit = 25) {
339
- const resp = await fetch(`${baseUrl}/api/v1/search/contacts`, {
451
+ const resp = await request(baseUrl, "/api/v1/search/contacts", apiKey, {
340
452
  method: "POST",
341
- headers: headers(),
342
- body: JSON.stringify({ filters, page, limit })
453
+ body: { filters, page, limit }
343
454
  });
344
- const data = await resp.json();
345
- return data.data || data;
455
+ return resp.data ?? resp;
346
456
  }
347
457
  };
348
458
  };
@@ -351,15 +461,6 @@ var createEnrichClient = (apiKey, apiUrl) => {
351
461
  var DEFAULT_API7 = "https://be.graph8.com";
352
462
  var createSequencesClient = (apiKey, apiUrl) => {
353
463
  const baseUrl = apiUrl || DEFAULT_API7;
354
- const headers = () => ({ "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` });
355
- const toQuery = (params) => {
356
- const qs = new URLSearchParams();
357
- for (const [k, v] of Object.entries(params)) {
358
- if (v != null) qs.set(k, String(v));
359
- }
360
- const s = qs.toString();
361
- return s ? `?${s}` : "";
362
- };
363
464
  async function list(pageOrParams, limit) {
364
465
  let params;
365
466
  if (typeof pageOrParams === "number") {
@@ -367,118 +468,111 @@ var createSequencesClient = (apiKey, apiUrl) => {
367
468
  } else {
368
469
  params = pageOrParams ?? {};
369
470
  }
370
- const resp = await fetch(`${baseUrl}/api/v1/sequences${toQuery(params)}`, { headers: headers() });
371
- const data = await resp.json();
372
- return data.data || data;
471
+ const resp = await request(baseUrl, "/api/v1/sequences", apiKey, {
472
+ query: params
473
+ });
474
+ return resp.data ?? resp;
373
475
  }
374
476
  return {
375
477
  /** List sequences with pagination + optional status filter. */
376
478
  list,
377
479
  /** Get full sequence details by ID. */
378
480
  async get(sequenceId) {
379
- const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}`, { headers: headers() });
380
- const data = await resp.json();
381
- return data.data || data;
481
+ const resp = await request(baseUrl, `/api/v1/sequences/${sequenceId}`, apiKey);
482
+ return resp.data ?? resp;
382
483
  },
383
484
  /** List contacts enrolled in a sequence. Filter by state (e.g. "active", "replied"). */
384
485
  async contacts(sequenceId, params = {}) {
385
- const resp = await fetch(
386
- `${baseUrl}/api/v1/sequences/${sequenceId}/contacts${toQuery(params)}`,
387
- { headers: headers() }
388
- );
389
- return resp.json();
486
+ return request(baseUrl, `/api/v1/sequences/${sequenceId}/contacts`, apiKey, {
487
+ query: params
488
+ });
390
489
  },
391
490
  /** Add contacts to a sequence (V2 queuing). Live or drafted sequences only. */
392
491
  async add(config) {
393
- const resp = await fetch(`${baseUrl}/api/v1/sequences/${config.sequenceId}/contacts`, {
394
- method: "POST",
395
- headers: headers(),
396
- body: JSON.stringify({ contact_ids: config.contactIds, list_id: config.listId })
397
- });
398
- const data = await resp.json();
399
- return data.data || data;
492
+ const resp = await request(
493
+ baseUrl,
494
+ `/api/v1/sequences/${config.sequenceId}/contacts`,
495
+ apiKey,
496
+ { method: "POST", body: { contact_ids: config.contactIds, list_id: config.listId } }
497
+ );
498
+ return resp.data ?? resp;
400
499
  },
401
500
  /** Create a new sequence with optional steps + channels. */
402
501
  async create(payload) {
403
- const resp = await fetch(`${baseUrl}/api/v1/sequences`, {
502
+ const resp = await request(baseUrl, "/api/v1/sequences", apiKey, {
404
503
  method: "POST",
405
- headers: headers(),
406
- body: JSON.stringify(payload)
504
+ body: payload
407
505
  });
408
- const data = await resp.json();
409
- return data.data || data;
506
+ return resp.data ?? resp;
410
507
  },
411
508
  /** Update sequence metadata. Rejected (409) if sequence is in a transitional status. */
412
509
  async update(sequenceId, fields) {
413
- const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}`, {
510
+ const resp = await request(baseUrl, `/api/v1/sequences/${sequenceId}`, apiKey, {
414
511
  method: "PATCH",
415
- headers: headers(),
416
- body: JSON.stringify(fields)
512
+ body: fields
417
513
  });
418
- const data = await resp.json();
419
- return data.data || data;
514
+ return resp.data ?? resp;
420
515
  },
421
516
  /** Update a single step within a sequence. */
422
517
  async updateStep(sequenceId, stepId, fields) {
423
- const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/steps/${stepId}`, {
424
- method: "PATCH",
425
- headers: headers(),
426
- body: JSON.stringify(fields)
427
- });
428
- const data = await resp.json();
429
- return data.data || data;
518
+ const resp = await request(
519
+ baseUrl,
520
+ `/api/v1/sequences/${sequenceId}/steps/${stepId}`,
521
+ apiKey,
522
+ { method: "PATCH", body: fields }
523
+ );
524
+ return resp.data ?? resp;
430
525
  },
431
526
  /** Soft-delete (archive) a sequence. */
432
527
  async delete(sequenceId) {
433
- const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}`, {
434
- method: "DELETE",
435
- headers: headers()
528
+ const resp = await request(baseUrl, `/api/v1/sequences/${sequenceId}`, apiKey, {
529
+ method: "DELETE"
436
530
  });
437
- const data = await resp.json();
438
- return data.data || data;
531
+ return resp.data ?? resp;
439
532
  },
440
533
  /** Run/start a DRAFTED sequence (V2 orchestration). */
441
534
  async run(sequenceId) {
442
- const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/run`, {
443
- method: "POST",
444
- headers: headers()
445
- });
446
- const data = await resp.json();
447
- return data.data || data;
535
+ const resp = await request(
536
+ baseUrl,
537
+ `/api/v1/sequences/${sequenceId}/run`,
538
+ apiKey,
539
+ { method: "POST" }
540
+ );
541
+ return resp.data ?? resp;
448
542
  },
449
543
  /** Pause a live sequence. */
450
544
  async pause(sequenceId) {
451
- const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/pause`, {
452
- method: "POST",
453
- headers: headers()
454
- });
455
- const data = await resp.json();
456
- return data.data || data;
545
+ const resp = await request(
546
+ baseUrl,
547
+ `/api/v1/sequences/${sequenceId}/pause`,
548
+ apiKey,
549
+ { method: "POST" }
550
+ );
551
+ return resp.data ?? resp;
457
552
  },
458
553
  /** Resume a paused sequence. */
459
554
  async resume(sequenceId) {
460
- const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/resume`, {
461
- method: "POST",
462
- headers: headers()
463
- });
464
- const data = await resp.json();
465
- return data.data || data;
555
+ const resp = await request(
556
+ baseUrl,
557
+ `/api/v1/sequences/${sequenceId}/resume`,
558
+ apiKey,
559
+ { method: "POST" }
560
+ );
561
+ return resp.data ?? resp;
466
562
  },
467
563
  /** Read-only sequence preview with all steps + channels (no enrollment). */
468
564
  async preview(sequenceId) {
469
- const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/preview`, {
470
- headers: headers()
471
- });
472
- const data = await resp.json();
473
- return data.data || data;
565
+ const resp = await request(baseUrl, `/api/v1/sequences/${sequenceId}/preview`, apiKey);
566
+ return resp.data ?? resp;
474
567
  },
475
568
  /** Comprehensive analytics for a sequence. */
476
569
  async analytics(sequenceId) {
477
- const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/analytics`, {
478
- headers: headers()
479
- });
480
- const data = await resp.json();
481
- return data.data || data;
570
+ const resp = await request(
571
+ baseUrl,
572
+ `/api/v1/sequences/${sequenceId}/analytics`,
573
+ apiKey
574
+ );
575
+ return resp.data ?? resp;
482
576
  }
483
577
  };
484
578
  };
@@ -487,37 +581,30 @@ var createSequencesClient = (apiKey, apiUrl) => {
487
581
  var DEFAULT_API8 = "https://be.graph8.com";
488
582
  var createCampaignsClient = (apiKey, apiUrl) => {
489
583
  const baseUrl = apiUrl || DEFAULT_API8;
490
- const headers = () => ({ "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` });
491
584
  return {
492
585
  async list(page = 1, limit = 50) {
493
- const resp = await fetch(`${baseUrl}/api/v1/campaigns?page=${page}&limit=${limit}`, { headers: headers() });
494
- const data = await resp.json();
495
- return data.data || data;
586
+ const resp = await request(baseUrl, "/api/v1/campaigns", apiKey, {
587
+ query: { page, limit }
588
+ });
589
+ return resp.data ?? resp;
496
590
  },
497
591
  async get(campaignId) {
498
- const resp = await fetch(`${baseUrl}/api/v1/campaigns/${campaignId}`, { headers: headers() });
499
- const data = await resp.json();
500
- return data.data || data;
592
+ const resp = await request(baseUrl, `/api/v1/campaigns/${campaignId}`, apiKey);
593
+ return resp.data ?? resp;
501
594
  },
502
595
  async create(config) {
503
- const resp = await fetch(`${baseUrl}/api/v1/campaigns`, {
596
+ const resp = await request(baseUrl, "/api/v1/campaigns", apiKey, {
504
597
  method: "POST",
505
- headers: headers(),
506
- body: JSON.stringify(config)
598
+ body: config
507
599
  });
508
- const data = await resp.json();
509
- return data.data || data;
600
+ return resp.data ?? resp;
510
601
  },
511
602
  async launch(campaignId) {
512
- await fetch(`${baseUrl}/api/v1/campaigns/${campaignId}/launch`, {
513
- method: "POST",
514
- headers: headers()
515
- });
603
+ await request(baseUrl, `/api/v1/campaigns/${campaignId}/launch`, apiKey, { method: "POST" });
516
604
  },
517
605
  async stats(campaignId) {
518
- const resp = await fetch(`${baseUrl}/api/v1/campaigns/${campaignId}/stats`, { headers: headers() });
519
- const data = await resp.json();
520
- return data.data || data;
606
+ const resp = await request(baseUrl, `/api/v1/campaigns/${campaignId}/stats`, apiKey);
607
+ return resp.data ?? resp;
521
608
  }
522
609
  };
523
610
  };
@@ -526,25 +613,21 @@ var createCampaignsClient = (apiKey, apiUrl) => {
526
613
  var DEFAULT_API9 = "https://be.graph8.com";
527
614
  var createIntegrationsClient = (apiKey, apiUrl) => {
528
615
  const baseUrl = apiUrl || DEFAULT_API9;
529
- const headers = () => ({ "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` });
530
616
  return {
531
617
  async list() {
532
- const resp = await fetch(`${baseUrl}/api/v1/integrations`, { headers: headers() });
533
- const data = await resp.json();
534
- return data.data || data;
618
+ const resp = await request(baseUrl, "/api/v1/integrations", apiKey);
619
+ return resp.data ?? resp;
535
620
  },
536
621
  async connect(provider, config) {
537
- await fetch(`${baseUrl}/api/v1/integrations/connect`, {
622
+ await request(baseUrl, "/api/v1/integrations/connect", apiKey, {
538
623
  method: "POST",
539
- headers: headers(),
540
- body: JSON.stringify({ provider, ...config })
624
+ body: { provider, ...config }
541
625
  });
542
626
  },
543
627
  async sync(provider, config) {
544
- await fetch(`${baseUrl}/api/v1/integrations/sync`, {
628
+ await request(baseUrl, "/api/v1/integrations/sync", apiKey, {
545
629
  method: "POST",
546
- headers: headers(),
547
- body: JSON.stringify({ provider, ...config })
630
+ body: { provider, ...config }
548
631
  });
549
632
  }
550
633
  };
@@ -584,15 +667,12 @@ var createSignalsClient = (key, isApiKey, apiUrl) => {
584
667
  var DEFAULT_API11 = "https://be.graph8.com";
585
668
  var createAnalyticsClient = (apiKey, apiUrl) => {
586
669
  const baseUrl = apiUrl || DEFAULT_API11;
587
- const headers = () => ({ "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` });
588
670
  return {
589
671
  async overview(config) {
590
- const params = new URLSearchParams();
591
- if (config?.period) params.set("period", config.period);
592
- const resp = await fetch(`${baseUrl}/api/v1/analytics/overview?${params.toString()}`, { headers: headers() });
593
- if (!resp.ok) return { visitors: 0, contacts_created: 0, emails_sent: 0, emails_opened: 0, replies: 0, meetings_booked: 0, period: config?.period || "30d" };
594
- const data = await resp.json();
595
- return data.data || data;
672
+ const resp = await request(baseUrl, "/api/v1/analytics/overview", apiKey, {
673
+ query: { period: config?.period }
674
+ });
675
+ return resp.data ?? resp;
596
676
  }
597
677
  };
598
678
  };
@@ -601,48 +681,37 @@ var createAnalyticsClient = (apiKey, apiUrl) => {
601
681
  var DEFAULT_API12 = "https://be.graph8.com";
602
682
  var createVoiceClient = (apiKey, apiUrl) => {
603
683
  const baseUrl = apiUrl || DEFAULT_API12;
604
- const headers = () => ({ "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` });
605
684
  const listeners = /* @__PURE__ */ new Map();
606
- const toQuery = (params) => {
607
- const qs = new URLSearchParams();
608
- for (const [k, v] of Object.entries(params)) {
609
- if (v != null) qs.set(k, String(v));
610
- }
611
- const s = qs.toString();
612
- return s ? `?${s}` : "";
613
- };
614
685
  const dialer = {
615
686
  /** List parallel-dialer sessions with filters + pagination. */
616
687
  async listSessions(params = {}) {
617
- const resp = await fetch(
618
- `${baseUrl}/api/v1/voice/dialer/sessions${toQuery(params)}`,
619
- { headers: headers() }
688
+ const resp = await request(
689
+ baseUrl,
690
+ "/api/v1/voice/dialer/sessions",
691
+ apiKey,
692
+ { query: params }
620
693
  );
621
- const data = await resp.json();
622
- return data.data || data;
694
+ return resp.data ?? resp;
623
695
  },
624
696
  /** Create a parallel-dialer session in PAUSED state. SDR opens UI to start dialing. */
625
697
  async createSession(payload) {
626
- const resp = await fetch(`${baseUrl}/api/v1/voice/dialer/sessions`, {
627
- method: "POST",
628
- headers: headers(),
629
- body: JSON.stringify(payload)
630
- });
631
- const data = await resp.json();
632
- return data.data || data;
698
+ const resp = await request(
699
+ baseUrl,
700
+ "/api/v1/voice/dialer/sessions",
701
+ apiKey,
702
+ { method: "POST", body: payload }
703
+ );
704
+ return resp.data ?? resp;
633
705
  },
634
706
  /** Pause / resume / stop a dialer session via status flip. */
635
707
  async updateSessionStatus(sessionId, status) {
636
- const resp = await fetch(
637
- `${baseUrl}/api/v1/voice/dialer/sessions/${sessionId}/status`,
638
- {
639
- method: "PATCH",
640
- headers: headers(),
641
- body: JSON.stringify({ status })
642
- }
708
+ const resp = await request(
709
+ baseUrl,
710
+ `/api/v1/voice/dialer/sessions/${sessionId}/status`,
711
+ apiKey,
712
+ { method: "PATCH", body: { status } }
643
713
  );
644
- const data = await resp.json();
645
- return data.data || data;
714
+ return resp.data ?? resp;
646
715
  },
647
716
  /**
648
717
  * Resume a PAUSED dialer session. Auto-fetches the next batch from the source list,
@@ -650,94 +719,97 @@ var createVoiceClient = (apiKey, apiUrl) => {
650
719
  * @param maxContacts 1-4 (voice caps parallel dialing at 4). Default 4.
651
720
  */
652
721
  async resumeSession(sessionId, maxContacts = 4) {
653
- const resp = await fetch(
654
- `${baseUrl}/api/v1/voice/dialer/sessions/${sessionId}/resume`,
655
- {
656
- method: "POST",
657
- headers: headers(),
658
- body: JSON.stringify({ max_contacts: maxContacts })
659
- }
722
+ const resp = await request(
723
+ baseUrl,
724
+ `/api/v1/voice/dialer/sessions/${sessionId}/resume`,
725
+ apiKey,
726
+ { method: "POST", body: { max_contacts: maxContacts } }
660
727
  );
661
- const data = await resp.json();
662
- return data.data || data;
728
+ return resp.data ?? resp;
663
729
  },
664
730
  /** Aggregated dialer analytics (daily breakdown or total). */
665
731
  async stats(params = {}) {
666
- const resp = await fetch(
667
- `${baseUrl}/api/v1/voice/dialer/stats${toQuery(params)}`,
668
- { headers: headers() }
732
+ const resp = await request(
733
+ baseUrl,
734
+ "/api/v1/voice/dialer/stats",
735
+ apiKey,
736
+ { query: params }
669
737
  );
670
- const data = await resp.json();
671
- return data.data || data;
738
+ return resp.data ?? resp;
672
739
  },
673
740
  /** List dialer-eligible phone numbers with 7-day stats + daily limits. */
674
741
  async numbers(userEmail) {
675
- const params = userEmail ? { user_email: userEmail } : {};
676
- const resp = await fetch(
677
- `${baseUrl}/api/v1/voice/dialer/numbers${toQuery(params)}`,
678
- { headers: headers() }
742
+ const resp = await request(
743
+ baseUrl,
744
+ "/api/v1/voice/dialer/numbers",
745
+ apiKey,
746
+ { query: userEmail ? { user_email: userEmail } : {} }
679
747
  );
680
- const data = await resp.json();
681
- return data.data || data;
748
+ return resp.data ?? resp;
682
749
  },
683
750
  /** List missed inbound callbacks with caller / contact info. */
684
751
  async missedCallbacks(limit = 50) {
685
- const resp = await fetch(
686
- `${baseUrl}/api/v1/voice/dialer/missed-callbacks${toQuery({ limit })}`,
687
- { headers: headers() }
752
+ const resp = await request(
753
+ baseUrl,
754
+ "/api/v1/voice/dialer/missed-callbacks",
755
+ apiKey,
756
+ { query: { limit } }
688
757
  );
689
- const data = await resp.json();
690
- return data.data || data;
758
+ return resp.data ?? resp;
691
759
  },
692
760
  /** AI grading for a single dialer call (returns "pending" while in progress). */
693
761
  async callGrading(roomName) {
694
- const resp = await fetch(
695
- `${baseUrl}/api/v1/voice/dialer/calls/${encodeURIComponent(roomName)}/grading`,
696
- { headers: headers() }
762
+ const resp = await request(
763
+ baseUrl,
764
+ `/api/v1/voice/dialer/calls/${encodeURIComponent(roomName)}/grading`,
765
+ apiKey
697
766
  );
698
- const data = await resp.json();
699
- return data.data || data;
767
+ return resp.data ?? resp;
700
768
  },
701
769
  /** List voice agents available for dialer sessions (capped at 100; no pagination). */
702
770
  async agents(params = {}) {
703
- const resp = await fetch(
704
- `${baseUrl}/api/v1/voice/dialer/agents${toQuery(params)}`,
705
- { headers: headers() }
771
+ const resp = await request(
772
+ baseUrl,
773
+ "/api/v1/voice/dialer/agents",
774
+ apiKey,
775
+ { query: params }
706
776
  );
707
- const data = await resp.json();
708
- return data.data || data;
777
+ return resp.data ?? resp;
709
778
  },
710
779
  /** Fetch the full transcript for a single dialer call. */
711
780
  async callTranscript(roomName) {
712
- const resp = await fetch(
713
- `${baseUrl}/api/v1/voice/dialer/calls/${encodeURIComponent(roomName)}/transcript`,
714
- { headers: headers() }
781
+ return request(
782
+ baseUrl,
783
+ `/api/v1/voice/dialer/calls/${encodeURIComponent(roomName)}/transcript`,
784
+ apiKey
715
785
  );
716
- return resp.json();
717
786
  },
718
787
  /** List dialer calls — pass `contact_id` or `user_email` to scope. */
719
788
  async listCalls(params = {}) {
720
- const resp = await fetch(
721
- `${baseUrl}/api/v1/voice/dialer/calls${toQuery(params)}`,
722
- { headers: headers() }
789
+ return request(
790
+ baseUrl,
791
+ "/api/v1/voice/dialer/calls",
792
+ apiKey,
793
+ { query: params }
723
794
  );
724
- return resp.json();
725
795
  },
726
796
  /** Convenience: list calls for a single contact. */
727
797
  async listCallsForContact(contactId, extra = {}) {
728
- const resp = await fetch(
729
- `${baseUrl}/api/v1/voice/dialer/calls${toQuery({ contact_id: contactId, ...extra })}`,
730
- { headers: headers() }
798
+ return request(
799
+ baseUrl,
800
+ "/api/v1/voice/dialer/calls",
801
+ apiKey,
802
+ { query: { contact_id: contactId, ...extra } }
731
803
  );
732
- return resp.json();
733
804
  },
734
805
  /** Convenience: list calls placed by a specific SDR. */
735
806
  async listCallsForSdr(userEmail, extra = {}) {
736
- const resp = await fetch(
737
- `${baseUrl}/api/v1/voice/dialer/calls${toQuery({ user_email: userEmail, ...extra })}`,
738
- { headers: headers() }
807
+ return request(
808
+ baseUrl,
809
+ "/api/v1/voice/dialer/calls",
810
+ apiKey,
811
+ { query: { user_email: userEmail, ...extra } }
739
812
  );
740
- return resp.json();
741
813
  }
742
814
  };
743
815
  return {
@@ -746,22 +818,25 @@ var createVoiceClient = (apiKey, apiUrl) => {
746
818
  * @deprecated Preview surface — for parallel-dialer flows use `voice.dialer.createSession()`.
747
819
  */
748
820
  async start(config) {
749
- const resp = await fetch(`${baseUrl}/api/v1/voice/sessions`, {
750
- method: "POST",
751
- headers: headers(),
752
- body: JSON.stringify(config)
753
- });
754
- const data = await resp.json();
755
- return data.data || data;
821
+ const resp = await request(
822
+ baseUrl,
823
+ "/api/v1/voice/sessions",
824
+ apiKey,
825
+ { method: "POST", body: config }
826
+ );
827
+ return resp.data ?? resp;
756
828
  },
757
829
  /**
758
830
  * Get call analysis for a completed session.
759
831
  * @deprecated Preview surface — for dialer-call grading use `voice.dialer.callGrading(roomName)`.
760
832
  */
761
833
  async analysis(sessionId) {
762
- const resp = await fetch(`${baseUrl}/api/v1/voice/sessions/${sessionId}/analysis`, { headers: headers() });
763
- const data = await resp.json();
764
- return data.data || data;
834
+ const resp = await request(
835
+ baseUrl,
836
+ `/api/v1/voice/sessions/${sessionId}/analysis`,
837
+ apiKey
838
+ );
839
+ return resp.data ?? resp;
765
840
  },
766
841
  /** Listen for voice events. */
767
842
  on(event, callback) {
@@ -777,75 +852,128 @@ var createVoiceClient = (apiKey, apiUrl) => {
777
852
  var DEFAULT_API13 = "https://be.graph8.com";
778
853
  var createPagesClient = (apiKey, apiUrl) => {
779
854
  const baseUrl = apiUrl || DEFAULT_API13;
780
- const headers = () => ({ "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` });
781
855
  return {
782
856
  /** Clone a landing page from any URL. */
783
857
  async clone(url) {
784
- const resp = await fetch(`${baseUrl}/api/v1/landing-pages/clone-url`, {
858
+ const resp = await request(baseUrl, "/api/v1/landing-pages/clone-url", apiKey, {
785
859
  method: "POST",
786
- headers: headers(),
787
- body: JSON.stringify({ url })
860
+ body: { url }
788
861
  });
789
- const data = await resp.json();
790
- return data.data || data;
862
+ return resp.data ?? resp;
791
863
  },
792
864
  /** Create a landing page from a template. */
793
865
  async create(config) {
794
- const resp = await fetch(`${baseUrl}/api/v1/landing-pages`, {
866
+ const resp = await request(baseUrl, "/api/v1/landing-pages", apiKey, {
795
867
  method: "POST",
796
- headers: headers(),
797
- body: JSON.stringify(config)
868
+ body: config
798
869
  });
799
- const data = await resp.json();
800
- return data.data || data;
870
+ return resp.data ?? resp;
801
871
  },
802
872
  /** Publish a landing page to CDN. */
803
873
  async publish(pageId) {
804
- const resp = await fetch(`${baseUrl}/api/v1/landing-pages/${pageId}/publish`, {
805
- method: "POST",
806
- headers: headers()
807
- });
808
- const data = await resp.json();
874
+ const data = await request(
875
+ baseUrl,
876
+ `/api/v1/landing-pages/${pageId}/publish`,
877
+ apiKey,
878
+ { method: "POST" }
879
+ );
809
880
  return { url: data.published_url || data.data?.published_url || "" };
810
881
  }
811
882
  };
812
883
  };
813
884
 
814
885
  // src/webhooks.ts
886
+ var import_node_crypto = require("crypto");
815
887
  var DEFAULT_API14 = "https://be.graph8.com";
816
- var createWebhooksClient = (apiKey, apiUrl) => {
888
+ var KNOWN_WEBHOOK_EVENTS = [
889
+ "campaign.created",
890
+ "campaign.updated",
891
+ "campaign.deleted",
892
+ "campaign.launched",
893
+ "campaign.paused",
894
+ "campaign.completed",
895
+ "campaign.status_changed",
896
+ "campaign.content_ready",
897
+ "document.generated",
898
+ "document.failed",
899
+ "intelligence.completed",
900
+ "intelligence.failed",
901
+ "company.enriched",
902
+ "company_intelligence.completed",
903
+ "audience.ready",
904
+ "audience.failed",
905
+ "sequence.deployed",
906
+ "sequence.started",
907
+ "sequence.paused",
908
+ "sequence.completed",
909
+ "engagement.email_sent",
910
+ "engagement.email_replied",
911
+ "engagement.email_bounced",
912
+ "engagement.email_skipped",
913
+ "engagement.call_dispatched",
914
+ "engagement.sms_sent",
915
+ "engagement.sms_replied",
916
+ "engagement.whatsapp_sent",
917
+ "engagement.linkedin_connection_sent",
918
+ "engagement.linkedin_message_sent",
919
+ "engagement.linkedin_inmail_sent",
920
+ "engagement.linkedin_reply_received",
921
+ "engagement.linkedin_connection_accepted",
922
+ "meeting.booked",
923
+ "meeting.cancelled",
924
+ "meeting.rescheduled"
925
+ ];
926
+ var WebhookSignatureError = class extends Error {
927
+ constructor(message) {
928
+ super(message);
929
+ this.name = "WebhookSignatureError";
930
+ }
931
+ };
932
+ function timingSafeEqualHex(a, b) {
933
+ if (a.length !== b.length) return false;
934
+ try {
935
+ const enc = new TextEncoder();
936
+ return (0, import_node_crypto.timingSafeEqual)(enc.encode(a), enc.encode(b));
937
+ } catch {
938
+ return false;
939
+ }
940
+ }
941
+ function constructEvent(payload, signature, timestamp, secret, opts = {}) {
942
+ if (!signature) throw new WebhookSignatureError("Missing X-Studio-Signature header");
943
+ if (timestamp === void 0 || timestamp === null || timestamp === "") {
944
+ throw new WebhookSignatureError("Missing X-Studio-Timestamp header");
945
+ }
946
+ if (!secret) throw new WebhookSignatureError("Missing signing secret");
947
+ const ts = typeof timestamp === "number" ? timestamp : parseInt(timestamp, 10);
948
+ if (!Number.isFinite(ts)) throw new WebhookSignatureError("Invalid X-Studio-Timestamp header");
949
+ const tolerance = opts.toleranceSeconds ?? 0;
950
+ if (tolerance > 0) {
951
+ const ageSeconds = Math.floor(Date.now() / 1e3) - ts;
952
+ if (Math.abs(ageSeconds) > tolerance) {
953
+ throw new WebhookSignatureError(`Timestamp outside tolerance (${ageSeconds}s > ${tolerance}s)`);
954
+ }
955
+ }
956
+ const expected = (0, import_node_crypto.createHmac)("sha256", secret).update(`${ts}.${payload}`).digest("hex");
957
+ const provided = signature.startsWith("sha256=") ? signature.slice(7) : signature;
958
+ if (!timingSafeEqualHex(expected, provided)) {
959
+ throw new WebhookSignatureError("Signature verification failed");
960
+ }
961
+ try {
962
+ return JSON.parse(payload);
963
+ } catch {
964
+ throw new WebhookSignatureError("Invalid JSON payload");
965
+ }
966
+ }
967
+ var createWebhooksClient = (_apiKey, apiUrl) => {
817
968
  const baseUrl = apiUrl || DEFAULT_API14;
818
- const headers = () => ({ "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` });
819
- const listeners = /* @__PURE__ */ new Map();
820
- let polling = false;
821
- let pollInterval = null;
822
969
  return {
823
- /** Register a listener for a webhook event. Starts polling automatically. */
824
- on(event, callback) {
825
- if (!listeners.has(event)) listeners.set(event, []);
826
- listeners.get(event).push(callback);
827
- if (!polling) {
828
- polling = true;
829
- pollInterval = setInterval(async () => {
830
- try {
831
- const resp = await fetch(`${baseUrl}/api/v1/webhooks/events?since=30s`, { headers: headers() });
832
- if (!resp.ok) return;
833
- const data = await resp.json();
834
- const events = data.data || data || [];
835
- for (const evt of events) {
836
- const cbs = listeners.get(evt.type);
837
- if (cbs) cbs.forEach((cb) => cb(evt));
838
- }
839
- } catch {
840
- }
841
- }, 3e4);
842
- }
843
- },
844
- /** Stop all webhook polling. */
845
- stop() {
846
- if (pollInterval) clearInterval(pollInterval);
847
- polling = false;
848
- listeners.clear();
970
+ /** Base URL the webhook subscription API lives under. */
971
+ baseUrl,
972
+ /** Known event types (for autocomplete / validation). */
973
+ knownEvents: KNOWN_WEBHOOK_EVENTS,
974
+ /** Verify an incoming webhook's HMAC signature and return the parsed event. */
975
+ constructEvent(payload, signature, timestamp, secret, opts) {
976
+ return constructEvent(payload, signature, timestamp, secret, opts);
849
977
  }
850
978
  };
851
979
  };
@@ -854,75 +982,59 @@ var createWebhooksClient = (apiKey, apiUrl) => {
854
982
  var DEFAULT_API15 = "https://be.graph8.com";
855
983
  var createContactsClient = (apiKey, apiUrl) => {
856
984
  const baseUrl = apiUrl || DEFAULT_API15;
857
- const headers = () => ({ "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` });
858
- const toQuery = (params) => {
859
- const qs = new URLSearchParams();
860
- for (const [k, v] of Object.entries(params)) {
861
- if (v != null) qs.set(k, String(v));
862
- }
863
- const s = qs.toString();
864
- return s ? `?${s}` : "";
865
- };
866
985
  return {
867
986
  /** List contacts with optional filters. */
868
987
  async list(params = {}) {
869
- const resp = await fetch(`${baseUrl}/api/v1/contacts${toQuery(params)}`, { headers: headers() });
870
- return resp.json();
988
+ return request(baseUrl, "/api/v1/contacts", apiKey, {
989
+ query: params
990
+ });
871
991
  },
872
992
  /** Get a single contact by ID. */
873
993
  async get(contactId) {
874
- const resp = await fetch(`${baseUrl}/api/v1/contacts/${contactId}`, { headers: headers() });
875
- const data = await resp.json();
876
- return data.data || data;
994
+ const resp = await request(baseUrl, `/api/v1/contacts/${contactId}`, apiKey);
995
+ return resp.data ?? resp;
877
996
  },
878
- /** Create a new contact. */
879
- async create(contact) {
880
- const resp = await fetch(`${baseUrl}/api/v1/contacts`, {
997
+ /**
998
+ * Create a new contact. Pass `idempotencyKey` to make a retry safe — the
999
+ * same key returns the first result instead of creating a duplicate (A6).
1000
+ */
1001
+ async create(contact, idempotencyKey) {
1002
+ const resp = await request(baseUrl, "/api/v1/contacts", apiKey, {
881
1003
  method: "POST",
882
- headers: headers(),
883
- body: JSON.stringify(contact)
1004
+ body: contact,
1005
+ idempotencyKey
884
1006
  });
885
- const data = await resp.json();
886
- return data.data || data;
1007
+ return resp.data ?? resp;
887
1008
  },
888
1009
  /** Update a contact (partial). */
889
1010
  async update(contactId, fields) {
890
- const resp = await fetch(`${baseUrl}/api/v1/contacts/${contactId}`, {
1011
+ return request(baseUrl, `/api/v1/contacts/${contactId}`, apiKey, {
891
1012
  method: "PATCH",
892
- headers: headers(),
893
- body: JSON.stringify(fields)
1013
+ body: fields
894
1014
  });
895
- return resp.json();
896
1015
  },
897
1016
  /** Delete a contact (soft-delete). */
898
1017
  async delete(contactId) {
899
- const resp = await fetch(`${baseUrl}/api/v1/contacts/${contactId}`, {
900
- method: "DELETE",
901
- headers: headers()
902
- });
903
- return resp.json();
1018
+ return request(baseUrl, `/api/v1/contacts/${contactId}`, apiKey, { method: "DELETE" });
904
1019
  },
905
1020
  /** List custom contact columns. Pass listId to include list-specific columns. */
906
1021
  async listColumns(listId) {
907
- const qs = listId != null ? `?list_id=${listId}` : "";
908
- const resp = await fetch(`${baseUrl}/api/v1/contacts/columns${qs}`, { headers: headers() });
909
- return resp.json();
1022
+ return request(baseUrl, "/api/v1/contacts/columns", apiKey, {
1023
+ query: listId != null ? { list_id: listId } : void 0
1024
+ });
910
1025
  },
911
1026
  /** Create a custom contact column. Global if list_id is omitted, list-scoped otherwise. */
912
1027
  async createColumn(params) {
913
- const body = {
914
- title: params.title,
915
- data_type: params.data_type || "text",
916
- list_id: params.list_id ?? null,
917
- created_by: params.created_by
918
- };
919
- const resp = await fetch(`${baseUrl}/api/v1/contacts/columns/create`, {
1028
+ const resp = await request(baseUrl, "/api/v1/contacts/columns/create", apiKey, {
920
1029
  method: "POST",
921
- headers: headers(),
922
- body: JSON.stringify(body)
1030
+ body: {
1031
+ title: params.title,
1032
+ data_type: params.data_type || "text",
1033
+ list_id: params.list_id ?? null,
1034
+ created_by: params.created_by
1035
+ }
923
1036
  });
924
- const data = await resp.json();
925
- return data.data || data;
1037
+ return resp.data ?? resp;
926
1038
  }
927
1039
  };
928
1040
  };
@@ -931,73 +1043,46 @@ var createContactsClient = (apiKey, apiUrl) => {
931
1043
  var DEFAULT_API16 = "https://be.graph8.com";
932
1044
  var createCompaniesClient = (apiKey, apiUrl) => {
933
1045
  const baseUrl = apiUrl || DEFAULT_API16;
934
- const headers = () => ({ "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` });
935
- const toQuery = (params) => {
936
- const qs = new URLSearchParams();
937
- for (const [k, v] of Object.entries(params)) {
938
- if (v != null) qs.set(k, String(v));
939
- }
940
- const s = qs.toString();
941
- return s ? `?${s}` : "";
942
- };
943
1046
  return {
944
1047
  /** List companies with optional filters. */
945
1048
  async list(params = {}) {
946
- const resp = await fetch(`${baseUrl}/api/v1/companies${toQuery(params)}`, { headers: headers() });
947
- return resp.json();
1049
+ return request(baseUrl, "/api/v1/companies", apiKey, { query: params });
948
1050
  },
949
1051
  /** Get a single company by ID. */
950
1052
  async get(companyId) {
951
- const resp = await fetch(`${baseUrl}/api/v1/companies/${companyId}`, { headers: headers() });
952
- const data = await resp.json();
953
- return data.data || data;
1053
+ const resp = await request(baseUrl, `/api/v1/companies/${companyId}`, apiKey);
1054
+ return resp.data ?? resp;
954
1055
  },
955
1056
  /** Get contacts belonging to a company. */
956
1057
  async contacts(companyId, limit = 50, offset = 0) {
957
- const resp = await fetch(
958
- `${baseUrl}/api/v1/companies/${companyId}/contacts?limit=${limit}&offset=${offset}`,
959
- { headers: headers() }
960
- );
961
- return resp.json();
1058
+ return request(baseUrl, `/api/v1/companies/${companyId}/contacts`, apiKey, { query: { limit, offset } });
962
1059
  },
963
1060
  /** Update a company (partial). */
964
1061
  async update(companyId, fields) {
965
- const resp = await fetch(`${baseUrl}/api/v1/companies/${companyId}`, {
966
- method: "PATCH",
967
- headers: headers(),
968
- body: JSON.stringify(fields)
969
- });
970
- return resp.json();
1062
+ return request(baseUrl, `/api/v1/companies/${companyId}`, apiKey, { method: "PATCH", body: fields });
971
1063
  },
972
1064
  /** Delete a company (soft-delete). */
973
1065
  async delete(companyId) {
974
- const resp = await fetch(`${baseUrl}/api/v1/companies/${companyId}`, {
975
- method: "DELETE",
976
- headers: headers()
977
- });
978
- return resp.json();
1066
+ return request(baseUrl, `/api/v1/companies/${companyId}`, apiKey, { method: "DELETE" });
979
1067
  },
980
1068
  /** List custom company columns. Pass listId to include list-specific columns. */
981
1069
  async listColumns(listId) {
982
- const qs = listId != null ? `?list_id=${listId}` : "";
983
- const resp = await fetch(`${baseUrl}/api/v1/companies/columns${qs}`, { headers: headers() });
984
- return resp.json();
1070
+ return request(baseUrl, "/api/v1/companies/columns", apiKey, {
1071
+ query: listId != null ? { list_id: listId } : void 0
1072
+ });
985
1073
  },
986
1074
  /** Create a custom company column. Global if list_id is omitted, list-scoped otherwise. */
987
1075
  async createColumn(params) {
988
- const body = {
989
- title: params.title,
990
- data_type: params.data_type || "text",
991
- list_id: params.list_id ?? null,
992
- created_by: params.created_by
993
- };
994
- const resp = await fetch(`${baseUrl}/api/v1/companies/columns/create`, {
1076
+ const resp = await request(baseUrl, "/api/v1/companies/columns/create", apiKey, {
995
1077
  method: "POST",
996
- headers: headers(),
997
- body: JSON.stringify(body)
1078
+ body: {
1079
+ title: params.title,
1080
+ data_type: params.data_type || "text",
1081
+ list_id: params.list_id ?? null,
1082
+ created_by: params.created_by
1083
+ }
998
1084
  });
999
- const data = await resp.json();
1000
- return data.data || data;
1085
+ return resp.data ?? resp;
1001
1086
  }
1002
1087
  };
1003
1088
  };
@@ -1006,56 +1091,40 @@ var createCompaniesClient = (apiKey, apiUrl) => {
1006
1091
  var DEFAULT_API17 = "https://be.graph8.com";
1007
1092
  var createListsClient = (apiKey, apiUrl) => {
1008
1093
  const baseUrl = apiUrl || DEFAULT_API17;
1009
- const headers = () => ({ "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` });
1010
1094
  return {
1011
1095
  /** List all lists. */
1012
1096
  async list(page = 1, limit = 50) {
1013
- const resp = await fetch(`${baseUrl}/api/v1/lists?page=${page}&limit=${limit}`, { headers: headers() });
1014
- return resp.json();
1097
+ return request(baseUrl, "/api/v1/lists", apiKey, { query: { page, limit } });
1015
1098
  },
1016
1099
  /** Create a new list. */
1017
1100
  async create(title, type = "contacts") {
1018
- const resp = await fetch(`${baseUrl}/api/v1/lists`, {
1101
+ const resp = await request(baseUrl, "/api/v1/lists", apiKey, {
1019
1102
  method: "POST",
1020
- headers: headers(),
1021
- body: JSON.stringify({ title, type })
1103
+ body: { title, type }
1022
1104
  });
1023
- const data = await resp.json();
1024
- return data.data || data;
1105
+ return resp.data ?? resp;
1025
1106
  },
1026
1107
  /** Delete a list (soft-delete). */
1027
1108
  async delete(listId) {
1028
- const resp = await fetch(`${baseUrl}/api/v1/lists/${listId}`, {
1029
- method: "DELETE",
1030
- headers: headers()
1031
- });
1032
- return resp.json();
1109
+ return request(baseUrl, `/api/v1/lists/${listId}`, apiKey, { method: "DELETE" });
1033
1110
  },
1034
1111
  /** Get contacts in a list. */
1035
1112
  async contacts(listId, page = 1, limit = 50) {
1036
- const resp = await fetch(
1037
- `${baseUrl}/api/v1/lists/${listId}/contacts?page=${page}&limit=${limit}`,
1038
- { headers: headers() }
1039
- );
1040
- return resp.json();
1113
+ return request(baseUrl, `/api/v1/lists/${listId}/contacts`, apiKey, { query: { page, limit } });
1041
1114
  },
1042
1115
  /** Add contacts to a list. */
1043
1116
  async addContacts(listId, contactIds) {
1044
- const resp = await fetch(`${baseUrl}/api/v1/lists/${listId}/contacts`, {
1117
+ return request(baseUrl, `/api/v1/lists/${listId}/contacts`, apiKey, {
1045
1118
  method: "POST",
1046
- headers: headers(),
1047
- body: JSON.stringify({ contact_ids: contactIds })
1119
+ body: { contact_ids: contactIds }
1048
1120
  });
1049
- return resp.json();
1050
1121
  },
1051
1122
  /** Remove contacts from a list. */
1052
1123
  async removeContacts(listId, contactIds) {
1053
- const resp = await fetch(`${baseUrl}/api/v1/lists/${listId}/contacts`, {
1124
+ return request(baseUrl, `/api/v1/lists/${listId}/contacts`, apiKey, {
1054
1125
  method: "DELETE",
1055
- headers: headers(),
1056
- body: JSON.stringify({ contact_ids: contactIds })
1126
+ body: { contact_ids: contactIds }
1057
1127
  });
1058
- return resp.json();
1059
1128
  }
1060
1129
  };
1061
1130
  };
@@ -1064,40 +1133,30 @@ var createListsClient = (apiKey, apiUrl) => {
1064
1133
  var DEFAULT_API18 = "https://be.graph8.com";
1065
1134
  var createNotesClient = (apiKey, apiUrl) => {
1066
1135
  const baseUrl = apiUrl || DEFAULT_API18;
1067
- const headers = () => ({ "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` });
1068
1136
  return {
1069
1137
  /** List all notes on a contact. */
1070
1138
  async list(contactId) {
1071
- const resp = await fetch(`${baseUrl}/api/v1/contacts/${contactId}/notes`, { headers: headers() });
1072
- return resp.json();
1139
+ return request(baseUrl, `/api/v1/contacts/${contactId}/notes`, apiKey);
1073
1140
  },
1074
1141
  /** Create a note on a contact. */
1075
1142
  async create(contactId, content) {
1076
- const resp = await fetch(`${baseUrl}/api/v1/contacts/${contactId}/notes`, {
1143
+ const resp = await request(baseUrl, `/api/v1/contacts/${contactId}/notes`, apiKey, {
1077
1144
  method: "POST",
1078
- headers: headers(),
1079
- body: JSON.stringify({ content })
1145
+ body: { content }
1080
1146
  });
1081
- const data = await resp.json();
1082
- return data.data || data;
1147
+ return resp.data ?? resp;
1083
1148
  },
1084
1149
  /** Update a note's content. */
1085
1150
  async update(noteId, content) {
1086
- const resp = await fetch(`${baseUrl}/api/v1/notes/${noteId}`, {
1151
+ const resp = await request(baseUrl, `/api/v1/notes/${noteId}`, apiKey, {
1087
1152
  method: "PATCH",
1088
- headers: headers(),
1089
- body: JSON.stringify({ content })
1153
+ body: { content }
1090
1154
  });
1091
- const data = await resp.json();
1092
- return data.data || data;
1155
+ return resp.data ?? resp;
1093
1156
  },
1094
1157
  /** Delete a note. */
1095
1158
  async delete(noteId) {
1096
- const resp = await fetch(`${baseUrl}/api/v1/notes/${noteId}`, {
1097
- method: "DELETE",
1098
- headers: headers()
1099
- });
1100
- return resp.json();
1159
+ return request(baseUrl, `/api/v1/notes/${noteId}`, apiKey, { method: "DELETE" });
1101
1160
  }
1102
1161
  };
1103
1162
  };
@@ -1106,54 +1165,36 @@ var createNotesClient = (apiKey, apiUrl) => {
1106
1165
  var DEFAULT_API19 = "https://be.graph8.com";
1107
1166
  var createTasksClient = (apiKey, apiUrl) => {
1108
1167
  const baseUrl = apiUrl || DEFAULT_API19;
1109
- const headers = () => ({ "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` });
1110
- const toQuery = (params) => {
1111
- const qs = new URLSearchParams();
1112
- for (const [k, v] of Object.entries(params)) {
1113
- if (v != null) qs.set(k, String(v));
1114
- }
1115
- const s = qs.toString();
1116
- return s ? `?${s}` : "";
1117
- };
1118
1168
  return {
1119
1169
  /** List tasks on a single contact. Optional status filter ("open" | "completed"). */
1120
1170
  async listForContact(contactId, status) {
1121
- const qs = status ? `?status=${encodeURIComponent(status)}` : "";
1122
- const resp = await fetch(`${baseUrl}/api/v1/contacts/${contactId}/tasks${qs}`, { headers: headers() });
1123
- return resp.json();
1171
+ return request(baseUrl, `/api/v1/contacts/${contactId}/tasks`, apiKey, {
1172
+ query: status ? { status } : void 0
1173
+ });
1124
1174
  },
1125
1175
  /** List all tasks org-wide with optional filters. */
1126
1176
  async list(params = {}) {
1127
- const resp = await fetch(`${baseUrl}/api/v1/tasks${toQuery(params)}`, { headers: headers() });
1128
- return resp.json();
1177
+ return request(baseUrl, "/api/v1/tasks", apiKey, { query: params });
1129
1178
  },
1130
1179
  /** Create a task on a contact. */
1131
1180
  async create(contactId, task) {
1132
- const resp = await fetch(`${baseUrl}/api/v1/contacts/${contactId}/tasks`, {
1181
+ const resp = await request(baseUrl, `/api/v1/contacts/${contactId}/tasks`, apiKey, {
1133
1182
  method: "POST",
1134
- headers: headers(),
1135
- body: JSON.stringify(task)
1183
+ body: task
1136
1184
  });
1137
- const data = await resp.json();
1138
- return data.data || data;
1185
+ return resp.data ?? resp;
1139
1186
  },
1140
1187
  /** Update a task (partial). */
1141
1188
  async update(taskId, fields) {
1142
- const resp = await fetch(`${baseUrl}/api/v1/tasks/${taskId}`, {
1189
+ const resp = await request(baseUrl, `/api/v1/tasks/${taskId}`, apiKey, {
1143
1190
  method: "PATCH",
1144
- headers: headers(),
1145
- body: JSON.stringify(fields)
1191
+ body: fields
1146
1192
  });
1147
- const data = await resp.json();
1148
- return data.data || data;
1193
+ return resp.data ?? resp;
1149
1194
  },
1150
1195
  /** Delete a task. */
1151
1196
  async delete(taskId) {
1152
- const resp = await fetch(`${baseUrl}/api/v1/tasks/${taskId}`, {
1153
- method: "DELETE",
1154
- headers: headers()
1155
- });
1156
- return resp.json();
1197
+ return request(baseUrl, `/api/v1/tasks/${taskId}`, apiKey, { method: "DELETE" });
1157
1198
  }
1158
1199
  };
1159
1200
  };
@@ -1162,67 +1203,42 @@ var createTasksClient = (apiKey, apiUrl) => {
1162
1203
  var DEFAULT_API20 = "https://be.graph8.com";
1163
1204
  var createFieldsClient = (apiKey, apiUrl) => {
1164
1205
  const baseUrl = apiUrl || DEFAULT_API20;
1165
- const headers = () => ({ "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` });
1166
- const toQuery = (params) => {
1167
- const qs = new URLSearchParams();
1168
- for (const [k, v] of Object.entries(params)) {
1169
- if (v != null) qs.set(k, String(v));
1170
- }
1171
- const s = qs.toString();
1172
- return s ? `?${s}` : "";
1173
- };
1174
1206
  return {
1175
1207
  /** List contact fields (base + custom). Pass listId to include list-specific custom fields. */
1176
1208
  async listContactFields(listId) {
1177
- const qs = listId != null ? `?list_id=${listId}` : "";
1178
- const resp = await fetch(`${baseUrl}/api/v1/fields${qs}`, { headers: headers() });
1179
- return resp.json();
1209
+ return request(baseUrl, "/api/v1/fields", apiKey, { query: listId != null ? { list_id: listId } : void 0 });
1180
1210
  },
1181
1211
  /** List company fields (base + custom). Pass listId to include list-specific custom fields. */
1182
1212
  async listCompanyFields(listId) {
1183
- const qs = listId != null ? `?list_id=${listId}` : "";
1184
- const resp = await fetch(`${baseUrl}/api/v1/fields/companies${qs}`, { headers: headers() });
1185
- return resp.json();
1213
+ return request(baseUrl, "/api/v1/fields/companies", apiKey, {
1214
+ query: listId != null ? { list_id: listId } : void 0
1215
+ });
1186
1216
  },
1187
1217
  /** Create a custom field on contacts (default) or companies. */
1188
1218
  async create(params) {
1189
- const body = {
1190
- title: params.title,
1191
- data_type: params.data_type || "text",
1192
- list_id: params.list_id ?? null,
1193
- entity: params.entity || "contacts"
1194
- };
1195
- const resp = await fetch(`${baseUrl}/api/v1/fields`, {
1219
+ const resp = await request(baseUrl, "/api/v1/fields", apiKey, {
1196
1220
  method: "POST",
1197
- headers: headers(),
1198
- body: JSON.stringify(body)
1221
+ body: {
1222
+ title: params.title,
1223
+ data_type: params.data_type || "text",
1224
+ list_id: params.list_id ?? null,
1225
+ entity: params.entity || "contacts"
1226
+ }
1199
1227
  });
1200
- const data = await resp.json();
1201
- return data.data || data;
1228
+ return resp.data ?? resp;
1202
1229
  },
1203
1230
  /** Delete a custom field (soft-delete). Pass list_id to scope-guard against cross-list deletion. */
1204
1231
  async delete(columnId, params = {}) {
1205
- const queryParams = { entity: params.entity || "contacts" };
1206
- if (params.list_id != null) queryParams.list_id = params.list_id;
1207
- const resp = await fetch(`${baseUrl}/api/v1/fields/${columnId}${toQuery(queryParams)}`, {
1208
- method: "DELETE",
1209
- headers: headers()
1210
- });
1211
- return resp.json();
1232
+ const query = { entity: params.entity || "contacts" };
1233
+ if (params.list_id != null) query.list_id = params.list_id;
1234
+ return request(baseUrl, `/api/v1/fields/${columnId}`, apiKey, { method: "DELETE", query });
1212
1235
  },
1213
1236
  /** Set a custom field value on a single contact or company row. Pass value=null to clear. */
1214
1237
  async setValue(columnId, params) {
1215
- const body = {
1216
- record_id: params.record_id,
1217
- value: params.value ?? null,
1218
- entity: params.entity || "contacts"
1219
- };
1220
- const resp = await fetch(`${baseUrl}/api/v1/fields/${columnId}/values`, {
1238
+ return request(baseUrl, `/api/v1/fields/${columnId}/values`, apiKey, {
1221
1239
  method: "PATCH",
1222
- headers: headers(),
1223
- body: JSON.stringify(body)
1240
+ body: { record_id: params.record_id, value: params.value ?? null, entity: params.entity || "contacts" }
1224
1241
  });
1225
- return resp.json();
1226
1242
  }
1227
1243
  };
1228
1244
  };
@@ -1231,69 +1247,44 @@ var createFieldsClient = (apiKey, apiUrl) => {
1231
1247
  var DEFAULT_API21 = "https://be.graph8.com";
1232
1248
  var createDealsClient = (apiKey, apiUrl) => {
1233
1249
  const baseUrl = apiUrl || DEFAULT_API21;
1234
- const headers = () => ({ "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` });
1235
- const toQuery = (params) => {
1236
- const qs = new URLSearchParams();
1237
- for (const [k, v] of Object.entries(params)) {
1238
- if (v != null) qs.set(k, String(v));
1239
- }
1240
- const s = qs.toString();
1241
- return s ? `?${s}` : "";
1242
- };
1243
1250
  return {
1244
1251
  /** List all deal pipelines and their stages. */
1245
1252
  async pipelines() {
1246
- const resp = await fetch(`${baseUrl}/api/v1/deals/pipelines`, { headers: headers() });
1247
- return resp.json();
1253
+ return request(baseUrl, "/api/v1/deals/pipelines", apiKey);
1248
1254
  },
1249
1255
  /** List deals org-wide with optional filters and pagination. */
1250
1256
  async list(params = {}) {
1251
- const resp = await fetch(`${baseUrl}/api/v1/deals${toQuery(params)}`, { headers: headers() });
1252
- return resp.json();
1257
+ return request(baseUrl, "/api/v1/deals", apiKey, { query: params });
1253
1258
  },
1254
1259
  /** Create a new deal. */
1255
1260
  async create(deal) {
1256
- const resp = await fetch(`${baseUrl}/api/v1/deals`, {
1257
- method: "POST",
1258
- headers: headers(),
1259
- body: JSON.stringify(deal)
1260
- });
1261
- const data = await resp.json();
1262
- return data.data || data;
1261
+ const resp = await request(baseUrl, "/api/v1/deals", apiKey, { method: "POST", body: deal });
1262
+ return resp.data ?? resp;
1263
1263
  },
1264
1264
  /** Get a single deal by ID. */
1265
1265
  async get(dealId) {
1266
- const resp = await fetch(`${baseUrl}/api/v1/deals/${dealId}`, { headers: headers() });
1267
- const data = await resp.json();
1268
- return data.data || data;
1266
+ const resp = await request(baseUrl, `/api/v1/deals/${dealId}`, apiKey);
1267
+ return resp.data ?? resp;
1269
1268
  },
1270
1269
  /** Update a deal (partial). */
1271
1270
  async update(dealId, fields) {
1272
- const resp = await fetch(`${baseUrl}/api/v1/deals/${dealId}`, {
1271
+ const resp = await request(baseUrl, `/api/v1/deals/${dealId}`, apiKey, {
1273
1272
  method: "PATCH",
1274
- headers: headers(),
1275
- body: JSON.stringify(fields)
1273
+ body: fields
1276
1274
  });
1277
- const data = await resp.json();
1278
- return data.data || data;
1275
+ return resp.data ?? resp;
1279
1276
  },
1280
1277
  /** Delete a deal. */
1281
1278
  async delete(dealId) {
1282
- const resp = await fetch(`${baseUrl}/api/v1/deals/${dealId}`, {
1283
- method: "DELETE",
1284
- headers: headers()
1285
- });
1286
- return resp.json();
1279
+ return request(baseUrl, `/api/v1/deals/${dealId}`, apiKey, { method: "DELETE" });
1287
1280
  },
1288
1281
  /** Get all deals associated with a contact. */
1289
1282
  async forContact(contactId) {
1290
- const resp = await fetch(`${baseUrl}/api/v1/contacts/${contactId}/deals`, { headers: headers() });
1291
- return resp.json();
1283
+ return request(baseUrl, `/api/v1/contacts/${contactId}/deals`, apiKey);
1292
1284
  },
1293
1285
  /** Get all deals associated with a company. */
1294
1286
  async forCompany(companyId) {
1295
- const resp = await fetch(`${baseUrl}/api/v1/companies/${companyId}/deals`, { headers: headers() });
1296
- return resp.json();
1287
+ return request(baseUrl, `/api/v1/companies/${companyId}/deals`, apiKey);
1297
1288
  }
1298
1289
  };
1299
1290
  };
@@ -1302,77 +1293,53 @@ var createDealsClient = (apiKey, apiUrl) => {
1302
1293
  var DEFAULT_API22 = "https://be.graph8.com";
1303
1294
  var createInboxClient = (apiKey, apiUrl) => {
1304
1295
  const baseUrl = apiUrl || DEFAULT_API22;
1305
- const headers = () => ({ "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` });
1306
- const toQuery = (params) => {
1307
- const qs = new URLSearchParams();
1308
- for (const [k, v] of Object.entries(params)) {
1309
- if (v != null) qs.set(k, String(v));
1310
- }
1311
- const s = qs.toString();
1312
- return s ? `?${s}` : "";
1313
- };
1314
1296
  return {
1315
1297
  /** List inbox threads across email, SMS, and LinkedIn. */
1316
1298
  async list(params = {}) {
1317
- const resp = await fetch(`${baseUrl}/api/v1/inbox${toQuery(params)}`, { headers: headers() });
1318
- return resp.json();
1299
+ return request(baseUrl, "/api/v1/inbox", apiKey, { query: params });
1319
1300
  },
1320
1301
  /** Get a single inbox thread. Defaults to email channel. */
1321
1302
  async get(replyId, channel = "email") {
1322
- const resp = await fetch(
1323
- `${baseUrl}/api/v1/inbox/${replyId}${toQuery({ channel })}`,
1324
- { headers: headers() }
1325
- );
1326
- const data = await resp.json();
1327
- return data.data || data;
1303
+ const resp = await request(baseUrl, `/api/v1/inbox/${replyId}`, apiKey, {
1304
+ query: { channel }
1305
+ });
1306
+ return resp.data ?? resp;
1328
1307
  },
1329
1308
  /** Assign a user to an inbox thread. */
1330
1309
  async assign(replyId, assigneeEmail, channel = "email") {
1331
- const resp = await fetch(
1332
- `${baseUrl}/api/v1/inbox/${replyId}/assign${toQuery({ channel })}`,
1333
- {
1334
- method: "POST",
1335
- headers: headers(),
1336
- body: JSON.stringify({ assignee_email: assigneeEmail })
1337
- }
1338
- );
1339
- const data = await resp.json();
1340
- return data.data || data;
1310
+ const resp = await request(baseUrl, `/api/v1/inbox/${replyId}/assign`, apiKey, {
1311
+ method: "POST",
1312
+ query: { channel },
1313
+ body: { assignee_email: assigneeEmail }
1314
+ });
1315
+ return resp.data ?? resp;
1341
1316
  },
1342
1317
  /** Attach tag IDs to an inbox thread. */
1343
1318
  async tag(replyId, tagIds, channel = "email") {
1344
- const resp = await fetch(
1345
- `${baseUrl}/api/v1/inbox/${replyId}/tag${toQuery({ channel })}`,
1346
- {
1347
- method: "POST",
1348
- headers: headers(),
1349
- body: JSON.stringify({ tag_ids: tagIds })
1350
- }
1351
- );
1352
- const data = await resp.json();
1353
- return data.data || data;
1319
+ const resp = await request(baseUrl, `/api/v1/inbox/${replyId}/tag`, apiKey, {
1320
+ method: "POST",
1321
+ query: { channel },
1322
+ body: { tag_ids: tagIds }
1323
+ });
1324
+ return resp.data ?? resp;
1354
1325
  },
1355
1326
  /**
1356
1327
  * Generate an AI draft reply for a thread.
1357
1328
  * Charges credits — server returns 402 if balance is insufficient.
1358
1329
  */
1359
1330
  async draft(replyId, channel = "email") {
1360
- const resp = await fetch(
1361
- `${baseUrl}/api/v1/inbox/${replyId}/draft${toQuery({ channel })}`,
1362
- { headers: headers() }
1363
- );
1364
- const data = await resp.json();
1365
- return data.data || data;
1331
+ const resp = await request(baseUrl, `/api/v1/inbox/${replyId}/draft`, apiKey, {
1332
+ query: { channel }
1333
+ });
1334
+ return resp.data ?? resp;
1366
1335
  },
1367
1336
  /** Send a reply through email, SMS, or LinkedIn. */
1368
1337
  async send(replyId, payload) {
1369
- const resp = await fetch(`${baseUrl}/api/v1/inbox/${replyId}/send`, {
1338
+ const resp = await request(baseUrl, `/api/v1/inbox/${replyId}/send`, apiKey, {
1370
1339
  method: "POST",
1371
- headers: headers(),
1372
- body: JSON.stringify(payload)
1340
+ body: payload
1373
1341
  });
1374
- const data = await resp.json();
1375
- return data.data || data;
1342
+ return resp.data ?? resp;
1376
1343
  }
1377
1344
  };
1378
1345
  };
@@ -1381,104 +1348,72 @@ var createInboxClient = (apiKey, apiUrl) => {
1381
1348
  var DEFAULT_API23 = "https://be.graph8.com";
1382
1349
  var createQuotesClient = (apiKey, apiUrl) => {
1383
1350
  const baseUrl = apiUrl || DEFAULT_API23;
1384
- const headers = () => ({ "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` });
1385
- const toQuery = (params) => {
1386
- const qs = new URLSearchParams();
1387
- for (const [k, v] of Object.entries(params)) {
1388
- if (v != null) qs.set(k, String(v));
1389
- }
1390
- const s = qs.toString();
1391
- return s ? `?${s}` : "";
1392
- };
1393
1351
  return {
1394
1352
  /** List quotes org-wide with optional filters and pagination. */
1395
1353
  async list(params = {}) {
1396
- const resp = await fetch(`${baseUrl}/api/v1/quotes${toQuery(params)}`, { headers: headers() });
1397
- return resp.json();
1354
+ return request(baseUrl, "/api/v1/quotes", apiKey, { query: params });
1398
1355
  },
1399
1356
  /** Get full details for a single quote. */
1400
1357
  async get(quoteId) {
1401
- const resp = await fetch(`${baseUrl}/api/v1/quotes/${quoteId}`, { headers: headers() });
1402
- const data = await resp.json();
1403
- return data.data || data;
1358
+ const resp = await request(baseUrl, `/api/v1/quotes/${quoteId}`, apiKey);
1359
+ return resp.data ?? resp;
1404
1360
  },
1405
1361
  /** Create a new quote from line items. */
1406
1362
  async create(quote) {
1407
- const resp = await fetch(`${baseUrl}/api/v1/quotes`, {
1363
+ const resp = await request(baseUrl, "/api/v1/quotes", apiKey, {
1408
1364
  method: "POST",
1409
- headers: headers(),
1410
- body: JSON.stringify(quote)
1365
+ body: quote
1411
1366
  });
1412
- const data = await resp.json();
1413
- return data.data || data;
1367
+ return resp.data ?? resp;
1414
1368
  },
1415
1369
  /** Update a draft quote (line items, expiry, notes). */
1416
1370
  async update(quoteId, fields) {
1417
- const resp = await fetch(`${baseUrl}/api/v1/quotes/${quoteId}`, {
1371
+ const resp = await request(baseUrl, `/api/v1/quotes/${quoteId}`, apiKey, {
1418
1372
  method: "PUT",
1419
- headers: headers(),
1420
- body: JSON.stringify(fields)
1373
+ body: fields
1421
1374
  });
1422
- const data = await resp.json();
1423
- return data.data || data;
1375
+ return resp.data ?? resp;
1424
1376
  },
1425
1377
  /** Delete a draft quote (irreversible). */
1426
1378
  async delete(quoteId) {
1427
- const resp = await fetch(`${baseUrl}/api/v1/quotes/${quoteId}`, {
1428
- method: "DELETE",
1429
- headers: headers()
1430
- });
1431
- return resp.json();
1379
+ return request(baseUrl, `/api/v1/quotes/${quoteId}`, apiKey, { method: "DELETE" });
1432
1380
  },
1433
1381
  /** Duplicate an existing quote (all line items copied into a new draft). */
1434
1382
  async duplicate(quoteId) {
1435
- const resp = await fetch(`${baseUrl}/api/v1/quotes/${quoteId}/duplicate`, {
1383
+ const resp = await request(baseUrl, `/api/v1/quotes/${quoteId}/duplicate`, apiKey, {
1436
1384
  method: "POST",
1437
- headers: headers(),
1438
- body: JSON.stringify({})
1385
+ body: {}
1439
1386
  });
1440
- const data = await resp.json();
1441
- return data.data || data;
1387
+ return resp.data ?? resp;
1442
1388
  },
1443
1389
  /** Convert a signed / sent quote back to editable draft state. */
1444
1390
  async editAsDraft(quoteId) {
1445
- const resp = await fetch(`${baseUrl}/api/v1/quotes/${quoteId}/edit-as-draft`, {
1446
- method: "POST",
1447
- headers: headers(),
1448
- body: JSON.stringify({})
1449
- });
1450
- return resp.json();
1391
+ return request(baseUrl, `/api/v1/quotes/${quoteId}/edit-as-draft`, apiKey, { method: "POST", body: {} });
1451
1392
  },
1452
1393
  /** Send a quote to its recipient via email (with signature + optional payment link). */
1453
1394
  async send(quoteId, params) {
1454
- const resp = await fetch(`${baseUrl}/api/v1/quotes/${quoteId}/send`, {
1395
+ const resp = await request(baseUrl, `/api/v1/quotes/${quoteId}/send`, apiKey, {
1455
1396
  method: "POST",
1456
- headers: headers(),
1457
- body: JSON.stringify(params)
1397
+ body: params
1458
1398
  });
1459
- const data = await resp.json();
1460
- return data.data || data;
1399
+ return resp.data ?? resp;
1461
1400
  },
1462
1401
  /** List products available for line items. */
1463
1402
  async products() {
1464
- const resp = await fetch(`${baseUrl}/api/v1/quotable-products`, { headers: headers() });
1465
- return resp.json();
1403
+ return request(baseUrl, "/api/v1/quotable-products", apiKey);
1466
1404
  },
1467
1405
  /** Get org-level quote settings (currency, tax rate, payment providers, logo). */
1468
1406
  async settings() {
1469
- const resp = await fetch(`${baseUrl}/api/v1/quote-settings`, { headers: headers() });
1470
- const data = await resp.json();
1471
- return data.data || data;
1407
+ const resp = await request(baseUrl, "/api/v1/quote-settings", apiKey);
1408
+ return resp.data ?? resp;
1472
1409
  },
1473
1410
  /** Get all quotes associated with a contact. */
1474
1411
  async forContact(contactId) {
1475
- const resp = await fetch(`${baseUrl}/api/v1/contacts/${contactId}/quotes`, { headers: headers() });
1476
- return resp.json();
1412
+ return request(baseUrl, `/api/v1/contacts/${contactId}/quotes`, apiKey);
1477
1413
  },
1478
1414
  /** Get all quotes associated with a company. */
1479
1415
  async forCompany(companyId) {
1480
- const resp = await fetch(`${baseUrl}/api/v1/companies/${companyId}/quotes`, { headers: headers() });
1481
- return resp.json();
1416
+ return request(baseUrl, `/api/v1/companies/${companyId}/quotes`, apiKey);
1482
1417
  }
1483
1418
  };
1484
1419
  };
@@ -1487,107 +1422,82 @@ var createQuotesClient = (apiKey, apiUrl) => {
1487
1422
  var DEFAULT_API24 = "https://be.graph8.com";
1488
1423
  var createPipelinesClient = (apiKey, apiUrl) => {
1489
1424
  const baseUrl = apiUrl || DEFAULT_API24;
1490
- const headers = () => ({ "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` });
1491
1425
  return {
1492
1426
  /** List all stage-checklist pipelines with stages, evidence, scripts. */
1493
1427
  async list() {
1494
- const resp = await fetch(`${baseUrl}/api/v1/pipelines`, { headers: headers() });
1495
- return resp.json();
1428
+ return request(baseUrl, "/api/v1/pipelines", apiKey);
1496
1429
  },
1497
1430
  /** Get a single pipeline by ID. */
1498
1431
  async get(pipelineId) {
1499
- const resp = await fetch(`${baseUrl}/api/v1/pipelines/${pipelineId}`, { headers: headers() });
1500
- const data = await resp.json();
1501
- return data.data || data;
1432
+ const resp = await request(baseUrl, `/api/v1/pipelines/${pipelineId}`, apiKey);
1433
+ return resp.data ?? resp;
1502
1434
  },
1503
1435
  /** Get the canonical evidence-key library (used when defining stages). */
1504
1436
  async evidenceLibrary() {
1505
- const resp = await fetch(`${baseUrl}/api/v1/pipelines/evidence-library`, { headers: headers() });
1506
- return resp.json();
1437
+ return request(baseUrl, "/api/v1/pipelines/evidence-library", apiKey);
1507
1438
  },
1508
1439
  /** Create a new pipeline. Defaults to a templated set of stages; pass blank=true for empty. */
1509
1440
  async create(params) {
1510
- const resp = await fetch(`${baseUrl}/api/v1/pipelines`, {
1441
+ const resp = await request(baseUrl, "/api/v1/pipelines", apiKey, {
1511
1442
  method: "POST",
1512
- headers: headers(),
1513
- body: JSON.stringify(params)
1443
+ body: params
1514
1444
  });
1515
- const data = await resp.json();
1516
- return data.data || data;
1445
+ return resp.data ?? resp;
1517
1446
  },
1518
1447
  /** Update pipeline metadata (name, target). */
1519
1448
  async update(pipelineId, fields) {
1520
- const resp = await fetch(`${baseUrl}/api/v1/pipelines/${pipelineId}`, {
1449
+ const resp = await request(baseUrl, `/api/v1/pipelines/${pipelineId}`, apiKey, {
1521
1450
  method: "PUT",
1522
- headers: headers(),
1523
- body: JSON.stringify(fields)
1451
+ body: fields
1524
1452
  });
1525
- const data = await resp.json();
1526
- return data.data || data;
1453
+ return resp.data ?? resp;
1527
1454
  },
1528
1455
  /** Delete a pipeline. Only allowed when no deals reference it. */
1529
1456
  async delete(pipelineId) {
1530
- const resp = await fetch(`${baseUrl}/api/v1/pipelines/${pipelineId}`, {
1531
- method: "DELETE",
1532
- headers: headers()
1533
- });
1534
- return resp.json();
1457
+ return request(baseUrl, `/api/v1/pipelines/${pipelineId}`, apiKey, { method: "DELETE" });
1535
1458
  },
1536
1459
  /** Add a new stage to a pipeline. */
1537
1460
  async createStage(pipelineId, stage) {
1538
- const resp = await fetch(`${baseUrl}/api/v1/pipelines/${pipelineId}/stages`, {
1539
- method: "POST",
1540
- headers: headers(),
1541
- body: JSON.stringify(stage)
1542
- });
1543
- const data = await resp.json();
1544
- return data.data || data;
1461
+ const resp = await request(
1462
+ baseUrl,
1463
+ `/api/v1/pipelines/${pipelineId}/stages`,
1464
+ apiKey,
1465
+ { method: "POST", body: stage }
1466
+ );
1467
+ return resp.data ?? resp;
1545
1468
  },
1546
1469
  /** Update a stage (evidence, scripts, position). */
1547
1470
  async updateStage(pipelineId, stageId, fields) {
1548
- const resp = await fetch(`${baseUrl}/api/v1/pipelines/${pipelineId}/stages/${stageId}`, {
1549
- method: "PUT",
1550
- headers: headers(),
1551
- body: JSON.stringify(fields)
1552
- });
1553
- const data = await resp.json();
1554
- return data.data || data;
1471
+ const resp = await request(
1472
+ baseUrl,
1473
+ `/api/v1/pipelines/${pipelineId}/stages/${stageId}`,
1474
+ apiKey,
1475
+ { method: "PUT", body: fields }
1476
+ );
1477
+ return resp.data ?? resp;
1555
1478
  },
1556
1479
  /** Reorder stages within a pipeline (pass full ordered list of stage IDs). */
1557
1480
  async reorderStages(pipelineId, stageIds) {
1558
- const resp = await fetch(`${baseUrl}/api/v1/pipelines/${pipelineId}/stages/reorder`, {
1481
+ return request(baseUrl, `/api/v1/pipelines/${pipelineId}/stages/reorder`, apiKey, {
1559
1482
  method: "PUT",
1560
- headers: headers(),
1561
- body: JSON.stringify({ stage_ids: stageIds })
1483
+ body: { stage_ids: stageIds }
1562
1484
  });
1563
- return resp.json();
1564
1485
  },
1565
1486
  /** Delete a stage from a pipeline. */
1566
1487
  async deleteStage(pipelineId, stageId) {
1567
- const resp = await fetch(`${baseUrl}/api/v1/pipelines/${pipelineId}/stages/${stageId}`, {
1568
- method: "DELETE",
1569
- headers: headers()
1570
- });
1571
- return resp.json();
1488
+ return request(baseUrl, `/api/v1/pipelines/${pipelineId}/stages/${stageId}`, apiKey, { method: "DELETE" });
1572
1489
  },
1573
1490
  /** Get an AI-suggested pipeline based on org context (brand, ICP, messaging). */
1574
1491
  async suggest() {
1575
- const resp = await fetch(`${baseUrl}/api/v1/pipelines/suggest`, {
1576
- method: "POST",
1577
- headers: headers(),
1578
- body: JSON.stringify({})
1579
- });
1580
- return resp.json();
1492
+ return request(baseUrl, "/api/v1/pipelines/suggest", apiKey, { method: "POST", body: {} });
1581
1493
  },
1582
1494
  /** Create a real pipeline from an AI suggestion (optionally with overrides). */
1583
1495
  async fromSuggestion(suggestionId, overrides) {
1584
- const resp = await fetch(`${baseUrl}/api/v1/pipelines/from-suggestion`, {
1496
+ const resp = await request(baseUrl, "/api/v1/pipelines/from-suggestion", apiKey, {
1585
1497
  method: "POST",
1586
- headers: headers(),
1587
- body: JSON.stringify({ suggestion_id: suggestionId, ...overrides || {} })
1498
+ body: { suggestion_id: suggestionId, ...overrides || {} }
1588
1499
  });
1589
- const data = await resp.json();
1590
- return data.data || data;
1500
+ return resp.data ?? resp;
1591
1501
  }
1592
1502
  };
1593
1503
  };
@@ -1596,36 +1506,23 @@ var createPipelinesClient = (apiKey, apiUrl) => {
1596
1506
  var DEFAULT_API25 = "https://be.graph8.com";
1597
1507
  var createWorkflowsClient = (apiKey, apiUrl) => {
1598
1508
  const baseUrl = apiUrl || DEFAULT_API25;
1599
- const headers = () => ({ "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` });
1600
- const toQuery = (params) => {
1601
- const qs = new URLSearchParams();
1602
- for (const [k, v] of Object.entries(params)) {
1603
- if (v != null) qs.set(k, String(v));
1604
- }
1605
- const s = qs.toString();
1606
- return s ? `?${s}` : "";
1607
- };
1608
1509
  return {
1609
1510
  /** List workflows org-wide. */
1610
1511
  async list(params = {}) {
1611
- const resp = await fetch(`${baseUrl}/api/v1/workflows${toQuery(params)}`, { headers: headers() });
1612
- return resp.json();
1512
+ return request(baseUrl, "/api/v1/workflows", apiKey, { query: params });
1613
1513
  },
1614
1514
  /** Get full workflow definition (nodes, connections, trigger, execution state). */
1615
1515
  async get(workflowId) {
1616
- const resp = await fetch(`${baseUrl}/api/v1/workflows/${workflowId}`, { headers: headers() });
1617
- const data = await resp.json();
1618
- return data.data || data;
1516
+ const resp = await request(baseUrl, `/api/v1/workflows/${workflowId}`, apiKey);
1517
+ return resp.data ?? resp;
1619
1518
  },
1620
1519
  /** Create a new workflow. */
1621
1520
  async create(params) {
1622
- const resp = await fetch(`${baseUrl}/api/v1/workflows`, {
1521
+ const resp = await request(baseUrl, "/api/v1/workflows", apiKey, {
1623
1522
  method: "POST",
1624
- headers: headers(),
1625
- body: JSON.stringify(params)
1523
+ body: params
1626
1524
  });
1627
- const data = await resp.json();
1628
- return data.data || data;
1525
+ return resp.data ?? resp;
1629
1526
  },
1630
1527
  /**
1631
1528
  * Update a workflow. Pass the full `config` (nodes + connections) to edit
@@ -1633,129 +1530,92 @@ var createWorkflowsClient = (apiKey, apiUrl) => {
1633
1530
  * config and submitting the updated record.
1634
1531
  */
1635
1532
  async update(workflowId, fields) {
1636
- const resp = await fetch(`${baseUrl}/api/v1/workflows/${workflowId}`, {
1533
+ const resp = await request(baseUrl, `/api/v1/workflows/${workflowId}`, apiKey, {
1637
1534
  method: "PUT",
1638
- headers: headers(),
1639
- body: JSON.stringify(fields)
1535
+ body: fields
1640
1536
  });
1641
- const data = await resp.json();
1642
- return data.data || data;
1537
+ return resp.data ?? resp;
1643
1538
  },
1644
1539
  /** Delete a workflow. */
1645
1540
  async delete(workflowId) {
1646
- const resp = await fetch(`${baseUrl}/api/v1/workflows/${workflowId}`, {
1647
- method: "DELETE",
1648
- headers: headers()
1649
- });
1650
- return resp.json();
1541
+ return request(baseUrl, `/api/v1/workflows/${workflowId}`, apiKey, { method: "DELETE" });
1651
1542
  },
1652
1543
  /** Validate a workflow definition (orphans, dangling connections, required fields). */
1653
1544
  async validate(workflow) {
1654
- const resp = await fetch(`${baseUrl}/api/v1/workflows/validate`, {
1655
- method: "POST",
1656
- headers: headers(),
1657
- body: JSON.stringify(workflow)
1658
- });
1659
- return resp.json();
1545
+ return request(baseUrl, "/api/v1/workflows/validate", apiKey, { method: "POST", body: workflow });
1660
1546
  },
1661
1547
  /** Execute a workflow immediately with a trigger payload. */
1662
1548
  async execute(workflowId, triggerPayload = {}) {
1663
- const resp = await fetch(`${baseUrl}/api/v1/workflows/${workflowId}/execute`, {
1664
- method: "POST",
1665
- headers: headers(),
1666
- body: JSON.stringify({ trigger_payload: triggerPayload })
1667
- });
1668
- const data = await resp.json();
1669
- return data.data || data;
1549
+ const resp = await request(
1550
+ baseUrl,
1551
+ `/api/v1/workflows/${workflowId}/execute`,
1552
+ apiKey,
1553
+ { method: "POST", body: { trigger_payload: triggerPayload } }
1554
+ );
1555
+ return resp.data ?? resp;
1670
1556
  },
1671
- /** Get the status + output of a workflow execution. */
1557
+ /** Get the status + outputs of a single execution. */
1672
1558
  async getExecution(executionId) {
1673
- const resp = await fetch(`${baseUrl}/api/v1/workflows/executions/${executionId}`, { headers: headers() });
1674
- const data = await resp.json();
1675
- return data.data || data;
1559
+ const resp = await request(
1560
+ baseUrl,
1561
+ `/api/v1/workflows/executions/${executionId}`,
1562
+ apiKey
1563
+ );
1564
+ return resp.data ?? resp;
1676
1565
  },
1677
- /** Pause an in-flight execution. */
1566
+ /** Pause a running execution. */
1678
1567
  async pauseExecution(executionId) {
1679
- const resp = await fetch(`${baseUrl}/api/v1/workflows/executions/${executionId}/pause`, {
1680
- method: "POST",
1681
- headers: headers(),
1682
- body: JSON.stringify({})
1683
- });
1684
- return resp.json();
1568
+ return request(baseUrl, `/api/v1/workflows/executions/${executionId}/pause`, apiKey, { method: "POST" });
1685
1569
  },
1686
1570
  /** Resume a paused execution. */
1687
1571
  async resumeExecution(executionId) {
1688
- const resp = await fetch(`${baseUrl}/api/v1/workflows/executions/${executionId}/resume`, {
1689
- method: "POST",
1690
- headers: headers(),
1691
- body: JSON.stringify({})
1692
- });
1693
- return resp.json();
1572
+ return request(baseUrl, `/api/v1/workflows/executions/${executionId}/resume`, apiKey, { method: "POST" });
1694
1573
  },
1695
- /** Stop an execution (terminal state — cannot resume). */
1574
+ /** Stop an execution. */
1696
1575
  async stopExecution(executionId) {
1697
- const resp = await fetch(`${baseUrl}/api/v1/workflows/executions/${executionId}/stop`, {
1698
- method: "POST",
1699
- headers: headers(),
1700
- body: JSON.stringify({})
1701
- });
1702
- return resp.json();
1576
+ return request(baseUrl, `/api/v1/workflows/executions/${executionId}/stop`, apiKey, { method: "POST" });
1703
1577
  },
1704
- /** Get the status of a workflow's external trigger (e.g. "waiting for webhook"). */
1578
+ /** Get the trigger status (e.g. schedule / webhook wiring) for a workflow. */
1705
1579
  async getTriggerStatus(workflowId) {
1706
- const resp = await fetch(`${baseUrl}/api/v1/workflows/${workflowId}/trigger-status`, { headers: headers() });
1707
- return resp.json();
1580
+ return request(baseUrl, `/api/v1/workflows/${workflowId}/trigger-status`, apiKey);
1708
1581
  },
1709
- /** Reset the trigger cursor (e.g. for event-stream triggers — resume from beginning). */
1582
+ /** Reset a workflow's trigger cursor / state. */
1710
1583
  async resetTrigger(workflowId) {
1711
- const resp = await fetch(`${baseUrl}/api/v1/workflows/${workflowId}/trigger-reset`, {
1712
- method: "POST",
1713
- headers: headers(),
1714
- body: JSON.stringify({})
1715
- });
1716
- return resp.json();
1584
+ return request(baseUrl, `/api/v1/workflows/${workflowId}/trigger-reset`, apiKey, { method: "POST" });
1717
1585
  },
1718
- /**
1719
- * List available node types with schemas. Pass a `type` param to fetch one type's full schema.
1720
- */
1586
+ /** Catalog of available workflow node types with config + output schemas. */
1721
1587
  async nodeTypes(params = {}) {
1722
- const resp = await fetch(`${baseUrl}/api/v1/workflows/node-types/schema${toQuery(params)}`, { headers: headers() });
1723
- return resp.json();
1588
+ return request(baseUrl, "/api/v1/workflows/node-types/schema", apiKey, {
1589
+ query: params
1590
+ });
1724
1591
  },
1725
- /** Slack workspace users (for Slack action recipients). */
1592
+ /** Slack users available to workflow nodes. */
1726
1593
  async listSlackUsers() {
1727
- const resp = await fetch(`${baseUrl}/api/v1/workflows/integrations/slack/users`, { headers: headers() });
1728
- return resp.json();
1594
+ return request(baseUrl, "/api/v1/workflows/integrations/slack/users", apiKey);
1729
1595
  },
1730
- /** Slack channels. */
1596
+ /** Slack channels available to workflow nodes. */
1731
1597
  async listSlackChannels() {
1732
- const resp = await fetch(`${baseUrl}/api/v1/workflows/integrations/slack/channels`, { headers: headers() });
1733
- return resp.json();
1598
+ return request(baseUrl, "/api/v1/workflows/integrations/slack/channels", apiKey);
1734
1599
  },
1735
- /** Roam (Copilot chat) users. */
1600
+ /** Roam users available to workflow nodes. */
1736
1601
  async listRoamUsers() {
1737
- const resp = await fetch(`${baseUrl}/api/v1/workflows/integrations/roam/users`, { headers: headers() });
1738
- return resp.json();
1602
+ return request(baseUrl, "/api/v1/workflows/integrations/roam/users", apiKey);
1739
1603
  },
1740
- /** Roam (Copilot chat) groups. */
1604
+ /** Roam groups available to workflow nodes. */
1741
1605
  async listRoamGroups() {
1742
- const resp = await fetch(`${baseUrl}/api/v1/workflows/integrations/roam/groups`, { headers: headers() });
1743
- return resp.json();
1606
+ return request(baseUrl, "/api/v1/workflows/integrations/roam/groups", apiKey);
1744
1607
  },
1745
- /** Available MCP servers (for Agent-node integrations). */
1608
+ /** MCP servers available to workflow nodes. */
1746
1609
  async listMcpServers() {
1747
- const resp = await fetch(`${baseUrl}/api/v1/workflows/mcp-servers`, { headers: headers() });
1748
- return resp.json();
1610
+ return request(baseUrl, "/api/v1/workflows/mcp-servers", apiKey);
1749
1611
  },
1750
- /** Available call dispositions (voice workflow nodes). */
1612
+ /** Disposition options available to workflow nodes. */
1751
1613
  async listDispositions() {
1752
- const resp = await fetch(`${baseUrl}/api/v1/workflows/dispositions`, { headers: headers() });
1753
- return resp.json();
1614
+ return request(baseUrl, "/api/v1/workflows/dispositions", apiKey);
1754
1615
  },
1755
- /** Form field schema for form-trigger nodes. */
1616
+ /** Form field definitions for a given form (used by form-trigger nodes). */
1756
1617
  async listFormFields(formId) {
1757
- const resp = await fetch(`${baseUrl}/api/v1/workflows/forms/${formId}/fields`, { headers: headers() });
1758
- return resp.json();
1618
+ return request(baseUrl, `/api/v1/workflows/forms/${formId}/fields`, apiKey);
1759
1619
  }
1760
1620
  };
1761
1621
  };
@@ -1764,127 +1624,87 @@ var createWorkflowsClient = (apiKey, apiUrl) => {
1764
1624
  var DEFAULT_API26 = "https://be.graph8.com";
1765
1625
  var createSkillsClient = (apiKey, apiUrl) => {
1766
1626
  const baseUrl = apiUrl || DEFAULT_API26;
1767
- const headers = () => ({ "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` });
1768
- const toQuery = (params) => {
1769
- const qs = new URLSearchParams();
1770
- for (const [k, v] of Object.entries(params)) {
1771
- if (v != null) qs.set(k, String(v));
1772
- }
1773
- const s = qs.toString();
1774
- return s ? `?${s}` : "";
1775
- };
1776
1627
  return {
1777
1628
  /** List skills. */
1778
1629
  async list(params = {}) {
1779
- const resp = await fetch(`${baseUrl}/api/v1/skills${toQuery(params)}`, { headers: headers() });
1780
- return resp.json();
1630
+ return request(baseUrl, "/api/v1/skills", apiKey, { query: params });
1781
1631
  },
1782
1632
  /** Get a skill by ID. */
1783
1633
  async get(skillId) {
1784
- const resp = await fetch(`${baseUrl}/api/v1/skills/${skillId}`, { headers: headers() });
1785
- const data = await resp.json();
1786
- return data.data || data;
1634
+ const resp = await request(baseUrl, `/api/v1/skills/${skillId}`, apiKey);
1635
+ return resp.data ?? resp;
1787
1636
  },
1788
1637
  /** Get the variables required by a skill (extracted from prompt or body template). */
1789
1638
  async getVariables(skillId) {
1790
- const resp = await fetch(`${baseUrl}/api/v1/skills/${skillId}/variables`, { headers: headers() });
1791
- return resp.json();
1639
+ return request(baseUrl, `/api/v1/skills/${skillId}/variables`, apiKey);
1792
1640
  },
1793
1641
  /** List available LLM models. */
1794
1642
  async listModels() {
1795
- const resp = await fetch(`${baseUrl}/api/v1/skills/models`, { headers: headers() });
1796
- return resp.json();
1643
+ return request(baseUrl, "/api/v1/skills/models", apiKey);
1797
1644
  },
1798
1645
  /** List skill templates. */
1799
1646
  async listTemplates(params = {}) {
1800
- const resp = await fetch(`${baseUrl}/api/v1/skills/templates${toQuery(params)}`, { headers: headers() });
1801
- return resp.json();
1647
+ return request(baseUrl, "/api/v1/skills/templates", apiKey, { query: params });
1802
1648
  },
1803
1649
  /** Create an LLM skill (prompt + model + schemas). */
1804
1650
  async createLLM(params) {
1805
- const resp = await fetch(`${baseUrl}/api/v1/skills`, {
1651
+ const resp = await request(baseUrl, "/api/v1/skills", apiKey, {
1806
1652
  method: "POST",
1807
- headers: headers(),
1808
- body: JSON.stringify({ ...params, type: "llm" })
1653
+ body: { ...params, type: "llm" }
1809
1654
  });
1810
- const data = await resp.json();
1811
- return data.data || data;
1655
+ return resp.data ?? resp;
1812
1656
  },
1813
1657
  /** Create an API skill (HTTP request wrapper). */
1814
1658
  async createAPI(params) {
1815
- const resp = await fetch(`${baseUrl}/api/v1/skills`, {
1659
+ const resp = await request(baseUrl, "/api/v1/skills", apiKey, {
1816
1660
  method: "POST",
1817
- headers: headers(),
1818
- body: JSON.stringify({ ...params, type: "api" })
1661
+ body: { ...params, type: "api" }
1819
1662
  });
1820
- const data = await resp.json();
1821
- return data.data || data;
1663
+ return resp.data ?? resp;
1822
1664
  },
1823
1665
  /** Create a skill from a built-in template. */
1824
1666
  async createFromTemplate(params) {
1825
- const resp = await fetch(`${baseUrl}/api/v1/skills/from-template`, {
1667
+ const resp = await request(baseUrl, "/api/v1/skills/from-template", apiKey, {
1826
1668
  method: "POST",
1827
- headers: headers(),
1828
- body: JSON.stringify(params)
1669
+ body: params
1829
1670
  });
1830
- const data = await resp.json();
1831
- return data.data || data;
1671
+ return resp.data ?? resp;
1832
1672
  },
1833
1673
  /** Lift a workflow node into a reusable skill. */
1834
1674
  async createFromNode(params) {
1835
- const resp = await fetch(`${baseUrl}/api/v1/skills/from-node`, {
1675
+ const resp = await request(baseUrl, "/api/v1/skills/from-node", apiKey, {
1836
1676
  method: "POST",
1837
- headers: headers(),
1838
- body: JSON.stringify(params)
1677
+ body: params
1839
1678
  });
1840
- const data = await resp.json();
1841
- return data.data || data;
1679
+ return resp.data ?? resp;
1842
1680
  },
1843
1681
  /** Update an LLM skill. */
1844
1682
  async updateLLM(skillId, fields) {
1845
- const resp = await fetch(`${baseUrl}/api/v1/skills/${skillId}`, {
1683
+ const resp = await request(baseUrl, `/api/v1/skills/${skillId}`, apiKey, {
1846
1684
  method: "PUT",
1847
- headers: headers(),
1848
- body: JSON.stringify({ ...fields, type: "llm" })
1685
+ body: { ...fields, type: "llm" }
1849
1686
  });
1850
- const data = await resp.json();
1851
- return data.data || data;
1687
+ return resp.data ?? resp;
1852
1688
  },
1853
1689
  /** Update an API skill. */
1854
1690
  async updateAPI(skillId, fields) {
1855
- const resp = await fetch(`${baseUrl}/api/v1/skills/${skillId}`, {
1691
+ const resp = await request(baseUrl, `/api/v1/skills/${skillId}`, apiKey, {
1856
1692
  method: "PUT",
1857
- headers: headers(),
1858
- body: JSON.stringify({ ...fields, type: "api" })
1693
+ body: { ...fields, type: "api" }
1859
1694
  });
1860
- const data = await resp.json();
1861
- return data.data || data;
1695
+ return resp.data ?? resp;
1862
1696
  },
1863
1697
  /** Delete a skill (irreversible if in-use workflows exist). */
1864
1698
  async delete(skillId) {
1865
- const resp = await fetch(`${baseUrl}/api/v1/skills/${skillId}`, {
1866
- method: "DELETE",
1867
- headers: headers()
1868
- });
1869
- return resp.json();
1699
+ return request(baseUrl, `/api/v1/skills/${skillId}`, apiKey, { method: "DELETE" });
1870
1700
  },
1871
1701
  /** Validate a skill definition (without saving). */
1872
1702
  async validate(skill) {
1873
- const resp = await fetch(`${baseUrl}/api/v1/skills/validate`, {
1874
- method: "POST",
1875
- headers: headers(),
1876
- body: JSON.stringify(skill)
1877
- });
1878
- return resp.json();
1703
+ return request(baseUrl, "/api/v1/skills/validate", apiKey, { method: "POST", body: skill });
1879
1704
  },
1880
1705
  /** Execute a skill immediately with an input payload (test / preview). */
1881
1706
  async execute(skillId, inputPayload) {
1882
- const resp = await fetch(`${baseUrl}/api/v1/skills/${skillId}/execute`, {
1883
- method: "POST",
1884
- headers: headers(),
1885
- body: JSON.stringify(inputPayload)
1886
- });
1887
- return resp.json();
1707
+ return request(baseUrl, `/api/v1/skills/${skillId}/execute`, apiKey, { method: "POST", body: inputPayload });
1888
1708
  }
1889
1709
  };
1890
1710
  };
@@ -1893,23 +1713,9 @@ var createSkillsClient = (apiKey, apiUrl) => {
1893
1713
  var DEFAULT_API27 = "https://be.graph8.com";
1894
1714
  var createIntentClient = (apiKey, apiUrl) => {
1895
1715
  const baseUrl = apiUrl || DEFAULT_API27;
1896
- const headers = () => ({ "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` });
1897
- const get = async (path) => {
1898
- const resp = await fetch(`${baseUrl}/api/v1${path}`, { headers: headers() });
1899
- return resp.json();
1900
- };
1901
- const post = async (path, body = {}) => {
1902
- const resp = await fetch(`${baseUrl}/api/v1${path}`, {
1903
- method: "POST",
1904
- headers: headers(),
1905
- body: JSON.stringify(body)
1906
- });
1907
- return resp.json();
1908
- };
1909
- const del = async (path) => {
1910
- const resp = await fetch(`${baseUrl}/api/v1${path}`, { method: "DELETE", headers: headers() });
1911
- return resp.json();
1912
- };
1716
+ const get = (path) => request(baseUrl, `/api/v1${path}`, apiKey);
1717
+ const post = (path, body = {}) => request(baseUrl, `/api/v1${path}`, apiKey, { method: "POST", body });
1718
+ const del = (path) => request(baseUrl, `/api/v1${path}`, apiKey, { method: "DELETE" });
1913
1719
  return {
1914
1720
  /** Org-level intent stats (totals over the last 30 days). */
1915
1721
  async stats() {
@@ -1966,12 +1772,10 @@ var createIntentClient = (apiKey, apiUrl) => {
1966
1772
  * of the intent surface — we call it directly here instead of through the shared `post()` helper.
1967
1773
  */
1968
1774
  async urlCompanies(url, params = {}) {
1969
- const resp = await fetch(`${baseUrl}/intent-search/url-companies`, {
1775
+ return request(baseUrl, "/intent-search/url-companies", apiKey, {
1970
1776
  method: "POST",
1971
- headers: headers(),
1972
- body: JSON.stringify({ url, ...params })
1777
+ body: { url, ...params }
1973
1778
  });
1974
- return resp.json();
1975
1779
  }
1976
1780
  };
1977
1781
  };
@@ -1980,21 +1784,11 @@ var createIntentClient = (apiKey, apiUrl) => {
1980
1784
  var DEFAULT_API28 = "https://be.graph8.com";
1981
1785
  var createStudioClient = (apiKey, apiUrl) => {
1982
1786
  const baseUrl = apiUrl || DEFAULT_API28;
1983
- const headers = () => ({ "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` });
1984
- const toQuery = (params) => {
1985
- const qs = new URLSearchParams();
1986
- for (const [k, v] of Object.entries(params)) {
1987
- if (v != null) qs.set(k, String(v));
1988
- }
1989
- const s = qs.toString();
1990
- return s ? `?${s}` : "";
1991
- };
1992
- const get = async (path, params = {}) => {
1993
- const resp = await fetch(`${baseUrl}/api/v1${path}${toQuery(params)}`, { headers: headers() });
1994
- return resp.json();
1995
- };
1787
+ const get = (path, params = {}) => request(baseUrl, `/api/v1${path}`, apiKey, { query: params });
1996
1788
  return {
1997
- /** Org-level Studio documents (brand_brief, value_props, messaging_house, etc.). */
1789
+ /** Org-level Studio documents (brand_brief, value_props, messaging_house, etc.).
1790
+ * `include_content` defaults to true server-side, so each document includes
1791
+ * its `content` body unless you explicitly pass `include_content: false`. */
1998
1792
  async globalContext(params = {}) {
1999
1793
  return get("/global-context/documents", params);
2000
1794
  },
@@ -2021,33 +1815,187 @@ var createStudioClient = (apiKey, apiUrl) => {
2021
1815
  var DEFAULT_API29 = "https://be.graph8.com";
2022
1816
  var createMeetingsClient = (apiKey, apiUrl) => {
2023
1817
  const baseUrl = apiUrl || DEFAULT_API29;
2024
- const headers = () => ({ "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` });
2025
- const toQuery = (params) => {
2026
- const qs = new URLSearchParams();
2027
- for (const [k, v] of Object.entries(params)) {
2028
- if (v != null) qs.set(k, String(v));
2029
- }
2030
- const s = qs.toString();
2031
- return s ? `?${s}` : "";
2032
- };
2033
1818
  return {
2034
1819
  /** List meetings with optional filters. Returns summary rows without transcript / analysis. */
2035
1820
  async list(params = {}) {
2036
- const resp = await fetch(`${baseUrl}/api/v1/inbox/meetings${toQuery(params)}`, { headers: headers() });
2037
- return resp.json();
1821
+ return request(baseUrl, "/api/v1/inbox/meetings", apiKey, { query: params });
2038
1822
  },
2039
1823
  /** Get full meeting detail including transcript + AI analysis (transcript available 1-5 min after meeting ends). */
2040
1824
  async get(meetingId) {
2041
- const resp = await fetch(`${baseUrl}/api/v1/inbox/meetings/${meetingId}`, { headers: headers() });
2042
- const data = await resp.json();
2043
- return data.data || data;
1825
+ const resp = await request(baseUrl, `/api/v1/inbox/meetings/${meetingId}`, apiKey);
1826
+ return resp.data ?? resp;
1827
+ }
1828
+ };
1829
+ };
1830
+
1831
+ // src/audiences.ts
1832
+ var DEFAULT_API30 = "https://be.graph8.com";
1833
+ var createAudiencesClient = (apiKey, apiUrl) => {
1834
+ const baseUrl = apiUrl || DEFAULT_API30;
1835
+ const base = "/api/v1/audience-syncs";
1836
+ return {
1837
+ /** List all audience syncs for the organization. */
1838
+ async list() {
1839
+ return request(baseUrl, base, apiKey);
1840
+ },
1841
+ /** Create a new audience sync to an ad platform. */
1842
+ async create(params) {
1843
+ const resp = await request(baseUrl, base, apiKey, {
1844
+ method: "POST",
1845
+ body: params
1846
+ });
1847
+ return resp.data ?? resp;
1848
+ },
1849
+ /** Get a single audience sync by ID. */
1850
+ async get(configId) {
1851
+ const resp = await request(baseUrl, `${base}/${configId}`, apiKey);
1852
+ return resp.data ?? resp;
1853
+ },
1854
+ /** Update an audience sync (partial). */
1855
+ async update(configId, fields) {
1856
+ const resp = await request(baseUrl, `${base}/${configId}`, apiKey, {
1857
+ method: "PATCH",
1858
+ body: fields
1859
+ });
1860
+ return resp.data ?? resp;
1861
+ },
1862
+ /** Delete an audience sync. */
1863
+ async delete(configId) {
1864
+ return request(baseUrl, `${base}/${configId}`, apiKey, { method: "DELETE" });
1865
+ },
1866
+ /** Trigger an immediate sync run for a config. */
1867
+ async trigger(configId) {
1868
+ return request(baseUrl, `${base}/${configId}/trigger`, apiKey, { method: "POST" });
1869
+ },
1870
+ /** List recent sync runs for a config (most recent first). */
1871
+ async runs(configId) {
1872
+ return request(baseUrl, `${base}/${configId}/runs`, apiKey);
1873
+ },
1874
+ /** List recent sync errors for a config. */
1875
+ async errors(configId) {
1876
+ return request(baseUrl, `${base}/${configId}/errors`, apiKey);
1877
+ }
1878
+ };
1879
+ };
1880
+
1881
+ // src/search.ts
1882
+ var DEFAULT_API31 = "https://be.graph8.com";
1883
+ var createSearchClient = (apiKey, apiUrl) => {
1884
+ const baseUrl = apiUrl || DEFAULT_API31;
1885
+ const body = (p) => ({ filters: [], page: 1, limit: 25, ...p });
1886
+ return {
1887
+ /** Search open-data contacts by filter. */
1888
+ async contacts(params = {}) {
1889
+ return request(baseUrl, "/api/v1/search/contacts", apiKey, { method: "POST", body: body(params) });
1890
+ },
1891
+ /** Search open-data companies by filter. */
1892
+ async companies(params = {}) {
1893
+ return request(baseUrl, "/api/v1/search/companies", apiKey, { method: "POST", body: body(params) });
1894
+ },
1895
+ /** Search contacts and save the matches into a new list. */
1896
+ async saveContacts(params) {
1897
+ const resp = await request(baseUrl, "/api/v1/search/contacts/save", apiKey, {
1898
+ method: "POST",
1899
+ body: { filters: [], page: 1, limit: 25, max_results: 1e3, ...params }
1900
+ });
1901
+ return resp.data ?? resp;
1902
+ },
1903
+ /** Search companies and save the matches into a new list. */
1904
+ async saveCompanies(params) {
1905
+ const resp = await request(baseUrl, "/api/v1/search/companies/save", apiKey, {
1906
+ method: "POST",
1907
+ body: { filters: [], page: 1, limit: 25, max_results: 1e3, ...params }
1908
+ });
1909
+ return resp.data ?? resp;
1910
+ }
1911
+ };
1912
+ };
1913
+
1914
+ // src/agency.ts
1915
+ var DEFAULT_API32 = "https://be.graph8.com";
1916
+ var createAgencyClient = (apiKey, apiUrl) => {
1917
+ const baseUrl = apiUrl || DEFAULT_API32;
1918
+ return {
1919
+ /** Describe the agency credential: agency org + authorized client count. */
1920
+ async me() {
1921
+ const resp = await request(baseUrl, "/api/v1/agency/me", apiKey);
1922
+ return resp.data ?? resp;
1923
+ },
1924
+ /** List the client orgs this agency key may target via `X-Target-Org-Id`. */
1925
+ async clients() {
1926
+ return request(baseUrl, "/api/v1/agency/clients", apiKey);
1927
+ }
1928
+ };
1929
+ };
1930
+
1931
+ // src/marketplace.ts
1932
+ var DEFAULT_API33 = "https://be.graph8.com";
1933
+ var createMarketplaceClient = (apiKey, apiUrl) => {
1934
+ const baseUrl = apiUrl || DEFAULT_API33;
1935
+ const base = "/api/v1/marketplace";
1936
+ return {
1937
+ /** Your own marketplace SDR profile. */
1938
+ async profile() {
1939
+ const resp = await request(baseUrl, `${base}/me/profile`, apiKey);
1940
+ return resp.data ?? resp;
1941
+ },
1942
+ /** Pending hire offers you can accept or reject. */
1943
+ async offers() {
1944
+ const resp = await request(
1945
+ baseUrl,
1946
+ `${base}/me/offers`,
1947
+ apiKey
1948
+ );
1949
+ return resp.data ?? resp;
1950
+ },
1951
+ /** Accept a pending hire offer (by hiring id). */
1952
+ async acceptOffer(hiringId) {
1953
+ const resp = await request(
1954
+ baseUrl,
1955
+ `${base}/me/offers/${hiringId}/accept`,
1956
+ apiKey,
1957
+ { method: "POST" }
1958
+ );
1959
+ return resp.data ?? resp;
1960
+ },
1961
+ /** Reject a pending hire offer (by hiring id). */
1962
+ async rejectOffer(hiringId) {
1963
+ const resp = await request(
1964
+ baseUrl,
1965
+ `${base}/me/offers/${hiringId}/reject`,
1966
+ apiKey,
1967
+ { method: "POST" }
1968
+ );
1969
+ return resp.data ?? resp;
1970
+ },
1971
+ /** Your active hiring contracts. */
1972
+ async hirings() {
1973
+ const resp = await request(
1974
+ baseUrl,
1975
+ `${base}/me/hirings`,
1976
+ apiKey
1977
+ );
1978
+ return resp.data ?? resp;
1979
+ }
1980
+ };
1981
+ };
1982
+
1983
+ // src/snippet.ts
1984
+ var DEFAULT_API34 = "https://be.graph8.com";
1985
+ var createSnippetClient = (apiKey, apiUrl) => {
1986
+ const baseUrl = apiUrl || DEFAULT_API34;
1987
+ return {
1988
+ /** Get your org's tracking snippet (write key + React/script-tag embeds + config). */
1989
+ async get() {
1990
+ const resp = await request(baseUrl, "/api/v1/snippet", apiKey);
1991
+ return resp.data ?? resp;
2044
1992
  }
2045
1993
  };
2046
1994
  };
2047
1995
 
2048
1996
  // src/core.ts
2049
1997
  var DEFAULT_HOST = "https://t.graph8.com";
2050
- var DEFAULT_API30 = "https://be.graph8.com";
1998
+ var DEFAULT_API35 = "https://be.graph8.com";
2051
1999
  var G8 = class {
2052
2000
  constructor() {
2053
2001
  /** @internal */
@@ -2112,6 +2060,16 @@ var G8 = class {
2112
2060
  this._studio = null;
2113
2061
  /** @internal */
2114
2062
  this._meetings = null;
2063
+ /** @internal */
2064
+ this._audiences = null;
2065
+ /** @internal */
2066
+ this._search = null;
2067
+ /** @internal */
2068
+ this._agency = null;
2069
+ /** @internal */
2070
+ this._marketplace = null;
2071
+ /** @internal */
2072
+ this._snippet = null;
2115
2073
  }
2116
2074
  /**
2117
2075
  * Initialize the graph8 SDK. Must be called before any other method.
@@ -2126,7 +2084,7 @@ var G8 = class {
2126
2084
  debug: config.debug
2127
2085
  });
2128
2086
  }
2129
- const apiUrl = config.apiUrl || DEFAULT_API30;
2087
+ const apiUrl = config.apiUrl || DEFAULT_API35;
2130
2088
  const writeKey = config.writeKey || "";
2131
2089
  const apiKey = config.apiKey || "";
2132
2090
  if (writeKey) {
@@ -2161,6 +2119,11 @@ var G8 = class {
2161
2119
  this._intent = createIntentClient(apiKey, apiUrl);
2162
2120
  this._studio = createStudioClient(apiKey, apiUrl);
2163
2121
  this._meetings = createMeetingsClient(apiKey, apiUrl);
2122
+ this._audiences = createAudiencesClient(apiKey, apiUrl);
2123
+ this._search = createSearchClient(apiKey, apiUrl);
2124
+ this._agency = createAgencyClient(apiKey, apiUrl);
2125
+ this._marketplace = createMarketplaceClient(apiKey, apiUrl);
2126
+ this._snippet = createSnippetClient(apiKey, apiUrl);
2164
2127
  this._signals = createSignalsClient(apiKey, true, apiUrl);
2165
2128
  }
2166
2129
  }
@@ -2327,6 +2290,31 @@ var G8 = class {
2327
2290
  this._assertKey("meetings");
2328
2291
  return this._meetings;
2329
2292
  }
2293
+ /** Audiences — sync audience lists to ad platforms (Meta, LinkedIn, Google, X) (requires API key). */
2294
+ get audiences() {
2295
+ this._assertKey("audiences");
2296
+ return this._audiences;
2297
+ }
2298
+ /** Search — prospect open-data contacts + companies by filter, optionally save to a list (requires API key). */
2299
+ get search() {
2300
+ this._assertKey("search");
2301
+ return this._search;
2302
+ }
2303
+ /** Agency — for agency keys: discover the agency credential + the client orgs it may target (requires API key). */
2304
+ get agency() {
2305
+ this._assertKey("agency");
2306
+ return this._agency;
2307
+ }
2308
+ /** Marketplace — for SDR/AE talent: profile, hire offers (accept/reject), active hirings (requires API key). */
2309
+ get marketplace() {
2310
+ this._assertKey("marketplace");
2311
+ return this._marketplace;
2312
+ }
2313
+ /** Snippet — fetch your org's tracking snippet (write key + React/script-tag embeds + config) for embedding (requires API key). */
2314
+ get snippet() {
2315
+ this._assertKey("snippet");
2316
+ return this._snippet;
2317
+ }
2330
2318
  /** Whether the SDK has been initialized. */
2331
2319
  get initialized() {
2332
2320
  return this.config !== null;