@graph8/sdk 0.5.3 → 0.7.2

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