@graph8/sdk 0.5.3 → 0.7.1

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