@graph8/sdk 0.5.3 → 0.7.2

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