@graph8/sdk 0.5.1 → 0.7.1

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