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