@graph8/sdk 0.5.3 → 0.7.2

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