@graph8/sdk 0.12.2 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/core.ts","../src/forms.ts","../src/utils.ts","../src/visitors.ts","../src/copilot.ts","../src/chat.ts","../src/calendar.ts","../src/http.ts","../src/enrich.ts","../src/sequences.ts","../src/campaigns.ts","../src/signals.ts","../src/voice.ts","../src/webhooks.ts","../src/contacts.ts","../src/companies.ts","../src/lists.ts","../src/notes.ts","../src/tasks.ts","../src/fields.ts","../src/deals.ts","../src/inbox.ts","../src/quotes.ts","../src/pipelines.ts","../src/workflows.ts","../src/skills.ts","../src/intent.ts","../src/studio.ts","../src/meetings.ts","../src/audiences.ts","../src/search.ts","../src/agency.ts","../src/marketplace.ts","../src/snippet.ts","../src/appTokens.ts","../src/appClient.ts"],"sourcesContent":["export { g8 } from \"./core\";\n\n// HTTP core (B2): typed errors, automatic retries, idempotency, auto-pagination.\nexport { G8Error, request, paginate, isRetryableStatus, parseRetryAfter, backoffDelayMs } from \"./http\";\nexport type { RequestOptions, PaginatedResponse } from \"./http\";\n\n// Core types\nexport type { G8Config, G8PrivacyConfig, TrackProperties, IdentifyProperties, EnrichLookupResult } from \"./types\";\n\n// Module types\nexport type { VisitorCompany, VisitorScore } from \"./visitors\";\nexport type { CopilotConfig } from \"./copilot\";\nexport type { ChatConfig } from \"./chat\";\nexport type { CalendarConfig, TimeSlot, Booking, BookingRequest } from \"./calendar\";\nexport type { PersonEnrichment, CompanyEnrichment, EmailVerification, SearchFilter, SearchResults } from \"./enrich\";\nexport type {\n Sequence,\n AddToSequenceConfig,\n SequenceListItem,\n SequenceListParams,\n SequenceDetail,\n SequenceContactItem,\n SequenceContactsParams,\n SequenceActionResult,\n SequenceKind,\n SequenceStepType,\n SequenceStepInputType,\n SequenceStepConfig,\n SequenceChannelConfig,\n SequenceCreateParams,\n SequenceCreateResult,\n SequenceUpdateParams,\n SequenceStepUpdateParams,\n SequencePreviewStep,\n SequencePreviewChannel,\n SequencePreview,\n SequenceAnalytics,\n} from \"./sequences\";\nexport type {\n InboxChannel,\n InboxContact,\n InboxTag,\n InboxAssignee,\n InboxMessage,\n InboxThread,\n InboxListParams,\n InboxAssignResult,\n InboxTagResult,\n InboxDraft,\n InboxSendParams,\n InboxSendResult,\n} from \"./inbox\";\nexport type {\n Campaign,\n CampaignCreateConfig,\n CampaignLaunchExecution,\n CampaignLaunchResult,\n} from \"./campaigns\";\nexport type { IntentSignals } from \"./signals\";\nexport type {\n VoicePagination,\n DialerSessionSummary,\n DialerSessionsListResult,\n DialerSessionsListParams,\n DialerStatsParams,\n DialerReportFilters,\n DialerReportMetric,\n DialerStatsResult,\n DialerNumberInfo,\n DialerNumbersListResult,\n MissedCallback,\n MissedCallbacksResult,\n CallGradingResult,\n DialerAgentSummary,\n DialerAgentsListResult,\n DialerAgentsListParams,\n DialerSessionCreateParams,\n DialerSessionCreateResult,\n DialerSessionStatus,\n DialerSessionStatusUpdateResult,\n DialerSessionResumeResult,\n} from \"./voice\";\nexport type { WebhookEvent, WebhookEventPayload, ConstructEventOptions } from \"./webhooks\";\nexport { constructEvent, WebhookSignatureError, KNOWN_WEBHOOK_EVENTS } from \"./webhooks\";\nexport type {\n Contact,\n ContactListParams,\n ContactCreateParams,\n ContactUpdateParams,\n ContactColumn,\n ContactColumnCreateParams,\n} from \"./contacts\";\nexport type {\n Company,\n CompanyListParams,\n CompanyUpdateParams,\n CompanyContact,\n CompanyColumn,\n CompanyColumnCreateParams,\n} from \"./companies\";\nexport type { ContactList, ListContact } from \"./lists\";\nexport type { Note } from \"./notes\";\nexport type { Task, TaskCreateParams, TaskUpdateParams, TaskListParams } from \"./tasks\";\nexport type {\n Field,\n CreatedField,\n FieldCreateParams,\n FieldDeleteParams,\n SetFieldValueParams,\n} from \"./fields\";\nexport type {\n Deal,\n DealCreateParams,\n DealUpdateParams,\n DealListParams,\n Pipeline,\n PipelineStage,\n ContactDeal,\n PaginationMeta,\n} from \"./deals\";\nexport type {\n QuoteSummary,\n QuoteDetail,\n QuoteLineItem,\n QuoteStatus,\n QuoteCreateParams,\n QuoteUpdateParams,\n QuoteSendParams,\n QuoteListParams,\n QuotableProduct,\n QuoteSettings,\n} from \"./quotes\";\nexport type {\n StagePipeline,\n StagePipelineStage,\n StagePipelineCreateParams,\n StagePipelineUpdateParams,\n StageCreateParams,\n StageUpdateParams,\n EvidenceKey,\n PipelineSuggestion,\n} from \"./pipelines\";\nexport type {\n Workflow,\n WorkflowNode,\n WorkflowConnection,\n WorkflowConfig,\n WorkflowCreateParams,\n WorkflowUpdateParams,\n WorkflowExecution,\n WorkflowListParams,\n NodeTypeSchema,\n} from \"./workflows\";\nexport type {\n Skill,\n SkillType,\n SkillInputField,\n SkillCreateLLMParams,\n SkillCreateAPIParams,\n SkillUpdateLLMParams,\n SkillUpdateAPIParams,\n SkillTemplate,\n SkillListParams,\n} from \"./skills\";\nexport type {\n IntentKeyword,\n IntentPage,\n IntentVisitor,\n IntentCompany,\n IntentContact,\n IntentStats,\n} from \"./intent\";\nexport type {\n GlobalContextDocument,\n Persona,\n ICP,\n IntelligenceData,\n ResearchReport,\n} from \"./studio\";\nexport type {\n MeetingSummary,\n MeetingDetail,\n MeetingAttendee,\n MeetingTranscriptLine,\n MeetingAnalysis,\n MeetingListParams,\n} from \"./meetings\";\nexport type {\n AudienceSync,\n AudienceSyncPlatform,\n AudienceSyncMode,\n AudienceSyncCreateParams,\n AudienceSyncUpdateParams,\n AudienceSyncRun,\n AudienceSyncError,\n} from \"./audiences\";\nexport type {\n SearchOperator,\n SearchCondition,\n SearchParams,\n SearchSaveParams,\n SearchContactItem,\n SearchCompanyItem,\n SearchSaveResult,\n} from \"./search\";\nexport type { AgencyInfo, AgencyClient } from \"./agency\";\nexport type { MarketplaceProfile, MarketplaceOffer, MarketplaceHiring } from \"./marketplace\";\nexport type { Snippet } from \"./snippet\";\n\n// App Platform (M6-9): hosted-app browser + service auth clients over\n// short-lived app tokens. Additive — independent of the g8 tracking/API-key\n// singleton. Each client exposes a token-bound `request()` that attaches the\n// minted app token to Developer API calls. The typed `apps` (control-plane) and\n// `objects` (custom-object CRUD) resource modules are DEFERRED until their\n// backend routes ship and freeze (M6-3 / M6-5).\nexport { createGraph8AppClient, createGraph8ServiceClient } from \"./appClient\";\nexport type {\n Graph8AppClient,\n Graph8AppClientConfig,\n Graph8ServiceClient,\n Graph8ServiceClientConfig,\n} from \"./appClient\";\nexport {\n createTokenManager,\n createAppRequester,\n exchangeBrowserToken,\n exchangeServiceToken,\n DEFAULT_APP_API,\n} from \"./appTokens\";\nexport type { AppTokenResponse, TokenManager, AppRequest, AppRequestOptions } from \"./appTokens\";\n","import { jitsuAnalytics } from \"@jitsu/js\";\nimport type { AnalyticsInterface } from \"@jitsu/js\";\nimport type { G8Config, TrackProperties, IdentifyProperties } from \"./types\";\nimport { createFormsClient } from \"./forms\";\nimport { createVisitorsClient } from \"./visitors\";\nimport { createCopilotClient } from \"./copilot\";\nimport { createChatClient } from \"./chat\";\nimport { createCalendarClient } from \"./calendar\";\nimport { createEnrichClient } from \"./enrich\";\nimport { createSequencesClient } from \"./sequences\";\nimport { createCampaignsClient } from \"./campaigns\";\nimport { createSignalsClient } from \"./signals\";\nimport { createVoiceClient } from \"./voice\";\nimport { createWebhooksClient } from \"./webhooks\";\nimport { createContactsClient } from \"./contacts\";\nimport { createCompaniesClient } from \"./companies\";\nimport { createListsClient } from \"./lists\";\nimport { createNotesClient } from \"./notes\";\nimport { createTasksClient } from \"./tasks\";\nimport { createFieldsClient } from \"./fields\";\nimport { createDealsClient } from \"./deals\";\nimport { createInboxClient } from \"./inbox\";\nimport { createQuotesClient } from \"./quotes\";\nimport { createPipelinesClient } from \"./pipelines\";\nimport { createWorkflowsClient } from \"./workflows\";\nimport { createSkillsClient } from \"./skills\";\nimport { createIntentClient } from \"./intent\";\nimport { createStudioClient } from \"./studio\";\nimport { createMeetingsClient } from \"./meetings\";\nimport { createAudiencesClient } from \"./audiences\";\nimport { createSearchClient } from \"./search\";\nimport { createAgencyClient } from \"./agency\";\nimport { createMarketplaceClient } from \"./marketplace\";\nimport { createSnippetClient } from \"./snippet\";\nimport { isServer } from \"./utils\";\n\nconst DEFAULT_HOST = \"https://t.graph8.com\";\nconst DEFAULT_API = \"https://be.graph8.com\";\n\n/**\n * graph8 SDK client.\n *\n * Handles event tracking, identity, and progressive forms.\n *\n * Usage:\n * import { g8 } from '@graph8/sdk';\n * g8.init({ writeKey: 'your_write_key' });\n * g8.track('page_view', { page: '/pricing' });\n * g8.identify('user@acme.com', { name: 'John', company: 'Acme' });\n */\nclass G8 {\n /** @internal */ client: AnalyticsInterface | null = null;\n /** @internal */ config: G8Config | null = null;\n /** @internal */ _forms: ReturnType<typeof createFormsClient> | null = null;\n /** @internal */ _visitors: ReturnType<typeof createVisitorsClient> | null = null;\n /** @internal */ _copilot: ReturnType<typeof createCopilotClient> | null = null;\n /** @internal */ _chat: ReturnType<typeof createChatClient> | null = null;\n /** @internal */ _calendar: ReturnType<typeof createCalendarClient> | null = null;\n /** @internal */ _enrich: ReturnType<typeof createEnrichClient> | null = null;\n /** @internal */ _sequences: ReturnType<typeof createSequencesClient> | null = null;\n /** @internal */ _campaigns: ReturnType<typeof createCampaignsClient> | null = null;\n /** @internal */ _signals: ReturnType<typeof createSignalsClient> | null = null;\n /** @internal */ _voice: ReturnType<typeof createVoiceClient> | null = null;\n /** @internal */ _webhooks: ReturnType<typeof createWebhooksClient> | null = null;\n /** @internal */ _contacts: ReturnType<typeof createContactsClient> | null = null;\n /** @internal */ _companies: ReturnType<typeof createCompaniesClient> | null = null;\n /** @internal */ _lists: ReturnType<typeof createListsClient> | null = null;\n /** @internal */ _notes: ReturnType<typeof createNotesClient> | null = null;\n /** @internal */ _tasks: ReturnType<typeof createTasksClient> | null = null;\n /** @internal */ _fields: ReturnType<typeof createFieldsClient> | null = null;\n /** @internal */ _deals: ReturnType<typeof createDealsClient> | null = null;\n /** @internal */ _inbox: ReturnType<typeof createInboxClient> | null = null;\n /** @internal */ _quotes: ReturnType<typeof createQuotesClient> | null = null;\n /** @internal */ _pipelines: ReturnType<typeof createPipelinesClient> | null = null;\n /** @internal */ _workflows: ReturnType<typeof createWorkflowsClient> | null = null;\n /** @internal */ _skills: ReturnType<typeof createSkillsClient> | null = null;\n /** @internal */ _intent: ReturnType<typeof createIntentClient> | null = null;\n /** @internal */ _studio: ReturnType<typeof createStudioClient> | null = null;\n /** @internal */ _meetings: ReturnType<typeof createMeetingsClient> | null = null;\n /** @internal */ _audiences: ReturnType<typeof createAudiencesClient> | null = null;\n /** @internal */ _search: ReturnType<typeof createSearchClient> | null = null;\n /** @internal */ _agency: ReturnType<typeof createAgencyClient> | null = null;\n /** @internal */ _marketplace: ReturnType<typeof createMarketplaceClient> | null = null;\n /** @internal */ _snippet: ReturnType<typeof createSnippetClient> | null = null;\n\n /**\n * Initialize the graph8 SDK. Must be called before any other method.\n * Safe to call on the server (SSR) - becomes a no-op for tracking.\n */\n init(config: G8Config): void {\n this.config = config;\n\n if (!isServer) {\n this.client = jitsuAnalytics({\n host: config.host || DEFAULT_HOST,\n writeKey: config.writeKey || \"\",\n debug: config.debug,\n });\n }\n\n const apiUrl = config.apiUrl || DEFAULT_API;\n const writeKey = config.writeKey || \"\";\n const apiKey = config.apiKey || \"\";\n\n // Public (write key) clients\n if (writeKey) {\n this._forms = createFormsClient(writeKey, apiUrl);\n this._visitors = createVisitorsClient(writeKey, apiUrl);\n this._copilot = createCopilotClient(writeKey, apiUrl);\n this._chat = createChatClient(writeKey, apiUrl);\n this._calendar = createCalendarClient(apiUrl);\n this._signals = createSignalsClient(writeKey, false, apiUrl);\n }\n\n // Private (API key) clients\n if (apiKey) {\n this._enrich = createEnrichClient(apiKey, apiUrl);\n this._sequences = createSequencesClient(apiKey, apiUrl);\n this._campaigns = createCampaignsClient(apiKey, apiUrl);\n this._voice = createVoiceClient(apiKey, apiUrl);\n this._webhooks = createWebhooksClient(apiKey, apiUrl);\n this._contacts = createContactsClient(apiKey, apiUrl);\n this._companies = createCompaniesClient(apiKey, apiUrl);\n this._lists = createListsClient(apiKey, apiUrl);\n this._notes = createNotesClient(apiKey, apiUrl);\n this._tasks = createTasksClient(apiKey, apiUrl);\n this._fields = createFieldsClient(apiKey, apiUrl);\n this._deals = createDealsClient(apiKey, apiUrl);\n this._inbox = createInboxClient(apiKey, apiUrl);\n this._quotes = createQuotesClient(apiKey, apiUrl);\n this._pipelines = createPipelinesClient(apiKey, apiUrl);\n this._workflows = createWorkflowsClient(apiKey, apiUrl);\n this._skills = createSkillsClient(apiKey, apiUrl);\n this._intent = createIntentClient(apiKey, apiUrl);\n this._studio = createStudioClient(apiKey, apiUrl);\n this._meetings = createMeetingsClient(apiKey, apiUrl);\n this._audiences = createAudiencesClient(apiKey, apiUrl);\n this._search = createSearchClient(apiKey, apiUrl);\n this._agency = createAgencyClient(apiKey, apiUrl);\n this._marketplace = createMarketplaceClient(apiKey, apiUrl);\n this._snippet = createSnippetClient(apiKey, apiUrl);\n // Override signals with API key version for full access\n this._signals = createSignalsClient(apiKey, true, apiUrl);\n }\n }\n\n // -- Core tracking (via Jitsu) --\n\n /** Track a custom event. */\n track(event: string, properties?: TrackProperties): void { this.client?.track(event, properties); }\n\n /** Identify a user with properties. */\n identify(userId: string, properties?: IdentifyProperties): void { this.client?.identify(userId, properties); }\n\n /** Track a page view. */\n page(properties?: TrackProperties): void { this.client?.page(properties); }\n\n /** Clear user identity (on logout). */\n reset(): void { this.client?.reset(); }\n\n // -- Module accessors --\n\n /** Progressive form helpers. */\n get forms() { this._assertInit(); return this._forms!; }\n\n /** Visitor intelligence - IP to company, engagement scoring. */\n get visitors() { this._assertInit(); return this._visitors!; }\n\n /** AI copilot widget. */\n get copilot() { this._assertInit(); return this._copilot!; }\n\n /** Webchat widget. */\n get chat() { this._assertInit(); return this._chat!; }\n\n /** Calendar and booking. */\n get calendar() { this._assertInit(); return this._calendar!; }\n\n /** Enrichment API (requires API key). */\n get enrich() { this._assertKey(\"enrich\"); return this._enrich!; }\n\n /** Sequences (requires API key). */\n get sequences() { this._assertKey(\"sequences\"); return this._sequences!; }\n\n /** Campaigns (requires API key). */\n get campaigns() { this._assertKey(\"campaigns\"); return this._campaigns!; }\n\n /** Intent signals. */\n get signals() { this._assertInit(); return this._signals!; }\n\n /** Voice AI (requires API key). */\n get voice() { this._assertKey(\"voice\"); return this._voice!; }\n\n /** Webhook event listeners (requires API key). */\n get webhooks() { this._assertKey(\"webhooks\"); return this._webhooks!; }\n\n /** Contacts CRUD (requires API key). */\n get contacts() { this._assertKey(\"contacts\"); return this._contacts!; }\n\n /** Companies CRUD (requires API key). */\n get companies() { this._assertKey(\"companies\"); return this._companies!; }\n\n /** Lists management (requires API key). */\n get lists() { this._assertKey(\"lists\"); return this._lists!; }\n\n /** Notes on contacts (requires API key). */\n get notes() { this._assertKey(\"notes\"); return this._notes!; }\n\n /** Tasks on contacts (requires API key). */\n get tasks() { this._assertKey(\"tasks\"); return this._tasks!; }\n\n /** Custom fields management (requires API key). */\n get fields() { this._assertKey(\"fields\"); return this._fields!; }\n\n /** Deals and pipelines (requires API key). */\n get deals() { this._assertKey(\"deals\"); return this._deals!; }\n\n /** Multi-channel inbox — read + reply across email, SMS, LinkedIn (requires API key). */\n get inbox() { this._assertKey(\"inbox\"); return this._inbox!; }\n\n /** Quote-to-cash: draft, send, sign, payment-link quotes (requires API key). */\n get quotes() { this._assertKey(\"quotes\"); return this._quotes!; }\n\n /** Stage Checklist v2 pipelines: workflow stages with evidence + scripts (requires API key). */\n get pipelines() { this._assertKey(\"pipelines\"); return this._pipelines!; }\n\n /** Workflow builder — multi-node automation graphs with execution lifecycle (requires API key). */\n get workflows() { this._assertKey(\"workflows\"); return this._workflows!; }\n\n /** Skill authoring — LLM and API building blocks that workflows compose (requires API key). */\n get skills() { this._assertKey(\"skills\"); return this._skills!; }\n\n /** Intent tracking — keyword groups, page visitors, account-level intent (requires API key). */\n get intent() { this._assertKey(\"intent\"); return this._intent!; }\n\n /** Studio context — ICPs, personas, brand briefs, intelligence, AI research reports (requires API key). */\n get studio() { this._assertKey(\"studio\"); return this._studio!; }\n\n /** Meetings — read scheduled, completed, cancelled meetings with transcripts + AI analysis (requires API key). */\n get meetings() { this._assertKey(\"meetings\"); return this._meetings!; }\n\n /** Audiences — sync audience lists to ad platforms (Meta, LinkedIn, Google, X) (requires API key). */\n get audiences() { this._assertKey(\"audiences\"); return this._audiences!; }\n\n /** Search — prospect open-data contacts + companies by filter, optionally save to a list (requires API key). */\n get search() { this._assertKey(\"search\"); return this._search!; }\n\n /** Agency — for agency keys: discover the agency credential + the client orgs it may target (requires API key). */\n get agency() { this._assertKey(\"agency\"); return this._agency!; }\n\n /** Marketplace — for SDR/AE talent: profile, hire offers (accept/reject), active hirings (requires API key). */\n get marketplace() { this._assertKey(\"marketplace\"); return this._marketplace!; }\n\n /** Snippet — fetch your org's tracking snippet (write key + React/script-tag embeds + config) for embedding (requires API key). */\n get snippet() { this._assertKey(\"snippet\"); return this._snippet!; }\n\n /** Whether the SDK has been initialized. */\n get initialized(): boolean { return this.config !== null; }\n\n /** @internal */\n _assertInit(): void {\n if (!this.config) throw new Error(\"g8.init() must be called first\");\n }\n\n /** @internal */\n _assertKey(module: string): void {\n this._assertInit();\n if (!this.config!.apiKey) throw new Error(`g8.${module} requires an API key. Use g8.init({ apiKey: '...' })`);\n }\n}\n\n/** Singleton g8 client instance. */\nexport const g8 = new G8();\n","import type { EnrichLookupResult } from \"./types\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\n/**\n * Progressive form enrichment - checks what fields graph8 already knows\n * about a contact so your form can skip those fields.\n *\n * Calls POST /api/v1/public/enrich/lookup (authenticated via write key).\n */\nexport const createFormsClient = (writeKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n /**\n * Look up known fields for an email address.\n *\n * Use this in progressive forms: after the user enters their email,\n * check what graph8 already knows and only show missing fields.\n */\n async lookup(email: string): Promise<EnrichLookupResult> {\n const resp = await fetch(`${baseUrl}/api/v1/public/enrich/lookup`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Write-Key\": writeKey,\n },\n body: JSON.stringify({ email }),\n });\n\n if (!resp.ok) {\n return { found: false, known_fields: {}, missing_fields: [] };\n }\n\n const data = await resp.json();\n return data.data || data;\n },\n };\n};\n","/** Check if running on the server (SSR). */\nexport const isServer = typeof window === \"undefined\";\n\n/** Safely get the window object (returns undefined on server). */\nexport const getWindow = (): Window | undefined =>\n isServer ? undefined : window;\n\n/**\n * Derive the APP origin (which serves the embed pages) from the API origin.\n *\n * `apiUrl` addresses the FastAPI backend — `be.graph8.com` in production — and\n * that host does not serve any page routes: `/webchat/embed` and\n * `/copilot/embed` are Next.js pages on `app.graph8.com`. The two hosts are\n * disjoint, so a single base URL cannot address both; an iframe pointed at\n * `${apiUrl}/webchat/embed` renders the backend's `{\"detail\":\"Not Found\"}`.\n *\n * This is the inverse of `detectApiBaseUrl` in\n * `frontend/chat-script/src/config.ts`, which maps `app.` → `be.` to find the\n * API from the page. Same host convention, read the other way.\n *\n * Anything that is not a recognised graph8 or localhost origin is returned\n * unchanged, so a self-hosted deployment keeps serving both from one host.\n */\nexport const resolveAppBaseUrl = (apiBaseUrl: string): string => {\n try {\n const url = new URL(apiBaseUrl);\n if (url.hostname === \"localhost\" || url.hostname === \"127.0.0.1\") {\n // Backend dev ports (8000 http / 8011 https) → Next dev server on 3000.\n url.port = \"3000\";\n return url.origin;\n }\n if (url.hostname.endsWith(\"graph8.com\")) {\n url.hostname = url.hostname.replace(/^be\\./, \"app.\");\n return url.origin;\n }\n } catch {\n // Not a parseable URL — leave the caller's value alone.\n }\n return apiBaseUrl;\n};\n","import type { G8Config } from \"./types\";\nimport { isServer } from \"./utils\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface VisitorCompany {\n company_name: string | null;\n company_domain: string | null;\n industry: string | null;\n employee_count: string | null;\n city: string | null;\n country: string | null;\n confidence: number;\n}\n\nexport interface VisitorScore {\n engagement: number;\n intent: \"low\" | \"medium\" | \"high\";\n signals: string[];\n}\n\ntype IntentCallback = (visitor: VisitorCompany & VisitorScore) => void;\n\n/**\n * Visitor intelligence - identify anonymous visitors by IP → company.\n *\n * Requires write key auth. Works client-side only.\n */\nexport const createVisitorsClient = (writeKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n let intentInterval: ReturnType<typeof setInterval> | null = null;\n\n const headers = () => ({\n \"Content-Type\": \"application/json\",\n \"X-Write-Key\": writeKey,\n });\n\n return {\n /** Identify the current visitor's company from their IP address. */\n async identify(): Promise<VisitorCompany> {\n if (isServer) return { company_name: null, company_domain: null, industry: null, employee_count: null, city: null, country: null, confidence: 0 };\n\n const resp = await fetch(`${baseUrl}/api/v1/public/visitors/company`, { headers: headers() });\n if (!resp.ok) return { company_name: null, company_domain: null, industry: null, employee_count: null, city: null, country: null, confidence: 0 };\n const data = await resp.json();\n return data.data || data;\n },\n\n /** Get engagement score for the current visitor. */\n async score(): Promise<VisitorScore> {\n if (isServer) return { engagement: 0, intent: \"low\", signals: [] };\n\n const resp = await fetch(`${baseUrl}/api/v1/public/visitors/score`, { headers: headers() });\n if (!resp.ok) return { engagement: 0, intent: \"low\", signals: [] };\n const data = await resp.json();\n return data.data || data;\n },\n\n /** Listen for visitors matching an intent level. Polls every 30 seconds. */\n onIntent(level: \"low\" | \"medium\" | \"high\", callback: IntentCallback): () => void {\n if (isServer) return () => {};\n\n const check = async () => {\n try {\n const [company, scoreData] = await Promise.all([\n this.identify(),\n this.score(),\n ]);\n if (scoreData.intent === level || (level === \"medium\" && scoreData.intent === \"high\")) {\n callback({ ...company, ...scoreData });\n }\n } catch { /* silent */ }\n };\n\n check();\n intentInterval = setInterval(check, 30_000);\n\n return () => {\n if (intentInterval) clearInterval(intentInterval);\n };\n },\n };\n};\n","import { isServer } from \"./utils\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface CopilotConfig {\n /** Initial greeting message */\n greeting?: string;\n /** Context passed to the AI */\n context?: Record<string, unknown>;\n /** Widget position */\n position?: \"bottom-right\" | \"bottom-left\";\n /** Widget theme */\n theme?: \"light\" | \"dark\" | \"auto\";\n}\n\ntype CopilotEvent = \"message\" | \"tool_used\" | \"open\" | \"close\" | \"error\";\ntype CopilotCallback = (data: Record<string, unknown>) => void;\ntype ActionHandler = (params: Record<string, unknown>) => void | Promise<void>;\n\n/**\n * Copilot - embeddable AI assistant with org knowledge base access.\n *\n * Client-side (write key) for the widget embed.\n */\nexport const createCopilotClient = (writeKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n const listeners: Map<CopilotEvent, CopilotCallback[]> = new Map();\n const actions: Map<string, ActionHandler> = new Map();\n let widgetEl: HTMLElement | null = null;\n\n const emit = (event: CopilotEvent, data: Record<string, unknown> = {}) => {\n listeners.get(event)?.forEach((cb) => cb(data));\n };\n\n return {\n /** Open the copilot widget. */\n open(config?: CopilotConfig): void {\n if (isServer) return;\n\n // Remove existing widget if any\n if (widgetEl) widgetEl.remove();\n\n const position = config?.position || \"bottom-right\";\n const posStyle = position === \"bottom-left\" ? \"left:16px;\" : \"right:16px;\";\n\n const iframe = document.createElement(\"iframe\");\n iframe.src = `${baseUrl}/copilot/embed?write_key=${writeKey}&theme=${config?.theme || \"auto\"}`;\n iframe.style.cssText = `position:fixed;bottom:16px;${posStyle}width:400px;height:600px;border:none;border-radius:12px;box-shadow:0 8px 32px rgba(0,0,0,0.15);z-index:99998;`;\n iframe.id = \"g8-copilot-widget\";\n\n document.body.appendChild(iframe);\n widgetEl = iframe;\n emit(\"open\");\n },\n\n /** Send a message to the copilot programmatically. */\n async ask(message: string): Promise<string> {\n const resp = await fetch(`${baseUrl}/api/v1/public/copilot/chat`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", \"X-Write-Key\": writeKey },\n body: JSON.stringify({ message }),\n });\n if (!resp.ok) return \"Sorry, I could not process that request.\";\n const data = await resp.json();\n return data.response || data.data?.response || \"\";\n },\n\n /** Register a custom action the copilot can trigger. */\n registerAction(name: string, handler: ActionHandler): void {\n actions.set(name, handler);\n },\n\n /** Listen for copilot events. */\n on(event: CopilotEvent, callback: CopilotCallback): void {\n if (!listeners.has(event)) listeners.set(event, []);\n listeners.get(event)!.push(callback);\n },\n\n /** Close the copilot widget. */\n close(): void {\n if (widgetEl) {\n widgetEl.remove();\n widgetEl = null;\n emit(\"close\");\n }\n },\n };\n};\n","import { isServer, resolveAppBaseUrl } from \"./utils\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface ChatConfig {\n /** Agent ID to connect to */\n agentId?: string;\n /** Pre-filled first message */\n message?: string;\n /** Widget position */\n position?: \"bottom-right\" | \"bottom-left\";\n /** Widget theme */\n theme?: \"light\" | \"dark\" | \"auto\";\n /** Custom greeting */\n greeting?: string;\n /** Custom avatar URL */\n avatar?: string;\n}\n\ntype ChatEvent = \"message\" | \"human_transfer\" | \"open\" | \"close\" | \"error\";\ntype ChatCallback = (data: Record<string, unknown>) => void;\n\n/**\n * Webchat - live chat + AI chat embedded in any app.\n *\n * Uses WebSocket connection to the voice/chat agent backend.\n * Public - no API key needed (org/agent IDs are in the config).\n */\nexport const createChatClient = (writeKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n // The embed page is a Next.js route on the APP host, not the API host.\n const appUrl = resolveAppBaseUrl(baseUrl);\n const listeners: Map<ChatEvent, ChatCallback[]> = new Map();\n let widgetEl: HTMLElement | null = null;\n let ws: WebSocket | null = null;\n\n const emit = (event: ChatEvent, data: Record<string, unknown> = {}) => {\n listeners.get(event)?.forEach((cb) => cb(data));\n };\n\n return {\n /** Open the chat widget. */\n open(config?: ChatConfig): void {\n if (isServer) return;\n\n if (widgetEl) widgetEl.remove();\n\n const position = config?.position || \"bottom-right\";\n const posStyle = position === \"bottom-left\" ? \"left:16px;\" : \"right:16px;\";\n\n const iframe = document.createElement(\"iframe\");\n iframe.src = `${appUrl}/webchat/embed?write_key=${writeKey}&theme=${config?.theme || \"auto\"}`;\n iframe.style.cssText = `position:fixed;bottom:16px;${posStyle}width:380px;height:560px;border:none;border-radius:12px;box-shadow:0 8px 32px rgba(0,0,0,0.15);z-index:99997;`;\n iframe.id = \"g8-chat-widget\";\n\n document.body.appendChild(iframe);\n widgetEl = iframe;\n emit(\"open\");\n },\n\n /** Send a message. */\n send(message: string): void {\n if (ws && ws.readyState === WebSocket.OPEN) {\n ws.send(JSON.stringify({ type: \"message\", content: message }));\n }\n },\n\n /** Listen for chat events. */\n on(event: ChatEvent, callback: ChatCallback): void {\n if (!listeners.has(event)) listeners.set(event, []);\n listeners.get(event)!.push(callback);\n },\n\n /** Configure the chat appearance. */\n configure(config: Partial<ChatConfig>): void {\n // Send config to iframe via postMessage\n if (widgetEl && widgetEl instanceof HTMLIFrameElement) {\n widgetEl.contentWindow?.postMessage({ type: \"g8_chat_config\", config }, \"*\");\n }\n },\n\n /** Close the chat widget. */\n close(): void {\n if (ws) { ws.close(); ws = null; }\n if (widgetEl) { widgetEl.remove(); widgetEl = null; }\n emit(\"close\");\n },\n };\n};\n","import { isServer } from \"./utils\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface CalendarConfig {\n /** Event type slug (e.g. \"demo-30min\") */\n eventType: string;\n /** Username of the host */\n username: string;\n /** Pre-fill attendee info */\n prefill?: { name?: string; email?: string; notes?: string };\n}\n\nexport interface TimeSlot {\n start: string;\n end: string;\n available: boolean;\n}\n\nexport interface Booking {\n uid: string;\n confirmation_url: string;\n start_time: string;\n end_time: string;\n}\n\nexport interface BookingRequest {\n event_type_id: number;\n slot: string;\n attendee: { name: string; email: string; notes?: string };\n}\n\ntype CalendarEvent = \"booked\" | \"cancelled\" | \"slot_selected\";\ntype CalendarCallback = (data: Record<string, unknown>) => void;\n\n/**\n * Calendar and booking - show scheduling widgets, get slots, book meetings.\n *\n * Public endpoints - no API key needed.\n */\nexport const createCalendarClient = (apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n const listeners: Map<CalendarEvent, CalendarCallback[]> = new Map();\n\n return {\n /** Show a booking modal overlay. */\n show(config: CalendarConfig): void {\n if (isServer) return;\n\n const url = `${baseUrl}/appointments/${config.username}/${config.eventType}`;\n const params = new URLSearchParams();\n if (config.prefill?.name) params.set(\"name\", config.prefill.name);\n if (config.prefill?.email) params.set(\"email\", config.prefill.email);\n\n const iframe = document.createElement(\"iframe\");\n iframe.src = `${url}?${params.toString()}`;\n iframe.style.cssText = \"position:fixed;top:0;left:0;width:100%;height:100%;border:none;z-index:99999;background:rgba(0,0,0,0.5);\";\n iframe.id = \"g8-calendar-modal\";\n document.body.appendChild(iframe);\n\n // Close on escape\n const onKey = (e: KeyboardEvent) => { if (e.key === \"Escape\") { iframe.remove(); document.removeEventListener(\"keydown\", onKey); } };\n document.addEventListener(\"keydown\", onKey);\n },\n\n /** Embed booking widget inline in a container element. */\n embed(selector: string, config: CalendarConfig): void {\n if (isServer) return;\n\n const container = document.querySelector(selector);\n if (!container) return;\n\n const url = `${baseUrl}/appointments/${config.username}/${config.eventType}`;\n const iframe = document.createElement(\"iframe\");\n iframe.src = url;\n iframe.style.cssText = \"width:100%;min-height:600px;border:none;border-radius:8px;\";\n container.appendChild(iframe);\n },\n\n /** Get available time slots for an event type. */\n async slots(username: string, eventSlug: string, range: { start: string; end: string }): Promise<TimeSlot[]> {\n const params = new URLSearchParams({\n start_date: range.start,\n end_date: range.end,\n });\n const resp = await fetch(`${baseUrl}/appointments/public/slots?username=${username}&event_slug=${eventSlug}&${params.toString()}`);\n if (!resp.ok) return [];\n const data = await resp.json();\n return data.slots || data.data || [];\n },\n\n /** Book a meeting programmatically. */\n async book(request: BookingRequest): Promise<Booking | null> {\n const resp = await fetch(`${baseUrl}/appointments/public/bookings`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(request),\n });\n if (!resp.ok) return null;\n const data = await resp.json();\n const emit = listeners.get(\"booked\");\n if (emit) emit.forEach((cb) => cb(data));\n return data;\n },\n\n /** Listen for calendar events. */\n on(event: CalendarEvent, callback: CalendarCallback): void {\n if (!listeners.has(event)) listeners.set(event, []);\n listeners.get(event)!.push(callback);\n },\n };\n};\n","/**\n * Hardened HTTP core for the graph8 JS SDK (sprint B2).\n *\n * Resource clients historically called `fetch` directly and returned\n * `resp.json()` even on a 4xx/5xx — so API errors surfaced silently as \"data\".\n * This module centralizes the request path with:\n *\n * - a typed `G8Error` (status, type, code, request_id, detail) thrown on any\n * non-2xx response, parsed from the standard ApiError envelope;\n * - automatic retry with exponential backoff + jitter on 429 and 5xx,\n * honoring the `Retry-After` header;\n * - `Idempotency-Key` support for safe POST retries (pairs with the backend\n * idempotency added in A6);\n * - a cursor `paginate()` async-iterator that follows `next_cursor` (A9).\n *\n * `fetch` and `sleep` are injectable so the retry/backoff logic is\n * deterministically unit-testable without real timers or network.\n */\n\nexport class G8Error extends Error {\n readonly status: number;\n readonly type: string;\n readonly code?: string;\n readonly requestId?: string;\n readonly detail?: unknown;\n /** True when the failure class is transient (429/5xx/network). */\n readonly retryable: boolean;\n\n constructor(args: {\n message: string;\n status: number;\n type: string;\n code?: string;\n requestId?: string;\n detail?: unknown;\n retryable?: boolean;\n }) {\n super(args.message);\n this.name = \"G8Error\";\n this.status = args.status;\n this.type = args.type;\n this.code = args.code;\n this.requestId = args.requestId;\n this.detail = args.detail;\n this.retryable = args.retryable ?? false;\n // Restore prototype chain for `instanceof` across the TS->ES5 downlevel.\n Object.setPrototypeOf(this, G8Error.prototype);\n }\n}\n\n/** 429 and 5xx are the transient classes worth retrying. */\nexport function isRetryableStatus(status: number): boolean {\n return status === 429 || (status >= 500 && status <= 599);\n}\n\n/**\n * Methods that are NOT safe to blind-retry.\n *\n * GET/HEAD/OPTIONS are safe, and PUT/DELETE are idempotent by their HTTP\n * definition — resending them cannot create a second thing. POST and PATCH can,\n * so a retry needs an `Idempotency-Key` to be safe.\n */\nfunction isNonIdempotentMethod(method: string): boolean {\n const m = method.toUpperCase();\n return m === \"POST\" || m === \"PATCH\";\n}\n\n/**\n * Mint an idempotency key for a request the SDK might retry on its own.\n *\n * THE BUG THIS FIXES: this client retries 429/5xx AND network errors for every\n * method, POST included, while `idempotencyKey` was optional and unset by\n * default. So a timeout or a 500 on `POST /contacts` would silently be retried\n * and could create the contact TWICE — a duplicate the caller never asked for\n * and could not see. Generating a key here means the server (which now honours\n * the header on every POST) collapses those retries into one operation.\n *\n * The key is generated ONCE per logical request and reused across that\n * request's retries — a fresh key per attempt would defeat the whole point.\n */\nfunction newIdempotencyKey(): string {\n const c: any = (globalThis as any).crypto;\n if (c?.randomUUID) return c.randomUUID();\n if (c?.getRandomValues) {\n const bytes = c.getRandomValues(new Uint8Array(16));\n return Array.from(bytes as Uint8Array, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n }\n // Last-resort fallback for exotic runtimes with no crypto at all. Weaker, but\n // still far better than retrying with no key: collisions only matter within\n // one credential's 24h replay window.\n return `g8-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 12)}`;\n}\n\n/**\n * Parse a `Retry-After` header into milliseconds. Supports both the\n * delta-seconds form (`\"2\"`) and the HTTP-date form. Returns null when absent\n * or unparseable so the caller falls back to computed backoff.\n */\nexport function parseRetryAfter(header: string | null | undefined, nowMs: number = Date.now()): number | null {\n if (!header) return null;\n const secs = Number(header);\n if (Number.isFinite(secs) && secs >= 0) return Math.round(secs * 1000);\n const dateMs = Date.parse(header);\n if (!Number.isNaN(dateMs)) {\n const delta = dateMs - nowMs;\n return delta > 0 ? delta : 0;\n }\n return null;\n}\n\n/** Exponential backoff with full jitter, capped at 10s. */\nexport function backoffDelayMs(attempt: number, baseMs = 200, rand: () => number = Math.random): number {\n const expo = Math.min(baseMs * 2 ** attempt, 10_000);\n return Math.floor(expo / 2 + rand() * (expo / 2));\n}\n\nfunction defaultSleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction buildQuery(query?: Record<string, unknown>): string {\n if (!query) return \"\";\n const qs = new URLSearchParams();\n for (const [k, v] of Object.entries(query)) {\n if (v != null) qs.set(k, String(v));\n }\n const s = qs.toString();\n return s ? `?${s}` : \"\";\n}\n\nasync function toG8Error(resp: Response): Promise<G8Error> {\n let body: unknown = undefined;\n try {\n const text = await resp.text();\n body = text ? JSON.parse(text) : undefined;\n } catch {\n body = undefined;\n }\n const env = (body ?? {}) as Record<string, unknown>;\n const message =\n (typeof env.message === \"string\" && env.message) ||\n (typeof env.error === \"string\" && env.error) ||\n `Request failed with status ${resp.status}`;\n return new G8Error({\n message,\n status: resp.status,\n type: (typeof env.type === \"string\" && env.type) || (typeof env.error === \"string\" && env.error) || \"api_error\",\n code: typeof env.code === \"string\" ? env.code : String(resp.status),\n requestId:\n (typeof env.request_id === \"string\" && env.request_id) || resp.headers.get(\"x-request-id\") || undefined,\n detail: env.detail,\n retryable: isRetryableStatus(resp.status),\n });\n}\n\nexport interface RequestOptions {\n method?: string;\n body?: unknown;\n headers?: Record<string, string>;\n query?: Record<string, unknown>;\n /** Sent as the `Idempotency-Key` header (safe POST retries; see A6). */\n idempotencyKey?: string;\n /** Max retry attempts on 429/5xx/network (default 2 -> up to 3 tries). */\n maxRetries?: number;\n /** Base backoff in ms (default 200). */\n retryBaseMs?: number;\n signal?: AbortSignal;\n /** Injected for tests; defaults to global fetch. */\n fetchImpl?: typeof fetch;\n /** Injected for tests; defaults to a real setTimeout sleep. */\n sleepImpl?: (ms: number) => Promise<void>;\n}\n\n/**\n * Perform a JSON request with retries + typed errors. Returns the parsed JSON\n * envelope (callers unwrap `.data` as today). Throws `G8Error` on any non-2xx\n * after exhausting retries.\n */\nexport async function request<T = unknown>(\n baseUrl: string,\n path: string,\n apiKey: string,\n opts: RequestOptions = {},\n): Promise<T> {\n const {\n method = \"GET\",\n body,\n headers = {},\n query,\n idempotencyKey,\n maxRetries = 2,\n retryBaseMs = 200,\n signal,\n fetchImpl = fetch,\n sleepImpl = defaultSleep,\n } = opts;\n\n const url = baseUrl.replace(/\\/+$/, \"\") + path + buildQuery(query);\n const finalHeaders: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n ...headers,\n };\n // An explicit key always wins. Otherwise mint one for any method this client\n // might retry unsafely, so OUR OWN retry cannot duplicate the caller's write.\n // Skipped entirely when retries are disabled (maxRetries === 0) — with no\n // retry there is nothing to make safe, and the header stays off the wire for\n // callers who never wanted it.\n const effectiveIdempotencyKey =\n idempotencyKey ??\n (maxRetries > 0 && isNonIdempotentMethod(method) ? newIdempotencyKey() : undefined);\n if (effectiveIdempotencyKey) finalHeaders[\"Idempotency-Key\"] = effectiveIdempotencyKey;\n\n let attempt = 0;\n for (;;) {\n let resp: Response;\n try {\n resp = await fetchImpl(url, {\n method,\n headers: finalHeaders,\n body: body == null ? undefined : JSON.stringify(body),\n signal,\n });\n } catch (err) {\n if (attempt < maxRetries) {\n await sleepImpl(backoffDelayMs(attempt, retryBaseMs));\n attempt++;\n continue;\n }\n throw new G8Error({\n message: `Network error: ${(err as Error)?.message ?? \"request failed\"}`,\n status: 0,\n type: \"network_error\",\n retryable: true,\n });\n }\n\n if (resp.ok) {\n const text = await resp.text();\n return (text ? JSON.parse(text) : undefined) as T;\n }\n\n if (isRetryableStatus(resp.status) && attempt < maxRetries) {\n const retryAfter = parseRetryAfter(resp.headers.get(\"retry-after\"));\n const delay = retryAfter != null ? retryAfter : backoffDelayMs(attempt, retryBaseMs);\n await sleepImpl(delay);\n attempt++;\n continue;\n }\n\n throw await toG8Error(resp);\n }\n}\n\nexport interface PaginatedResponse<T> {\n data: T[];\n pagination?: { next_cursor?: string | null; has_next?: boolean; page?: number };\n}\n\n/**\n * Auto-pagination async iterator following `next_cursor` (A9). Yields every\n * item across pages so callers can `for await (const x of paginate(...))`\n * without manual cursor bookkeeping.\n */\nexport async function* paginate<T>(\n fetchPage: (cursor?: string) => Promise<PaginatedResponse<T>>,\n): AsyncGenerator<T, void, unknown> {\n let cursor: string | undefined = undefined;\n for (;;) {\n const page = await fetchPage(cursor);\n for (const item of page.data ?? []) yield item;\n const next = page.pagination?.next_cursor;\n if (!next) break;\n cursor = next;\n }\n}\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface PersonEnrichment {\n found: boolean;\n confidence: number;\n data: Record<string, unknown>;\n}\n\nexport interface CompanyEnrichment {\n found: boolean;\n confidence: number;\n data: Record<string, unknown>;\n}\n\nexport interface EmailVerification {\n email: string;\n valid: boolean;\n deliverable: boolean;\n catch_all: boolean;\n}\n\nexport interface SearchFilter {\n field: string;\n operator: \"any_of\" | \"contains\" | \"all_of\" | \"none_of\" | \"is_empty\" | \"is_not_empty\" | \"between\" | \"exists\";\n value: unknown;\n}\n\nexport interface SearchResults {\n contacts: Record<string, unknown>[];\n total: number;\n page: number;\n}\n\n/**\n * Enrichment API - person/company lookup, email verification, prospecting search.\n *\n * Requires API key (server-side only). Credits charged per call. On the hardened\n * HTTP core: throws a typed `G8Error` on failure and retries transient errors.\n */\nexport const createEnrichClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n /** Look up a person by email, LinkedIn, or name + company. Costs 1 credit. */\n async person(params: {\n email?: string;\n linkedin_url?: string;\n first_name?: string;\n last_name?: string;\n company_domain?: string;\n }): Promise<PersonEnrichment> {\n const resp = await request<{ data?: PersonEnrichment }>(baseUrl, \"/api/v1/enrichment/lookup/person\", apiKey, {\n method: \"POST\",\n body: params,\n });\n return resp.data ?? (resp as unknown as PersonEnrichment);\n },\n\n /** Look up a company by domain or name. Costs 1 credit. */\n async company(params: { domain?: string; name?: string }): Promise<CompanyEnrichment> {\n const resp = await request<{ data?: CompanyEnrichment }>(baseUrl, \"/api/v1/enrichment/lookup/company\", apiKey, {\n method: \"POST\",\n body: params,\n });\n return resp.data ?? (resp as unknown as CompanyEnrichment);\n },\n\n /** Verify an email address. Costs 1 credit. */\n async verifyEmail(email: string): Promise<EmailVerification> {\n const resp = await request<{ data?: EmailVerification }>(baseUrl, \"/api/v1/enrichment/verify-email\", apiKey, {\n method: \"POST\",\n body: { email },\n });\n return resp.data ?? (resp as unknown as EmailVerification);\n },\n\n /** Search 700M+ contacts with filters. Credits charged per result. */\n async search(filters: SearchFilter[], page = 1, limit = 25): Promise<SearchResults> {\n const resp = await request<{ data?: SearchResults }>(baseUrl, \"/api/v1/search/contacts\", apiKey, {\n method: \"POST\",\n body: { filters, page, limit },\n });\n return resp.data ?? (resp as unknown as SearchResults);\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\n// -- Existing types (preserved for backwards compat) --\n\nexport interface Sequence {\n id: string;\n name: string;\n status: string;\n step_count: number | null;\n contact_count: number | null;\n}\n\nexport interface AddToSequenceConfig {\n sequenceId: string;\n contactIds: number[];\n listId: number;\n}\n\n// -- New (0.4.0) types --\n\nexport interface SequenceListItem {\n id: string;\n name: string | null;\n status: string | null;\n user_email: string | null;\n step_count: number | null;\n contact_count: number | null;\n sequence_kind: string | null;\n associated_list_id: number | null;\n created_at: string | null;\n updated_at: string | null;\n}\n\nexport interface SequenceListParams {\n page?: number;\n limit?: number;\n /** Filter by sequence status (e.g. \"live\", \"draft\", \"paused\"). */\n status?: string;\n /** Filter by sequence kind (\"cold_outbound\" or \"nurture\"). Omit to list all kinds. */\n sequence_kind?: SequenceKind;\n}\n\nexport interface SequenceDetail {\n id: string;\n name: string | null;\n description: string | null;\n status: string | null;\n user_email: string | null;\n associated_list_id: number | null;\n finish_on_reply: boolean;\n send_in_same_thread: boolean;\n wait_for_new_contacts: boolean;\n paused_at: string | null;\n resumed_at: string | null;\n /** Sequence kind. Immutable after creation. \"cold_outbound\" (default) or \"nurture\". */\n sequence_kind?: string | null;\n /** Pinned mailbox for nurture sequences; null for cold_outbound. Immutable. */\n pinned_mailbox_id?: number | null;\n created_at: string | null;\n updated_at: string | null;\n}\n\nexport interface SequenceContactItem {\n id: string | null;\n contact_id: number | null;\n state: string | null;\n current_step_order: number | null;\n created_at: string | null;\n updated_at: string | null;\n}\n\nexport interface SequenceContactsParams {\n page?: number;\n limit?: number;\n /** Filter by contact state (e.g. \"active\", \"completed\", \"replied\"). */\n state?: string;\n}\n\nexport interface SequenceActionResult {\n sequence_id: string;\n status: string;\n contacts_affected: number;\n}\n\n/**\n * Step type. Note: LinkedIn flows use \"HEYREACH\" — there is no \"LINKEDIN\" step type.\n * Server is case-insensitive on input but stores uppercase.\n */\nexport type SequenceStepType =\n | \"EMAIL\"\n | \"PHONE\"\n | \"SMS\"\n | \"WHATSAPP\"\n | \"HEYREACH\"\n | \"MANUAL_DIALER\";\n\nexport type SequenceStepInputType =\n | \"ON_DEMAND\"\n | \"MANUAL_TEMPLATE\"\n | \"AI_GENERATED_TEMPLATE\";\n\nexport interface SequenceStepConfig {\n step_order: number;\n step_type: SequenceStepType | string;\n /** Defaults to \"ON_DEMAND\". */\n input_type?: SequenceStepInputType | string;\n /** Wait time before this step in seconds. Defaults to 0. */\n time_interval?: number;\n /** Template body, AI prompt, or other step-type-specific config. */\n step_data?: Record<string, unknown>;\n}\n\nexport interface SequenceChannelConfig {\n channel_id: number;\n channel_value: string;\n channel_type: string;\n channel_data?: Record<string, unknown>;\n}\n\n/**\n * Sequence kind. A \"nurture\" is an ordinary sequence with `sequence_kind:\n * \"nurture\"` — same step types as cold outreach, but pinned to a single mailbox\n * with a higher daily email cap. There is no separate nurture client; create one\n * via `sequences.create({ sequence_kind: \"nurture\", pinned_mailbox_id })`.\n */\nexport type SequenceKind = \"cold_outbound\" | \"nurture\";\n\nexport interface SequenceCreateParams {\n name: string;\n user_email: string;\n description?: string;\n finish_on_reply?: boolean;\n send_in_same_thread?: boolean;\n wait_for_new_contacts?: boolean;\n associated_list_id?: number;\n steps?: SequenceStepConfig[];\n channels?: SequenceChannelConfig[];\n campaign_id?: string;\n /**\n * Sequence kind. Defaults to \"cold_outbound\" server-side. Immutable after\n * creation. \"nurture\" requires `pinned_mailbox_id` and is gated by the\n * ENABLE_NURTURE feature flag.\n */\n sequence_kind?: SequenceKind;\n /**\n * Mailbox to pin for a nurture sequence. Required when\n * `sequence_kind: \"nurture\"`; all email channels must use this mailbox.\n * Ignored for cold_outbound sequences.\n */\n pinned_mailbox_id?: number;\n}\n\nexport interface SequenceCreateResult {\n id: string;\n name: string;\n status: string;\n created_at: string | null;\n}\n\nexport interface SequenceUpdateParams {\n name?: string;\n description?: string;\n is_shared?: boolean;\n finish_on_reply?: boolean;\n send_in_same_thread?: boolean;\n wait_for_new_contacts?: boolean;\n schedule_id?: string;\n appointment_id?: number;\n}\n\nexport interface SequenceStepUpdateParams {\n step_data?: Record<string, unknown>;\n time_interval?: number;\n step_type?: SequenceStepType | string;\n input_type?: SequenceStepInputType | string;\n}\n\nexport interface SequencePreviewStep {\n id: string;\n step_order: number;\n step_type: string;\n input_type: string;\n time_interval: number | null;\n step_data: Record<string, unknown> | null;\n}\n\nexport interface SequencePreviewChannel {\n id: string;\n channel_id: number | null;\n channel_value: string | null;\n channel_type: string | null;\n}\n\nexport interface SequencePreview {\n id: string;\n name: string | null;\n status: string | null;\n description: string | null;\n steps: SequencePreviewStep[];\n channels: SequencePreviewChannel[];\n}\n\nexport interface SequenceAnalytics {\n overview: Record<string, unknown>;\n performance: Record<string, unknown>;\n engagement: Record<string, unknown>;\n timeline: Record<string, unknown>[];\n contact_distribution: Record<string, unknown>;\n step_breakdown: Record<string, unknown>[];\n step_creation_methods: Record<string, unknown>[];\n sender_distribution: Record<string, unknown>[];\n}\n\nexport interface PaginationMeta {\n page: number;\n limit: number;\n total: number;\n has_next: boolean;\n}\n\n/**\n * Sequences API — list, create, run, pause, update, analyze multi-channel sequences.\n * Requires API key (server-side).\n *\n * Backed by:\n * GET /api/v1/sequences\n * POST /api/v1/sequences\n * GET /api/v1/sequences/{sequence_id}\n * PATCH /api/v1/sequences/{sequence_id}\n * DELETE /api/v1/sequences/{sequence_id}\n * GET /api/v1/sequences/{sequence_id}/contacts\n * POST /api/v1/sequences/{sequence_id}/contacts\n * POST /api/v1/sequences/{sequence_id}/run\n * POST /api/v1/sequences/{sequence_id}/pause\n * POST /api/v1/sequences/{sequence_id}/resume\n * PATCH /api/v1/sequences/{sequence_id}/steps/{step_id}\n * GET /api/v1/sequences/{sequence_id}/preview\n * GET /api/v1/sequences/{sequence_id}/analytics\n */\nexport const createSequencesClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n // -- Overloads on list() preserve backwards compat with v0.3.0's positional signature --\n\n async function list(): Promise<SequenceListItem[]>;\n async function list(page: number, limit?: number): Promise<SequenceListItem[]>;\n async function list(params: SequenceListParams): Promise<SequenceListItem[]>;\n async function list(\n pageOrParams?: number | SequenceListParams,\n limit?: number,\n ): Promise<SequenceListItem[]> {\n let params: SequenceListParams;\n if (typeof pageOrParams === \"number\") {\n params = { page: pageOrParams, limit: limit ?? 50 };\n } else {\n params = pageOrParams ?? {};\n }\n const resp = await request<{ data?: SequenceListItem[] }>(baseUrl, \"/api/v1/sequences\", apiKey, {\n query: params as Record<string, unknown>,\n });\n return (resp.data ?? (resp as unknown as SequenceListItem[])) as SequenceListItem[];\n }\n\n return {\n /** List sequences with pagination + optional status filter. */\n list,\n\n /** Get full sequence details by ID. */\n async get(sequenceId: string): Promise<SequenceDetail> {\n const resp = await request<{ data?: SequenceDetail }>(baseUrl, `/api/v1/sequences/${sequenceId}`, apiKey);\n return resp.data ?? (resp as unknown as SequenceDetail);\n },\n\n /** List contacts enrolled in a sequence. Filter by state (e.g. \"active\", \"replied\"). */\n async contacts(\n sequenceId: string,\n params: SequenceContactsParams = {},\n ): Promise<{ data: SequenceContactItem[]; pagination?: PaginationMeta }> {\n return request(baseUrl, `/api/v1/sequences/${sequenceId}/contacts`, apiKey, {\n query: params as Record<string, unknown>,\n });\n },\n\n /**\n * Add contacts to a sequence (V2 queuing). Live or drafted sequences only.\n * Pass `idempotencyKey` to make a retry safe — the same key returns the\n * first result instead of re-enrolling on a 5xx-then-success (A6).\n */\n async add(config: AddToSequenceConfig, idempotencyKey?: string): Promise<SequenceActionResult> {\n const resp = await request<{ data?: SequenceActionResult }>(\n baseUrl,\n `/api/v1/sequences/${config.sequenceId}/contacts`,\n apiKey,\n { method: \"POST\", body: { contact_ids: config.contactIds, list_id: config.listId }, idempotencyKey },\n );\n return resp.data ?? (resp as unknown as SequenceActionResult);\n },\n\n /**\n * Create a new sequence with optional steps + channels. Pass `idempotencyKey`\n * to make a retry safe — the same key returns the first result instead of\n * creating a duplicate sequence on a 5xx-then-success (A6).\n */\n async create(payload: SequenceCreateParams, idempotencyKey?: string): Promise<SequenceCreateResult> {\n const resp = await request<{ data?: SequenceCreateResult }>(baseUrl, \"/api/v1/sequences\", apiKey, {\n method: \"POST\",\n body: payload,\n idempotencyKey,\n });\n return resp.data ?? (resp as unknown as SequenceCreateResult);\n },\n\n /** Update sequence metadata. Rejected (409) if sequence is in a transitional status. */\n async update(sequenceId: string, fields: SequenceUpdateParams): Promise<SequenceActionResult> {\n const resp = await request<{ data?: SequenceActionResult }>(baseUrl, `/api/v1/sequences/${sequenceId}`, apiKey, {\n method: \"PATCH\",\n body: fields,\n });\n return resp.data ?? (resp as unknown as SequenceActionResult);\n },\n\n /** Update a single step within a sequence. */\n async updateStep(\n sequenceId: string,\n stepId: string,\n fields: SequenceStepUpdateParams,\n ): Promise<SequenceActionResult> {\n const resp = await request<{ data?: SequenceActionResult }>(\n baseUrl,\n `/api/v1/sequences/${sequenceId}/steps/${stepId}`,\n apiKey,\n { method: \"PATCH\", body: fields },\n );\n return resp.data ?? (resp as unknown as SequenceActionResult);\n },\n\n /** Soft-delete (archive) a sequence. */\n async delete(sequenceId: string): Promise<SequenceActionResult> {\n const resp = await request<{ data?: SequenceActionResult }>(baseUrl, `/api/v1/sequences/${sequenceId}`, apiKey, {\n method: \"DELETE\",\n });\n return resp.data ?? (resp as unknown as SequenceActionResult);\n },\n\n /**\n * Run/start a DRAFTED sequence (V2 orchestration). Pass `idempotencyKey` to\n * make a retry safe — the same key won't re-trigger the run on a\n * 5xx-then-success (A6).\n */\n async run(sequenceId: string, idempotencyKey?: string): Promise<SequenceActionResult> {\n const resp = await request<{ data?: SequenceActionResult }>(\n baseUrl,\n `/api/v1/sequences/${sequenceId}/run`,\n apiKey,\n { method: \"POST\", idempotencyKey },\n );\n return resp.data ?? (resp as unknown as SequenceActionResult);\n },\n\n /**\n * Pause a live sequence. Pass `idempotencyKey` to make a retry safe — the\n * same key won't double-apply on a 5xx-then-success (A6).\n */\n async pause(sequenceId: string, idempotencyKey?: string): Promise<SequenceActionResult> {\n const resp = await request<{ data?: SequenceActionResult }>(\n baseUrl,\n `/api/v1/sequences/${sequenceId}/pause`,\n apiKey,\n { method: \"POST\", idempotencyKey },\n );\n return resp.data ?? (resp as unknown as SequenceActionResult);\n },\n\n /**\n * Resume a paused sequence. Pass `idempotencyKey` to make a retry safe — the\n * same key won't double-apply on a 5xx-then-success (A6).\n */\n async resume(sequenceId: string, idempotencyKey?: string): Promise<SequenceActionResult> {\n const resp = await request<{ data?: SequenceActionResult }>(\n baseUrl,\n `/api/v1/sequences/${sequenceId}/resume`,\n apiKey,\n { method: \"POST\", idempotencyKey },\n );\n return resp.data ?? (resp as unknown as SequenceActionResult);\n },\n\n /** Read-only sequence preview with all steps + channels (no enrollment). */\n async preview(sequenceId: string): Promise<SequencePreview> {\n const resp = await request<{ data?: SequencePreview }>(baseUrl, `/api/v1/sequences/${sequenceId}/preview`, apiKey);\n return resp.data ?? (resp as unknown as SequencePreview);\n },\n\n /** Comprehensive analytics for a sequence. */\n async analytics(sequenceId: string): Promise<SequenceAnalytics> {\n const resp = await request<{ data?: SequenceAnalytics }>(\n baseUrl,\n `/api/v1/sequences/${sequenceId}/analytics`,\n apiKey,\n );\n return resp.data ?? (resp as unknown as SequenceAnalytics);\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface Campaign {\n id: string;\n name: string;\n slug: string;\n status: string;\n category: string | null;\n goal: string | null;\n target_persona: string | null;\n}\n\nexport interface CampaignCreateConfig {\n name: string;\n category?: string;\n brief?: string;\n core_concept?: string;\n primary_hook?: string;\n target_persona?: string;\n goal?: string;\n}\n\nexport interface CampaignLaunchExecution {\n status: string;\n contacts_total?: number;\n contacts_scheduled?: number;\n contacts_queued?: number;\n job_id?: string;\n polling_url?: string;\n orchestrator_version?: string;\n}\n\nexport interface CampaignLaunchResult {\n status: string;\n campaign_id: string;\n sequence_id: string;\n sequence_name: string;\n steps_created: number;\n link_id: string;\n warnings: string[];\n channels_attached: Array<{\n mailbox_id: number;\n email: string;\n channel_type: string;\n auto_picked: boolean;\n }>;\n agent_assigned: {\n textual_agent_name: string | null;\n voice_agent_name: string | null;\n };\n schedule_id: string | null;\n appointment_id: number | null;\n execution: CampaignLaunchExecution;\n message: string;\n}\n\n/**\n * Campaigns - create, manage, launch campaigns. Requires API key (server-side).\n * On the hardened HTTP core: throws a typed `G8Error` on failure and retries\n * transient errors.\n */\nexport const createCampaignsClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n async list(page = 1, limit = 50): Promise<Campaign[]> {\n const resp = await request<{ data?: Campaign[] }>(baseUrl, \"/api/v1/campaigns\", apiKey, {\n query: { page, limit },\n });\n return resp.data ?? (resp as unknown as Campaign[]);\n },\n\n async get(campaignId: string): Promise<Campaign> {\n const resp = await request<{ data?: Campaign }>(baseUrl, `/api/v1/campaigns/${campaignId}`, apiKey);\n return resp.data ?? (resp as unknown as Campaign);\n },\n\n async create(config: CampaignCreateConfig): Promise<Campaign> {\n const resp = await request<{ data?: Campaign }>(baseUrl, \"/api/v1/campaigns\", apiKey, {\n method: \"POST\",\n body: config,\n });\n return resp.data ?? (resp as unknown as Campaign);\n },\n\n async launch(campaignId: string): Promise<CampaignLaunchResult> {\n const resp = await request<{ data?: CampaignLaunchResult }>(\n baseUrl,\n `/api/v1/campaigns/${campaignId}/launch`,\n apiKey,\n { method: \"POST\" },\n );\n return resp.data ?? (resp as unknown as CampaignLaunchResult);\n },\n };\n};\n","const DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface IntentSignals {\n domain: string;\n score: number;\n intent: \"low\" | \"medium\" | \"high\";\n signals: string[];\n last_seen: string | null;\n}\n\n/**\n * Intent signals - company-level buying signals. Write key for public, API key for full access.\n */\nexport const createSignalsClient = (key: string, isApiKey: boolean, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n const headers = (): Record<string, string> => isApiKey\n ? { \"Content-Type\": \"application/json\", \"Authorization\": `Bearer ${key}` }\n : { \"Content-Type\": \"application/json\", \"X-Write-Key\": key };\n\n // There is exactly ONE intent-signals route in the platform:\n // `/api/v1/public/signals/company` (developer_api/interfaces/v1/public_signals_router.py).\n //\n // This used to branch to `/api/v1/signals/company` for API-key callers. That\n // route has never existed at any prefix — the request 404'd, and the `!resp.ok`\n // guard below swallowed it into an all-zeros result, so a server-side caller\n // saw \"no intent\" rather than an error. Pointing both credential types at the\n // route that exists removes the phantom endpoint.\n //\n // KNOWN GAP (not fixed here, needs a product decision): the live route\n // authenticates with `validate_write_key`, which requires an `X-Write-Key`\n // header and rejects `Authorization: Bearer`. So an API-key-only caller still\n // cannot read signals — it now gets a 401 instead of a 404, both swallowed to\n // zeros. Closing that needs a server-side signals endpoint that accepts API\n // keys; until then, pass a `writeKey` to `g8.init()` to use this surface.\n const endpoint = \"/api/v1/public/signals/company\";\n\n return {\n /** Get intent signals for a specific company domain. */\n async company(domain: string): Promise<IntentSignals> {\n const resp = await fetch(`${baseUrl}${endpoint}?domain=${encodeURIComponent(domain)}`, { headers: headers() });\n if (!resp.ok) return { domain, score: 0, intent: \"low\", signals: [], last_seen: null };\n const data = await resp.json();\n return data.data || data;\n },\n\n /** Stream intent signals - calls callback every 30s with latest data. */\n stream(domains: string[], callback: (signals: IntentSignals[]) => void): () => void {\n const check = async () => {\n try {\n const results = await Promise.all(domains.map((d) => this.company(d)));\n callback(results);\n } catch { /* silent */ }\n };\n\n check();\n const interval = setInterval(check, 30_000);\n return () => clearInterval(interval);\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\ntype VoiceEvent = \"connected\" | \"transcription\" | \"ended\" | \"error\";\ntype VoiceCallback = (data: Record<string, unknown>) => void;\n\n// -- New (0.4.0) dialer types — backed by /api/v1/voice/dialer/* --\n\nexport interface VoicePagination {\n total_items: number;\n total_pages: number;\n current_page: number;\n page_size: number;\n}\n\nexport interface DialerSessionSummary {\n session_id: string;\n status: string | null;\n user_id: string | null;\n user_email: string | null;\n org_id: string | null;\n from_phone: string | null;\n campaign_id: string | null;\n campaign_builder_campaign_id: string | null;\n name: string | null;\n session_metadata: Record<string, unknown> | null;\n total_calls: number | null;\n created_at: string | null;\n updated_at: string | null;\n contact_ids: unknown[];\n}\n\nexport interface DialerSessionsListResult {\n sessions: DialerSessionSummary[];\n pagination: VoicePagination;\n}\n\nexport interface DialerSessionsListParams {\n page?: number;\n /** Default 50, max 200. */\n page_size?: number;\n /** Comma-separated status list (e.g. \"ACTIVE,PAUSED\"). */\n status?: string;\n user_email?: string;\n /** Substring search on session name. */\n name?: string;\n campaign_id?: string;\n list_id?: string;\n list_name?: string;\n /** ISO 8601 timestamp. */\n date_from?: string;\n /** ISO 8601 timestamp. */\n date_to?: string;\n sort_by?: string;\n /** Default \"desc\". */\n sort_order?: \"asc\" | \"desc\";\n}\n\nexport interface DialerStatsParams {\n session_id?: string;\n user_email?: string;\n /** YYYY-MM-DD. */\n date_from?: string;\n /** YYYY-MM-DD. */\n date_to?: string;\n /** Default \"DAILY\". */\n aggregation?: \"DAILY\" | \"TOTAL\";\n}\n\nexport interface DialerReportFilters {\n session_id: string | null;\n user_email: string | null;\n org_id: string | null;\n date_from: string | null;\n date_to: string | null;\n aggregation: string | null;\n}\n\nexport interface DialerReportMetric {\n date: string | null;\n sdr_name: string | null;\n org_id: string | null;\n list_title: string | null;\n total_dials: number;\n total_connections: number;\n connection_rate: number;\n total_voicemails: number;\n voicemail_rate: number;\n talk_time_minutes: number;\n avg_call_duration_minutes: number;\n dispositions: Record<string, unknown> | null;\n success_rate: number;\n unique_sessions: number;\n avg_calls_per_session: number;\n redial_count: number;\n redial_success_rate: number | null;\n peak_calling_hour: number | null;\n total_callbacks: number;\n}\n\nexport interface DialerStatsResult {\n filters: DialerReportFilters;\n metrics: DialerReportMetric[];\n summary: DialerReportMetric | null;\n}\n\nexport interface DialerNumberInfo {\n number: string;\n /** Default \"ai\". */\n inbound_type: string;\n /** Default \"twilio\". */\n telephony_provider: string;\n calls_today: number;\n calls_7d: number;\n connect_rate_7d: number;\n is_valid: boolean;\n daily_limit: number;\n assigned_to: string | null;\n created_at: string | null;\n}\n\nexport interface DialerNumbersListResult {\n numbers: DialerNumberInfo[];\n}\n\nexport interface MissedCallback {\n id: string | number | null;\n caller_phone: string | null;\n inbound_number: string | null;\n first_name: string | null;\n last_name: string | null;\n email: string | null;\n contact_id: string | number | null;\n parallel_session_id: string | null;\n created_at: string | null;\n call_duration: number | null;\n is_callback: boolean;\n}\n\nexport interface MissedCallbacksResult {\n missed_callbacks: MissedCallback[];\n count: number;\n}\n\nexport interface CallGradingResult {\n room_name: string;\n /** \"ready\" or \"pending\". */\n status: string;\n grading: Record<string, unknown> | null;\n reviewed: boolean;\n reviewed_at: string | null;\n}\n\nexport interface DialerAgentSummary {\n agent_id: string;\n agent_name: string | null;\n /** \"SDR\", etc. */\n role: string | null;\n agent_status: string | null;\n /** \"agent\" or \"twin\". */\n entity_type: string | null;\n description: string | null;\n phone: string | null;\n is_template: boolean;\n}\n\nexport interface DialerAgentsListResult {\n agents: DialerAgentSummary[];\n total_count: number;\n}\n\nexport interface DialerAgentsListParams {\n /** e.g. \"SDR\". */\n role?: string;\n agent_status?: string;\n /** \"agent\" or \"twin\". */\n entity_type?: \"agent\" | \"twin\";\n /** Substring filter on name/description. */\n search?: string;\n}\n\nexport interface DialerSessionCreateParams {\n /** 1-200 chars. */\n name: string;\n /** Org's dialer phone (E.164, 9-16 chars). */\n from_phone: string;\n /** Contact list ID (stored in session metadata). */\n list_id?: string;\n /** Display title for the source list. */\n list_title?: string;\n /** V2 agent UUID. */\n agent_id?: string;\n /** V1 fallback name. Defaults to \"default_agent\". */\n agent_name?: string;\n /** \"agent\" or \"twin\". */\n entity_type?: \"agent\" | \"twin\";\n /** Campaign Builder UUID. */\n studio_campaign_id?: string;\n /** IANA timezone (e.g. \"America/New_York\"). */\n user_timezone?: string;\n /** Per-session voicemail-skip override. null/omit = use SDR default. */\n skip_voicemails?: boolean;\n}\n\nexport interface DialerSessionCreateResult {\n session_id: string;\n status: string;\n name: string | null;\n org_id: string | null;\n user_id: string | null;\n user_email: string | null;\n from_phone: string | null;\n session_metadata: Record<string, unknown> | null;\n total_calls: number;\n created_at: string | null;\n message: string | null;\n}\n\n/** \"ACTIVE\" resumes, \"PAUSED\" pauses, \"COMPLETED\" stops. \"FAILED\" is rejected. */\nexport type DialerSessionStatus = \"ACTIVE\" | \"PAUSED\" | \"COMPLETED\";\n\nexport interface DialerSessionStatusUpdateResult {\n session_id: string;\n status: string;\n message: string | null;\n name: string | null;\n org_id: string | null;\n user_id: string | null;\n user_email: string | null;\n from_phone: string | null;\n session_metadata: Record<string, unknown> | null;\n total_calls: number;\n created_at: string | null;\n updated_at: string | null;\n}\n\nexport interface DialerSessionResumeResult extends DialerSessionStatusUpdateResult {\n /** Contacts actually placed in this batch (voice caps at 4). */\n dialed_count: number;\n}\n\n/**\n * Voice AI — start AI voice calls, get transcriptions and analysis.\n * Plus full parallel-dialer session control via the `dialer` namespace.\n * Requires API key (server-side).\n *\n * Backed by:\n * GET /api/v1/voice/dialer/sessions\n * POST /api/v1/voice/dialer/sessions\n * PATCH /api/v1/voice/dialer/sessions/{session_id}/status\n * POST /api/v1/voice/dialer/sessions/{session_id}/resume\n * GET /api/v1/voice/dialer/stats\n * GET /api/v1/voice/dialer/numbers\n * GET /api/v1/voice/dialer/missed-callbacks\n * GET /api/v1/voice/dialer/calls/{room_name}/grading\n * GET /api/v1/voice/dialer/agents\n */\nexport const createVoiceClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n const listeners: Map<VoiceEvent, VoiceCallback[]> = new Map();\n\n const dialer = {\n /** List parallel-dialer sessions with filters + pagination. */\n async listSessions(params: DialerSessionsListParams = {}): Promise<DialerSessionsListResult> {\n const resp = await request<{ data?: DialerSessionsListResult }>(\n baseUrl,\n \"/api/v1/voice/dialer/sessions\",\n apiKey,\n { query: params as Record<string, unknown> },\n );\n return resp.data ?? (resp as unknown as DialerSessionsListResult);\n },\n\n /** Create a parallel-dialer session in PAUSED state. SDR opens UI to start dialing. */\n async createSession(payload: DialerSessionCreateParams): Promise<DialerSessionCreateResult> {\n const resp = await request<{ data?: DialerSessionCreateResult }>(\n baseUrl,\n \"/api/v1/voice/dialer/sessions\",\n apiKey,\n { method: \"POST\", body: payload },\n );\n return resp.data ?? (resp as unknown as DialerSessionCreateResult);\n },\n\n /** Pause / resume / stop a dialer session via status flip. */\n async updateSessionStatus(\n sessionId: string,\n status: DialerSessionStatus,\n ): Promise<DialerSessionStatusUpdateResult> {\n const resp = await request<{ data?: DialerSessionStatusUpdateResult }>(\n baseUrl,\n `/api/v1/voice/dialer/sessions/${sessionId}/status`,\n apiKey,\n { method: \"PATCH\", body: { status } },\n );\n return resp.data ?? (resp as unknown as DialerSessionStatusUpdateResult);\n },\n\n /**\n * Resume a PAUSED dialer session. Auto-fetches the next batch from the source list,\n * filters already-called + phoneless rows, and forwards to voice's start-session.\n * @param maxContacts 1-4 (voice caps parallel dialing at 4). Default 4.\n */\n async resumeSession(sessionId: string, maxContacts = 4): Promise<DialerSessionResumeResult> {\n const resp = await request<{ data?: DialerSessionResumeResult }>(\n baseUrl,\n `/api/v1/voice/dialer/sessions/${sessionId}/resume`,\n apiKey,\n { method: \"POST\", body: { max_contacts: maxContacts } },\n );\n return resp.data ?? (resp as unknown as DialerSessionResumeResult);\n },\n\n /** Aggregated dialer analytics (daily breakdown or total). */\n async stats(params: DialerStatsParams = {}): Promise<DialerStatsResult> {\n const resp = await request<{ data?: DialerStatsResult }>(\n baseUrl,\n \"/api/v1/voice/dialer/stats\",\n apiKey,\n { query: params as Record<string, unknown> },\n );\n return resp.data ?? (resp as unknown as DialerStatsResult);\n },\n\n /** List dialer-eligible phone numbers with 7-day stats + daily limits. */\n async numbers(userEmail?: string): Promise<DialerNumbersListResult> {\n const resp = await request<{ data?: DialerNumbersListResult }>(\n baseUrl,\n \"/api/v1/voice/dialer/numbers\",\n apiKey,\n { query: userEmail ? { user_email: userEmail } : {} },\n );\n return resp.data ?? (resp as unknown as DialerNumbersListResult);\n },\n\n /** List missed inbound callbacks with caller / contact info. */\n async missedCallbacks(limit = 50): Promise<MissedCallbacksResult> {\n const resp = await request<{ data?: MissedCallbacksResult }>(\n baseUrl,\n \"/api/v1/voice/dialer/missed-callbacks\",\n apiKey,\n { query: { limit } },\n );\n return resp.data ?? (resp as unknown as MissedCallbacksResult);\n },\n\n /** AI grading for a single dialer call (returns \"pending\" while in progress). */\n async callGrading(roomName: string): Promise<CallGradingResult> {\n const resp = await request<{ data?: CallGradingResult }>(\n baseUrl,\n `/api/v1/voice/dialer/calls/${encodeURIComponent(roomName)}/grading`,\n apiKey,\n );\n return resp.data ?? (resp as unknown as CallGradingResult);\n },\n\n /** List voice agents available for dialer sessions (capped at 100; no pagination). */\n async agents(params: DialerAgentsListParams = {}): Promise<DialerAgentsListResult> {\n const resp = await request<{ data?: DialerAgentsListResult }>(\n baseUrl,\n \"/api/v1/voice/dialer/agents\",\n apiKey,\n { query: params as Record<string, unknown> },\n );\n return resp.data ?? (resp as unknown as DialerAgentsListResult);\n },\n\n /** Fetch the full transcript for a single dialer call. */\n async callTranscript(roomName: string): Promise<{ data: { status: string; transcript: Array<{ speaker: string; text: string; timestamp: number }> } }> {\n return request(\n baseUrl,\n `/api/v1/voice/dialer/calls/${encodeURIComponent(roomName)}/transcript`,\n apiKey,\n );\n },\n\n /** List dialer calls — pass `contact_id` or `user_email` to scope. */\n async listCalls(params: { contact_id?: number; user_email?: string; campaign_id?: string; date_from?: string; date_to?: string; limit?: number; page?: number } = {}): Promise<{ data: Array<Record<string, unknown>>; pagination?: VoicePagination }> {\n return request(\n baseUrl,\n \"/api/v1/voice/dialer/calls\",\n apiKey,\n { query: params as Record<string, unknown> },\n );\n },\n\n /** Convenience: list calls for a single contact. */\n async listCallsForContact(contactId: number, extra: { limit?: number } = {}): Promise<{ data: Array<Record<string, unknown>> }> {\n return request(\n baseUrl,\n \"/api/v1/voice/dialer/calls\",\n apiKey,\n { query: { contact_id: contactId, ...extra } },\n );\n },\n\n /** Convenience: list calls placed by a specific SDR. */\n async listCallsForSdr(userEmail: string, extra: { limit?: number; date_from?: string; date_to?: string } = {}): Promise<{ data: Array<Record<string, unknown>> }> {\n return request(\n baseUrl,\n \"/api/v1/voice/dialer/calls\",\n apiKey,\n { query: { user_email: userEmail, ...extra } },\n );\n },\n };\n\n return {\n /** Listen for voice events. */\n on(event: VoiceEvent, callback: VoiceCallback): void {\n if (!listeners.has(event)) listeners.set(event, []);\n listeners.get(event)!.push(callback);\n },\n\n /** Parallel-dialer session control + analytics. */\n dialer,\n };\n};\n","import { createHmac, timingSafeEqual } from \"node:crypto\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\n/**\n * Known graph8 webhook event types.\n *\n * Source of truth is the backend ``WEBHOOK_EVENTS`` catalog\n * (campaign_builder/services/webhook_service.py). Keep in sync when the backend\n * adds events. ``WebhookEvent`` also accepts any string so a newly-added\n * backend event never breaks a client that hasn't upgraded.\n */\nexport const KNOWN_WEBHOOK_EVENTS = [\n \"campaign.created\",\n \"campaign.updated\",\n \"campaign.deleted\",\n \"campaign.launched\",\n \"campaign.paused\",\n \"campaign.completed\",\n \"campaign.status_changed\",\n \"campaign.content_ready\",\n \"document.generated\",\n \"document.failed\",\n \"intelligence.completed\",\n \"intelligence.failed\",\n \"company.enriched\",\n \"company_intelligence.completed\",\n \"audience.ready\",\n \"audience.failed\",\n \"sequence.draft_created\",\n \"sequence.started\",\n \"sequence.paused\",\n \"sequence.completed\",\n \"engagement.email_sent\",\n \"engagement.email_replied\",\n \"engagement.email_bounced\",\n \"engagement.email_skipped\",\n \"engagement.call_dispatched\",\n \"engagement.sms_sent\",\n \"engagement.sms_replied\",\n \"engagement.whatsapp_sent\",\n \"engagement.linkedin_connection_sent\",\n \"engagement.linkedin_message_sent\",\n \"engagement.linkedin_inmail_sent\",\n \"engagement.linkedin_reply_received\",\n \"engagement.linkedin_connection_accepted\",\n \"meeting.booked\",\n \"meeting.cancelled\",\n \"meeting.rescheduled\",\n] as const;\n\n// Union of known events for autocomplete, plus `string` so unknown/new backend\n// events still type-check (Stripe-style forward compatibility).\nexport type WebhookEvent = (typeof KNOWN_WEBHOOK_EVENTS)[number] | (string & {});\n\n/** The decoded body graph8 delivers to a webhook endpoint. */\nexport interface WebhookEventPayload {\n event: WebhookEvent;\n timestamp: string;\n data: Record<string, unknown>;\n org_id: string;\n /** Stable per-delivery id (present once the backend adds it; for consumer dedup). */\n id?: string;\n}\n\nexport interface ConstructEventOptions {\n /**\n * Reject events whose ``X-Studio-Timestamp`` is older (or newer) than this\n * many seconds — replay protection. Disabled when 0/undefined.\n */\n toleranceSeconds?: number;\n}\n\n/** Thrown by {@link constructEvent} when a webhook cannot be verified. */\nexport class WebhookSignatureError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"WebhookSignatureError\";\n }\n}\n\nfunction timingSafeEqualHex(a: string, b: string): boolean {\n // Length check first: timingSafeEqual throws on length mismatch.\n if (a.length !== b.length) return false;\n try {\n const enc = new TextEncoder();\n return timingSafeEqual(enc.encode(a), enc.encode(b));\n } catch {\n return false;\n }\n}\n\n/**\n * Verify the HMAC-SHA256 signature of an incoming graph8 webhook and return the\n * parsed event. Server-side only (needs the per-endpoint signing secret).\n *\n * Mirrors the backend signing scheme exactly: the signature is\n * ``HMAC_SHA256(secret, `${timestamp}.${rawBody}`)`` hex-encoded, delivered in\n * the ``X-Studio-Signature`` header alongside the unix ``X-Studio-Timestamp``.\n *\n * @param payload The RAW request body string (verify before JSON.parse — re-serializing changes bytes).\n * @param signature The ``X-Studio-Signature`` header (an optional ``sha256=`` prefix is tolerated).\n * @param timestamp The ``X-Studio-Timestamp`` header (unix seconds).\n * @param secret The endpoint's signing secret.\n * @throws {WebhookSignatureError} on a missing/invalid signature, stale timestamp, or bad JSON.\n *\n * @example\n * app.post(\"/webhooks/graph8\", (req, res) => {\n * const event = g8.webhooks.constructEvent(\n * req.rawBody,\n * req.header(\"X-Studio-Signature\"),\n * req.header(\"X-Studio-Timestamp\"),\n * process.env.G8_WEBHOOK_SECRET,\n * { toleranceSeconds: 300 },\n * );\n * if (event.event === \"meeting.booked\") { ... }\n * res.sendStatus(200);\n * });\n */\nexport function constructEvent(\n payload: string,\n signature: string,\n timestamp: string | number,\n secret: string,\n opts: ConstructEventOptions = {},\n): WebhookEventPayload {\n if (!signature) throw new WebhookSignatureError(\"Missing X-Studio-Signature header\");\n if (timestamp === undefined || timestamp === null || timestamp === \"\") {\n throw new WebhookSignatureError(\"Missing X-Studio-Timestamp header\");\n }\n if (!secret) throw new WebhookSignatureError(\"Missing signing secret\");\n\n const ts = typeof timestamp === \"number\" ? timestamp : parseInt(timestamp, 10);\n if (!Number.isFinite(ts)) throw new WebhookSignatureError(\"Invalid X-Studio-Timestamp header\");\n\n const tolerance = opts.toleranceSeconds ?? 0;\n if (tolerance > 0) {\n const ageSeconds = Math.floor(Date.now() / 1000) - ts;\n if (Math.abs(ageSeconds) > tolerance) {\n throw new WebhookSignatureError(`Timestamp outside tolerance (${ageSeconds}s > ${tolerance}s)`);\n }\n }\n\n const expected = createHmac(\"sha256\", secret).update(`${ts}.${payload}`).digest(\"hex\");\n const provided = signature.startsWith(\"sha256=\") ? signature.slice(7) : signature;\n if (!timingSafeEqualHex(expected, provided)) {\n throw new WebhookSignatureError(\"Signature verification failed\");\n }\n\n try {\n return JSON.parse(payload) as WebhookEventPayload;\n } catch {\n throw new WebhookSignatureError(\"Invalid JSON payload\");\n }\n}\n\n/**\n * Webhooks client (server-side). graph8 webhooks are PUSH — graph8 POSTs to your\n * endpoint; you verify each delivery with {@link constructEvent}. (The previous\n * polling implementation hit a feed endpoint that never existed and is removed.)\n */\nexport const createWebhooksClient = (_apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n return {\n /** Base URL the webhook subscription API lives under. */\n baseUrl,\n /** Known event types (for autocomplete / validation). */\n knownEvents: KNOWN_WEBHOOK_EVENTS,\n /** Verify an incoming webhook's HMAC signature and return the parsed event. */\n constructEvent(\n payload: string,\n signature: string,\n timestamp: string | number,\n secret: string,\n opts?: ConstructEventOptions,\n ): WebhookEventPayload {\n return constructEvent(payload, signature, timestamp, secret, opts);\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface Contact {\n id: number;\n first_name: string | null;\n last_name: string | null;\n work_email: string | null;\n job_title: string | null;\n seniority_level: string | null;\n company_name: string | null;\n company_domain: string | null;\n linkedin_url: string | null;\n direct_phone: string | null;\n mobile_phone: string | null;\n city: string | null;\n state: string | null;\n country: string | null;\n}\n\nexport interface ContactListParams {\n page?: number;\n limit?: number;\n email?: string;\n name?: string;\n job_title?: string;\n seniority_level?: string;\n company_name?: string;\n country?: string;\n list_id?: number;\n}\n\nexport interface ContactCreateParams {\n work_email: string;\n first_name?: string;\n last_name?: string;\n job_title?: string;\n company_domain?: string;\n linkedin_url?: string;\n direct_phone?: string;\n mobile_phone?: string;\n city?: string;\n state?: string;\n country?: string;\n list_id?: number;\n}\n\nexport interface ContactUpdateParams {\n first_name?: string;\n last_name?: string;\n work_email?: string;\n job_title?: string;\n company_domain?: string;\n linkedin_url?: string;\n direct_phone?: string;\n mobile_phone?: string;\n city?: string;\n state?: string;\n country?: string;\n}\n\n/** A column definition on contacts (base or custom). */\nexport interface ContactColumn {\n id: number | null;\n title: string;\n data_type: string;\n name: string | null;\n /** Stringified list_id when list-scoped, otherwise null/empty for global. */\n object_id: string | null;\n is_global: boolean;\n}\n\nexport interface ContactColumnCreateParams {\n title: string;\n /** Email of the user creating the column. Required by the backend (audit). */\n created_by: string;\n /** \"text\" (default) — additional types may be added later. */\n data_type?: string;\n /** If set, the column is list-specific. Omit for org-wide global column. */\n list_id?: number;\n}\n\n/**\n * Contacts API - full CRUD for CRM contacts. Requires API key (server-side).\n */\nexport const createContactsClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n /** List contacts with optional filters. */\n async list(params: ContactListParams = {}): Promise<{ data: Contact[]; total: number }> {\n return request(baseUrl, \"/api/v1/contacts\", apiKey, {\n query: params as Record<string, unknown>,\n });\n },\n\n /** Get a single contact by ID. */\n async get(contactId: number): Promise<Contact> {\n const resp = await request<{ data?: Contact }>(baseUrl, `/api/v1/contacts/${contactId}`, apiKey);\n return resp.data ?? (resp as unknown as Contact);\n },\n\n /**\n * Create a new contact. Pass `idempotencyKey` to make a retry safe — the\n * same key returns the first result instead of creating a duplicate (A6).\n */\n async create(contact: ContactCreateParams, idempotencyKey?: string): Promise<Contact> {\n const resp = await request<{ data?: Contact }>(baseUrl, \"/api/v1/contacts\", apiKey, {\n method: \"POST\",\n body: contact,\n idempotencyKey,\n });\n return resp.data ?? (resp as unknown as Contact);\n },\n\n /** Update a contact (partial). */\n async update(contactId: number, fields: ContactUpdateParams): Promise<{ updated: number }> {\n return request(baseUrl, `/api/v1/contacts/${contactId}`, apiKey, {\n method: \"PATCH\",\n body: fields,\n });\n },\n\n /** Delete a contact (soft-delete). */\n async delete(contactId: number): Promise<{ deleted: boolean }> {\n return request(baseUrl, `/api/v1/contacts/${contactId}`, apiKey, { method: \"DELETE\" });\n },\n\n /** List custom contact columns. Pass listId to include list-specific columns. */\n async listColumns(listId?: number): Promise<{ data: ContactColumn[] }> {\n return request(baseUrl, \"/api/v1/contacts/columns\", apiKey, {\n query: listId != null ? { list_id: listId } : undefined,\n });\n },\n\n /** Create a custom contact column. Global if list_id is omitted, list-scoped otherwise. */\n async createColumn(params: ContactColumnCreateParams): Promise<ContactColumn> {\n const resp = await request<{ data?: ContactColumn }>(baseUrl, \"/api/v1/contacts/columns/create\", apiKey, {\n method: \"POST\",\n body: {\n title: params.title,\n data_type: params.data_type || \"text\",\n list_id: params.list_id ?? null,\n created_by: params.created_by,\n },\n });\n return resp.data ?? (resp as unknown as ContactColumn);\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface Company {\n id: number;\n name: string | null;\n domain: string | null;\n industry: string | null;\n employee_count: number | null;\n revenue: number | null;\n founded_year: number | null;\n description: string | null;\n linkedin_url: string | null;\n country: string | null;\n state: string | null;\n city: string | null;\n}\n\nexport interface CompanyListParams {\n page?: number;\n limit?: number;\n domain?: string;\n industry?: string;\n name?: string;\n country?: string;\n}\n\nexport interface CompanyUpdateParams {\n name?: string;\n domain?: string;\n industry?: string;\n description?: string;\n linkedin_url?: string;\n country?: string;\n state?: string;\n city?: string;\n}\n\nexport interface CompanyContact {\n id: number;\n first_name: string | null;\n last_name: string | null;\n work_email: string | null;\n job_title: string | null;\n}\n\n/** A column definition on companies (base or custom). */\nexport interface CompanyColumn {\n id: number | null;\n title: string;\n data_type: string;\n name: string | null;\n /** Stringified list_id when list-scoped, otherwise null/empty for global. */\n object_id: string | null;\n is_global: boolean;\n}\n\nexport interface CompanyColumnCreateParams {\n title: string;\n /** Email of the user creating the column. Required by the backend (audit). */\n created_by: string;\n /** \"text\" (default) — additional types may be added later. */\n data_type?: string;\n /** If set, the column is list-specific. Omit for org-wide global column. */\n list_id?: number;\n}\n\n/**\n * Companies API - search and manage CRM companies. Requires API key (server-side).\n */\nexport const createCompaniesClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n /** List companies with optional filters. */\n async list(params: CompanyListParams = {}): Promise<{ data: Company[]; total: number }> {\n return request(baseUrl, \"/api/v1/companies\", apiKey, { query: params as Record<string, unknown> });\n },\n\n /** Get a single company by ID. */\n async get(companyId: number): Promise<Company> {\n const resp = await request<{ data?: Company }>(baseUrl, `/api/v1/companies/${companyId}`, apiKey);\n return resp.data ?? (resp as unknown as Company);\n },\n\n /** Get contacts belonging to a company. */\n async contacts(companyId: number, limit = 50, offset = 0): Promise<{ data: CompanyContact[]; total: number }> {\n return request(baseUrl, `/api/v1/companies/${companyId}/contacts`, apiKey, { query: { limit, offset } });\n },\n\n /** Update a company (partial). */\n async update(companyId: number, fields: CompanyUpdateParams): Promise<{ updated: number }> {\n return request(baseUrl, `/api/v1/companies/${companyId}`, apiKey, { method: \"PATCH\", body: fields });\n },\n\n /** Delete a company (soft-delete). */\n async delete(companyId: number): Promise<{ deleted: boolean }> {\n return request(baseUrl, `/api/v1/companies/${companyId}`, apiKey, { method: \"DELETE\" });\n },\n\n /** List custom company columns. Pass listId to include list-specific columns. */\n async listColumns(listId?: number): Promise<{ data: CompanyColumn[] }> {\n return request(baseUrl, \"/api/v1/companies/columns\", apiKey, {\n query: listId != null ? { list_id: listId } : undefined,\n });\n },\n\n /** Create a custom company column. Global if list_id is omitted, list-scoped otherwise. */\n async createColumn(params: CompanyColumnCreateParams): Promise<CompanyColumn> {\n const resp = await request<{ data?: CompanyColumn }>(baseUrl, \"/api/v1/companies/columns/create\", apiKey, {\n method: \"POST\",\n body: {\n title: params.title,\n data_type: params.data_type || \"text\",\n list_id: params.list_id ?? null,\n created_by: params.created_by,\n },\n });\n return resp.data ?? (resp as unknown as CompanyColumn);\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface ContactList {\n id: number;\n title: string;\n type: \"contacts\" | \"companies\";\n contact_count: number | null;\n created_at: string | null;\n}\n\nexport interface ListContact {\n id: number;\n first_name: string | null;\n last_name: string | null;\n work_email: string | null;\n job_title: string | null;\n company_name: string | null;\n}\n\n/**\n * Lists API - manage contact and company lists. Requires API key (server-side).\n */\nexport const createListsClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n /** List all lists. */\n async list(page = 1, limit = 50): Promise<{ data: ContactList[]; total: number }> {\n return request(baseUrl, \"/api/v1/lists\", apiKey, { query: { page, limit } });\n },\n\n /** Create a new list. */\n async create(title: string, type: \"contacts\" | \"companies\" = \"contacts\"): Promise<ContactList> {\n const resp = await request<{ data?: ContactList }>(baseUrl, \"/api/v1/lists\", apiKey, {\n method: \"POST\",\n body: { title, type },\n });\n return resp.data ?? (resp as unknown as ContactList);\n },\n\n /** Delete a list (soft-delete). */\n async delete(listId: number): Promise<{ deleted: boolean }> {\n return request(baseUrl, `/api/v1/lists/${listId}`, apiKey, { method: \"DELETE\" });\n },\n\n /** Get contacts in a list. */\n async contacts(listId: number, page = 1, limit = 50): Promise<{ data: ListContact[]; total: number }> {\n return request(baseUrl, `/api/v1/lists/${listId}/contacts`, apiKey, { query: { page, limit } });\n },\n\n /** Add contacts to a list. */\n async addContacts(listId: number, contactIds: number[]): Promise<{ added: number }> {\n return request(baseUrl, `/api/v1/lists/${listId}/contacts`, apiKey, {\n method: \"POST\",\n body: { contact_ids: contactIds },\n });\n },\n\n /** Remove contacts from a list. */\n async removeContacts(listId: number, contactIds: number[]): Promise<{ removed: number }> {\n return request(baseUrl, `/api/v1/lists/${listId}/contacts`, apiKey, {\n method: \"DELETE\",\n body: { contact_ids: contactIds },\n });\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface Note {\n id: string;\n entity_type: string;\n entity_id: string;\n content: string;\n created_by: string | null;\n created_by_name: string | null;\n created_at: string | null;\n updated_at: string | null;\n}\n\n/**\n * Notes API - CRUD for notes attached to contacts. Requires API key (server-side).\n *\n * Backed by:\n * GET /api/v1/contacts/{contact_id}/notes\n * POST /api/v1/contacts/{contact_id}/notes\n * PATCH /api/v1/notes/{note_id}\n * DELETE /api/v1/notes/{note_id}\n */\nexport const createNotesClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n /** List all notes on a contact. */\n async list(contactId: number): Promise<{ data: Note[] }> {\n return request(baseUrl, `/api/v1/contacts/${contactId}/notes`, apiKey);\n },\n\n /** Create a note on a contact. */\n async create(contactId: number, content: string): Promise<Note> {\n const resp = await request<{ data?: Note }>(baseUrl, `/api/v1/contacts/${contactId}/notes`, apiKey, {\n method: \"POST\",\n body: { content },\n });\n return resp.data ?? (resp as unknown as Note);\n },\n\n /** Update a note's content. */\n async update(noteId: string, content: string): Promise<Note> {\n const resp = await request<{ data?: Note }>(baseUrl, `/api/v1/notes/${noteId}`, apiKey, {\n method: \"PATCH\",\n body: { content },\n });\n return resp.data ?? (resp as unknown as Note);\n },\n\n /** Delete a note. */\n async delete(noteId: string): Promise<{ data: { deleted: boolean } }> {\n return request(baseUrl, `/api/v1/notes/${noteId}`, apiKey, { method: \"DELETE\" });\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface Task {\n id: string;\n entity_type: string | null;\n entity_id: string | null;\n title: string;\n description: string | null;\n due_date: string | null;\n assignee_id: string | null;\n assignee_name: string | null;\n /** \"open\" | \"completed\" (other values may appear if the backend adds states later) */\n status: string | null;\n /** 0=none, 1=urgent, 2=high, 3=normal, 4=low */\n priority: number | null;\n task_type: string | null;\n created_by: string | null;\n created_at: string | null;\n updated_at: string | null;\n /** Parent task UUID when this row is a subtask, else null. One level deep. */\n parent_task_id?: string | null;\n /** Sort position within the parent's subtasks. 0 when not a subtask. */\n subtask_sort_order?: number | null;\n /** Number of subtasks under this task (top-level rows only). */\n subtask_count?: number | null;\n /** Subset of subtask_count whose status is \"completed\". */\n completed_subtask_count?: number | null;\n}\n\nexport interface TaskCreateParams {\n title: string;\n description?: string;\n /** ISO 8601 date string. */\n due_date?: string;\n assignee_id?: string;\n /** 0=none, 1=urgent, 2=high, 3=normal, 4=low */\n priority?: number;\n /**\n * When set, the new task is created as a subtask of this parent. Parent\n * must exist and must not itself be a subtask (subtasks are one level\n * deep).\n */\n parent_task_id?: string;\n /**\n * Sort position within the parent's subtasks. Omit to append at the end.\n */\n subtask_sort_order?: number;\n}\n\nexport interface TaskUpdateParams {\n title?: string;\n description?: string;\n due_date?: string;\n assignee_id?: string;\n /** \"open\" | \"completed\" */\n status?: string;\n priority?: number;\n /**\n * Re-parent this task into a subtask of the given parent. Parent must\n * exist, must not itself be a subtask, and a task that already has\n * subtasks of its own cannot be demoted into a subtask.\n */\n parent_task_id?: string;\n /** Reorder within the parent's subtasks. Can be sent alone. */\n subtask_sort_order?: number;\n}\n\nexport interface TaskListParams {\n /** \"open\" | \"completed\" */\n status?: string;\n /** 0-4 */\n priority?: number;\n assignee_id?: string;\n /** Substring search on title. */\n search?: string;\n}\n\n/**\n * Tasks API - CRUD for tasks attached to contacts. Requires API key (server-side).\n *\n * Backed by:\n * GET /api/v1/contacts/{contact_id}/tasks\n * GET /api/v1/tasks\n * POST /api/v1/contacts/{contact_id}/tasks\n * PATCH /api/v1/tasks/{task_id}\n * DELETE /api/v1/tasks/{task_id}\n */\nexport const createTasksClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n /** List tasks on a single contact. Optional status filter (\"open\" | \"completed\"). */\n async listForContact(contactId: number, status?: string): Promise<{ data: Task[] }> {\n return request(baseUrl, `/api/v1/contacts/${contactId}/tasks`, apiKey, {\n query: status ? { status } : undefined,\n });\n },\n\n /** List all tasks org-wide with optional filters. */\n async list(params: TaskListParams = {}): Promise<{ data: Task[] }> {\n return request(baseUrl, \"/api/v1/tasks\", apiKey, { query: params as Record<string, unknown> });\n },\n\n /** Create a task on a contact. */\n async create(contactId: number, task: TaskCreateParams): Promise<Task> {\n const resp = await request<{ data?: Task }>(baseUrl, `/api/v1/contacts/${contactId}/tasks`, apiKey, {\n method: \"POST\",\n body: task,\n });\n return resp.data ?? (resp as unknown as Task);\n },\n\n /** Update a task (partial). */\n async update(taskId: string, fields: TaskUpdateParams): Promise<Task> {\n const resp = await request<{ data?: Task }>(baseUrl, `/api/v1/tasks/${taskId}`, apiKey, {\n method: \"PATCH\",\n body: fields,\n });\n return resp.data ?? (resp as unknown as Task);\n },\n\n /** Delete a task. */\n async delete(taskId: string): Promise<{ data: { deleted: boolean } }> {\n return request(baseUrl, `/api/v1/tasks/${taskId}`, apiKey, { method: \"DELETE\" });\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\n/** A field (column) definition on contacts or companies — base or custom. */\nexport interface Field {\n id: number | null;\n title: string;\n /** Internal column name (slug). */\n name: string | null;\n /** Currently only \"text\" is supported; other types may be added later. */\n data_type: string;\n /** True if available org-wide, false if list-specific. */\n is_global: boolean;\n}\n\n/** Created custom field (column). */\nexport interface CreatedField {\n id: number;\n title: string;\n data_type: string;\n name: string;\n list_id: number | null;\n is_global: boolean;\n}\n\nexport interface FieldCreateParams {\n title: string;\n /** \"contacts\" or \"companies\". Defaults to \"contacts\" on the backend. */\n entity?: \"contacts\" | \"companies\";\n /** \"text\" (default) — additional types may be added later. */\n data_type?: string;\n /** If set, the field is list-specific. Omit for org-wide global field. */\n list_id?: number;\n}\n\nexport interface FieldDeleteParams {\n entity?: \"contacts\" | \"companies\";\n /**\n * Optional list scope guard. If provided, the column must belong to this\n * list or the request returns 404. Use this to prevent accidentally\n * deleting a column on a different list in the same org.\n */\n list_id?: number;\n}\n\nexport interface SetFieldValueParams {\n /** Contact ID (entity='contacts') or company ID (entity='companies'). */\n record_id: number;\n /** Value to write. Pass null/undefined to clear the field. */\n value?: string | null;\n entity?: \"contacts\" | \"companies\";\n}\n\n/**\n * Fields API - manage custom fields (columns) on contacts and companies.\n * Requires API key (server-side). On the hardened HTTP core: throws a typed\n * `G8Error` on failure and retries transient errors.\n *\n * Backed by:\n * GET /api/v1/fields — list contact fields\n * GET /api/v1/fields/companies — list company fields\n * POST /api/v1/fields — create a custom field\n * DELETE /api/v1/fields/{column_id} — delete a custom field\n * PATCH /api/v1/fields/{column_id}/values — set value on a single record\n */\nexport const createFieldsClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n /** List contact fields (base + custom). Pass listId to include list-specific custom fields. */\n async listContactFields(listId?: number): Promise<{ data: Field[] }> {\n return request(baseUrl, \"/api/v1/fields\", apiKey, { query: listId != null ? { list_id: listId } : undefined });\n },\n\n /** List company fields (base + custom). Pass listId to include list-specific custom fields. */\n async listCompanyFields(listId?: number): Promise<{ data: Field[] }> {\n return request(baseUrl, \"/api/v1/fields/companies\", apiKey, {\n query: listId != null ? { list_id: listId } : undefined,\n });\n },\n\n /** Create a custom field on contacts (default) or companies. */\n async create(params: FieldCreateParams): Promise<CreatedField> {\n const resp = await request<{ data?: CreatedField }>(baseUrl, \"/api/v1/fields\", apiKey, {\n method: \"POST\",\n body: {\n title: params.title,\n data_type: params.data_type || \"text\",\n list_id: params.list_id ?? null,\n entity: params.entity || \"contacts\",\n },\n });\n return resp.data ?? (resp as unknown as CreatedField);\n },\n\n /** Delete a custom field (soft-delete). Pass list_id to scope-guard against cross-list deletion. */\n async delete(\n columnId: number,\n params: FieldDeleteParams = {},\n ): Promise<{ data: { column_id: number; deleted: boolean } }> {\n const query: Record<string, unknown> = { entity: params.entity || \"contacts\" };\n if (params.list_id != null) query.list_id = params.list_id;\n return request(baseUrl, `/api/v1/fields/${columnId}`, apiKey, { method: \"DELETE\", query });\n },\n\n /** Set a custom field value on a single contact or company row. Pass value=null to clear. */\n async setValue(\n columnId: number,\n params: SetFieldValueParams,\n ): Promise<{ data: { column_id: number; record_id: number; updated: boolean } }> {\n return request(baseUrl, `/api/v1/fields/${columnId}/values`, apiKey, {\n method: \"PATCH\",\n body: { record_id: params.record_id, value: params.value ?? null, entity: params.entity || \"contacts\" },\n });\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface Deal {\n id: string | null;\n name: string | null;\n description: string | null;\n amount: number | null;\n currency: string | null;\n stage_id: string | null;\n stage_name: string | null;\n pipeline_id: string | null;\n /** Linked company ID (mashup_company_id), if any. */\n company_id: number | string | null;\n owner_id: string | null;\n /** ISO 8601 date. */\n close_date: string | null;\n created_at: string | null;\n updated_at: string | null;\n}\n\nexport interface DealCreateParams {\n name: string;\n /**\n * Deal owner (user id or email). Required.\n */\n owner_id: string;\n /**\n * mashup_contact_ids to attach. Required, at least one. Every contact must\n * belong to the same company, and that company becomes the deal's company.\n * Same id space as the contacts API. Attach is idempotent.\n */\n contact_ids: number[];\n /**\n * Deprecated: the company is derived from `contact_ids`. If supplied it must\n * match the contacts' company, otherwise omit it.\n */\n company_id?: number;\n description?: string;\n /**\n * Monetary value. Required (HTTP 400, marker `amount_required_for_won_stage`)\n * when creating directly in a Closed Won stage; 0 is a valid amount.\n */\n amount?: number;\n /** Default: \"USD\". */\n currency?: string;\n /**\n * Stage ID from the org's pipelines. Must exist and belong to `pipeline_id`\n * when both are sent (HTTP 422 otherwise). Defaults to the default\n * pipeline's first non-closed stage if omitted. Creating directly in a\n * closed stage triggers the closed-stage rules (HTTP 400 with marker\n * `close_date_required_for_won_stage` / `close_date_required_for_lost_stage`\n * / `amount_required_for_won_stage` / `owner_required_for_closed_stage`).\n */\n stage_id?: string;\n /** Pipeline ID. Must exist (HTTP 422 otherwise). Defaults to org's default pipeline if omitted. */\n pipeline_id?: string;\n /**\n * ISO 8601 close date. Required when creating directly in a Closed Won or\n * Closed Lost stage; past dates are allowed (backdating is supported).\n */\n close_date?: string;\n /**\n * Set true to create a deal even if one already exists for the company.\n * By default the API rejects a duplicate with HTTP 409.\n */\n allow_duplicate?: boolean;\n}\n\nexport interface DealUpdateParams {\n name?: string;\n description?: string;\n amount?: number;\n currency?: string;\n /**\n * New stage ID. Must be an existing stage in the deal's pipeline (HTTP 422\n * otherwise). Moving a deal INTO a Closed Won stage requires an effective\n * close_date AND amount; Closed Lost requires an effective close_date;\n * either requires an owner (\"effective\" = the value in this call, else the\n * deal's existing value). Violations return HTTP 400 with a marker-prefixed\n * detail: `close_date_required_for_won_stage`,\n * `close_date_required_for_lost_stage`, `amount_required_for_won_stage`,\n * `owner_required_for_closed_stage`. Re-submitting the deal's current stage\n * is never gated.\n */\n stage_id?: string;\n /**\n * ISO 8601 close date. Explicit null clears it, except on a deal sitting in\n * a closed stage (HTTP 400, closed-stage markers above).\n */\n close_date?: string | null;\n /**\n * Reassign the deal owner (user id or email). Pass null to clear, except on\n * a deal sitting in a closed stage (HTTP 400, marker\n * `owner_required_for_closed_stage`).\n */\n owner_id?: string | null;\n /**\n * mashup_contact_ids to link. Must belong to the deal's company (if the deal\n * has none yet, the added contacts' company is adopted); a cross-company add\n * is rejected. Idempotent.\n */\n add_contact_ids?: number[];\n /** mashup_contact_ids to unlink. Idempotent. */\n remove_contact_ids?: number[];\n}\n\nexport interface DealListParams {\n page?: number;\n limit?: number;\n stage_id?: string;\n pipeline_id?: string;\n /** Substring search on deal name. */\n search?: string;\n}\n\nexport interface PipelineStage {\n id: string;\n name: string;\n probability: number | null;\n position: number | null;\n stage_type: string | null;\n color: string | null;\n required_elements: string[];\n recommended_elements: string[];\n channel_scripts: Record<string, string>;\n}\n\nexport interface Pipeline {\n id: string;\n name: string;\n is_default: boolean;\n stages: PipelineStage[];\n}\n\n/** Trimmed deal shape returned by /contacts/{id}/deals and /companies/{id}/deals. */\nexport interface ContactDeal {\n deal_id: string | null;\n name: string | null;\n stage: string | null;\n value: number | null;\n currency: string | null;\n role: string | null;\n pipeline_id: string | null;\n close_date: string | null;\n owner_id: string | null;\n created_at: string | null;\n}\n\nexport interface PaginationMeta {\n page: number;\n limit: number;\n total: number;\n has_next: boolean;\n}\n\n/**\n * Deals API - CRUD for deals + pipelines. Requires API key (server-side).\n *\n * Backed by:\n * GET /api/v1/deals/pipelines\n * GET /api/v1/deals\n * POST /api/v1/deals\n * GET /api/v1/deals/{deal_id}\n * PATCH /api/v1/deals/{deal_id}\n * DELETE /api/v1/deals/{deal_id}\n * GET /api/v1/contacts/{contact_id}/deals\n * GET /api/v1/companies/{company_id}/deals\n */\nexport const createDealsClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n /** List all deal pipelines and their stages. */\n async pipelines(): Promise<{ data: Pipeline[] }> {\n return request(baseUrl, \"/api/v1/deals/pipelines\", apiKey);\n },\n\n /** List deals org-wide with optional filters and pagination. */\n async list(params: DealListParams = {}): Promise<{ data: Deal[]; pagination?: PaginationMeta }> {\n return request(baseUrl, \"/api/v1/deals\", apiKey, { query: params as Record<string, unknown> });\n },\n\n /** Create a new deal. */\n async create(deal: DealCreateParams): Promise<Deal> {\n const resp = await request<{ data?: Deal }>(baseUrl, \"/api/v1/deals\", apiKey, { method: \"POST\", body: deal });\n return resp.data ?? (resp as unknown as Deal);\n },\n\n /** Get a single deal by ID. */\n async get(dealId: string): Promise<Deal> {\n const resp = await request<{ data?: Deal }>(baseUrl, `/api/v1/deals/${dealId}`, apiKey);\n return resp.data ?? (resp as unknown as Deal);\n },\n\n /** Update a deal (partial). */\n async update(dealId: string, fields: DealUpdateParams): Promise<Deal> {\n const resp = await request<{ data?: Deal }>(baseUrl, `/api/v1/deals/${dealId}`, apiKey, {\n method: \"PATCH\",\n body: fields,\n });\n return resp.data ?? (resp as unknown as Deal);\n },\n\n /** Delete a deal. */\n async delete(dealId: string): Promise<{ data: { deleted: boolean } }> {\n return request(baseUrl, `/api/v1/deals/${dealId}`, apiKey, { method: \"DELETE\" });\n },\n\n /** Get all deals associated with a contact. */\n async forContact(contactId: number): Promise<{ data: ContactDeal[] }> {\n return request(baseUrl, `/api/v1/contacts/${contactId}/deals`, apiKey);\n },\n\n /** Get all deals associated with a company. */\n async forCompany(companyId: number): Promise<{ data: ContactDeal[] }> {\n return request(baseUrl, `/api/v1/companies/${companyId}/deals`, apiKey);\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\n/**\n * Inbox channel — `email`, `sms`, or `linkedin` (HeyReach internally).\n * Defaults to `email` on routes that accept this as a query param.\n */\nexport type InboxChannel = \"email\" | \"sms\" | \"linkedin\";\n\nexport interface InboxContact {\n name?: string | null;\n email?: string | null;\n company?: string | null;\n [key: string]: unknown;\n}\n\nexport interface InboxTag {\n id: string;\n name: string;\n}\n\nexport interface InboxAssignee {\n email: string;\n name?: string | null;\n [key: string]: unknown;\n}\n\nexport interface InboxMessage {\n message_id: string | null;\n from_address: string | null;\n to_addresses: string[];\n content: string | null;\n /** \"USER\", \"AI\", or \"OTHER\". */\n responder: string | null;\n date: string | null;\n is_draft: boolean;\n}\n\nexport interface InboxThread {\n id: string;\n /** \"email\", \"sms\", or \"linkedin\". */\n channel: string;\n subject: string | null;\n contact: InboxContact | null;\n messages: InboxMessage[];\n /** \"open\", \"responded\", \"ai_responded\", etc. */\n status: string | null;\n tags: InboxTag[];\n assignees: InboxAssignee[];\n created_at: string | null;\n updated_at: string | null;\n}\n\nexport interface InboxListParams {\n channel?: InboxChannel;\n sequence_id?: string;\n /** Filter by thread status (e.g. \"open\", \"responded\", \"ai_responded\"). */\n status?: string;\n /** Filter by assignee email. */\n assignee?: string;\n /** Filter by tag ID. */\n tag?: string;\n page?: number;\n /** Default 50, max 100. */\n page_size?: number;\n}\n\nexport interface InboxAssignResult {\n assigned: boolean;\n assignee: string;\n count: number;\n [key: string]: unknown;\n}\n\nexport interface InboxTagResult {\n tagged: boolean;\n tag_count: number;\n [key: string]: unknown;\n}\n\nexport interface InboxDraft {\n /** HTML body. */\n content: string;\n /** Plain-text alternative, if available. */\n plain_content: string | null;\n /** Credits charged for AI generation. */\n credits_charged: number;\n}\n\nexport interface InboxSendParams {\n body: string;\n channel: InboxChannel;\n subject?: string;\n /** Recipient override — email, phone, or LinkedIn ID. */\n to?: string;\n /** Sender override — mailbox email, Twilio number, or LinkedIn account ID. */\n from_address?: string;\n}\n\nexport interface InboxSendResult {\n message_id: string | null;\n status: string;\n channel: string;\n}\n\n/**\n * Inbox API — read + reply to multi-channel inbox threads (email, SMS, LinkedIn/HeyReach).\n * Requires API key (server-side). On the hardened HTTP core: throws a typed\n * `G8Error` on failure (e.g. 402 when an AI draft exceeds your credit balance)\n * and retries transient errors.\n *\n * Backed by:\n * GET /api/v1/inbox\n * GET /api/v1/inbox/{reply_id}\n * POST /api/v1/inbox/{reply_id}/assign\n * POST /api/v1/inbox/{reply_id}/tag\n * GET /api/v1/inbox/{reply_id}/draft (charges credits, 402 on insufficient balance)\n * POST /api/v1/inbox/{reply_id}/send\n */\nexport const createInboxClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n /** List inbox threads across email, SMS, and LinkedIn. */\n async list(params: InboxListParams = {}): Promise<{ data: InboxThread[] }> {\n return request(baseUrl, \"/api/v1/inbox\", apiKey, { query: params as Record<string, unknown> });\n },\n\n /** Get a single inbox thread. Defaults to email channel. */\n async get(replyId: string, channel: InboxChannel = \"email\"): Promise<InboxThread> {\n const resp = await request<{ data?: InboxThread }>(baseUrl, `/api/v1/inbox/${replyId}`, apiKey, {\n query: { channel },\n });\n return resp.data ?? (resp as unknown as InboxThread);\n },\n\n /** Assign a user to an inbox thread. */\n async assign(replyId: string, assigneeEmail: string, channel: InboxChannel = \"email\"): Promise<InboxAssignResult> {\n const resp = await request<{ data?: InboxAssignResult }>(baseUrl, `/api/v1/inbox/${replyId}/assign`, apiKey, {\n method: \"POST\",\n query: { channel },\n body: { assignee_email: assigneeEmail },\n });\n return resp.data ?? (resp as unknown as InboxAssignResult);\n },\n\n /** Attach tag IDs to an inbox thread. */\n async tag(replyId: string, tagIds: string[], channel: InboxChannel = \"email\"): Promise<InboxTagResult> {\n const resp = await request<{ data?: InboxTagResult }>(baseUrl, `/api/v1/inbox/${replyId}/tag`, apiKey, {\n method: \"POST\",\n query: { channel },\n body: { tag_ids: tagIds },\n });\n return resp.data ?? (resp as unknown as InboxTagResult);\n },\n\n /**\n * Generate an AI draft reply for a thread.\n * Charges credits — server returns 402 if balance is insufficient.\n */\n async draft(replyId: string, channel: InboxChannel = \"email\"): Promise<InboxDraft> {\n const resp = await request<{ data?: InboxDraft }>(baseUrl, `/api/v1/inbox/${replyId}/draft`, apiKey, {\n query: { channel },\n });\n return resp.data ?? (resp as unknown as InboxDraft);\n },\n\n /** Send a reply through email, SMS, or LinkedIn. */\n async send(replyId: string, payload: InboxSendParams): Promise<InboxSendResult> {\n const resp = await request<{ data?: InboxSendResult }>(baseUrl, `/api/v1/inbox/${replyId}/send`, apiKey, {\n method: \"POST\",\n body: payload,\n });\n return resp.data ?? (resp as unknown as InboxSendResult);\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface QuoteLineItem {\n product_id?: string | null;\n name?: string | null;\n description?: string | null;\n quantity: number;\n unit_price: number;\n currency?: string | null;\n recurring?: boolean | null;\n}\n\nexport type QuoteStatus = \"draft\" | \"sent\" | \"signed\" | \"declined\" | \"expired\" | \"void\";\n\nexport interface QuoteSummary {\n id: string;\n name: string | null;\n status: QuoteStatus;\n total: number | null;\n currency: string | null;\n contact_id: number | null;\n company_id: number | null;\n expires_at: string | null;\n created_at: string | null;\n updated_at: string | null;\n signing_url: string | null;\n payment_link_url: string | null;\n}\n\nexport interface QuoteDetail extends QuoteSummary {\n line_items: QuoteLineItem[];\n notes: string | null;\n recipient_email: string | null;\n signed_at: string | null;\n sent_at: string | null;\n}\n\nexport interface QuoteCreateParams {\n contact_id?: number;\n company_id?: number;\n line_items: QuoteLineItem[];\n currency?: string;\n notes?: string;\n /** ISO 8601 timestamp. */\n expires_at?: string;\n name?: string;\n}\n\nexport interface QuoteUpdateParams {\n line_items?: QuoteLineItem[];\n notes?: string;\n expires_at?: string;\n name?: string;\n}\n\nexport interface QuoteSendParams {\n recipient_email: string;\n /** Whether to attach the signing link. Defaults to true. */\n send_signing_link?: boolean;\n}\n\nexport interface QuoteListParams {\n status?: QuoteStatus;\n contact_id?: number;\n company_id?: number;\n page?: number;\n limit?: number;\n}\n\nexport interface QuotableProduct {\n id: string;\n name: string;\n description: string | null;\n unit_price: number;\n currency: string;\n recurring: boolean;\n}\n\nexport interface QuoteSettings {\n default_currency: string;\n default_tax_rate: number | null;\n payment_providers: string[];\n logo_url: string | null;\n signature_required: boolean;\n}\n\nexport interface PaginationMeta {\n page: number;\n limit: number;\n total: number;\n has_next: boolean;\n}\n\n/**\n * Quotes API - quote-to-cash lifecycle. Requires API key (server-side). On the\n * hardened HTTP core: throws a typed `G8Error` on failure and retries transient\n * errors.\n *\n * Backed by:\n * GET /api/v1/quotes\n * GET /api/v1/quotes/{id}\n * POST /api/v1/quotes\n * PUT /api/v1/quotes/{id}\n * DELETE /api/v1/quotes/{id}\n * POST /api/v1/quotes/{id}/duplicate\n * POST /api/v1/quotes/{id}/edit-as-draft\n * POST /api/v1/quotes/{id}/send\n * GET /api/v1/quotable-products\n * GET /api/v1/quote-settings\n * GET /api/v1/contacts/{contact_id}/quotes\n * GET /api/v1/companies/{company_id}/quotes\n */\nexport const createQuotesClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n /** List quotes org-wide with optional filters and pagination. */\n async list(params: QuoteListParams = {}): Promise<{ data: QuoteSummary[]; pagination?: PaginationMeta }> {\n return request(baseUrl, \"/api/v1/quotes\", apiKey, { query: params as Record<string, unknown> });\n },\n\n /** Get full details for a single quote. */\n async get(quoteId: string): Promise<QuoteDetail> {\n const resp = await request<{ data?: QuoteDetail }>(baseUrl, `/api/v1/quotes/${quoteId}`, apiKey);\n return resp.data ?? (resp as unknown as QuoteDetail);\n },\n\n /** Create a new quote from line items. */\n async create(quote: QuoteCreateParams): Promise<QuoteDetail> {\n const resp = await request<{ data?: QuoteDetail }>(baseUrl, \"/api/v1/quotes\", apiKey, {\n method: \"POST\",\n body: quote,\n });\n return resp.data ?? (resp as unknown as QuoteDetail);\n },\n\n /** Update a draft quote (line items, expiry, notes). */\n async update(quoteId: string, fields: QuoteUpdateParams): Promise<QuoteDetail> {\n const resp = await request<{ data?: QuoteDetail }>(baseUrl, `/api/v1/quotes/${quoteId}`, apiKey, {\n method: \"PUT\",\n body: fields,\n });\n return resp.data ?? (resp as unknown as QuoteDetail);\n },\n\n /** Delete a draft quote (irreversible). */\n async delete(quoteId: string): Promise<{ data: { deleted: boolean } }> {\n return request(baseUrl, `/api/v1/quotes/${quoteId}`, apiKey, { method: \"DELETE\" });\n },\n\n /** Duplicate an existing quote (all line items copied into a new draft). */\n async duplicate(quoteId: string): Promise<QuoteDetail> {\n const resp = await request<{ data?: QuoteDetail }>(baseUrl, `/api/v1/quotes/${quoteId}/duplicate`, apiKey, {\n method: \"POST\",\n body: {},\n });\n return resp.data ?? (resp as unknown as QuoteDetail);\n },\n\n /** Convert a signed / sent quote back to editable draft state. */\n async editAsDraft(quoteId: string): Promise<{ data: { id: string; status: string } }> {\n return request(baseUrl, `/api/v1/quotes/${quoteId}/edit-as-draft`, apiKey, { method: \"POST\", body: {} });\n },\n\n /** Send a quote to its recipient via email (with signature + optional payment link). */\n async send(quoteId: string, params: QuoteSendParams): Promise<QuoteDetail> {\n const resp = await request<{ data?: QuoteDetail }>(baseUrl, `/api/v1/quotes/${quoteId}/send`, apiKey, {\n method: \"POST\",\n body: params,\n });\n return resp.data ?? (resp as unknown as QuoteDetail);\n },\n\n /** List products available for line items. */\n async products(): Promise<{ data: QuotableProduct[] }> {\n return request(baseUrl, \"/api/v1/quotable-products\", apiKey);\n },\n\n /** Get org-level quote settings (currency, tax rate, payment providers, logo). */\n async settings(): Promise<QuoteSettings> {\n const resp = await request<{ data?: QuoteSettings }>(baseUrl, \"/api/v1/quote-settings\", apiKey);\n return resp.data ?? (resp as unknown as QuoteSettings);\n },\n\n /** Get all quotes associated with a contact. */\n async forContact(contactId: number): Promise<{ data: QuoteSummary[] }> {\n return request(baseUrl, `/api/v1/contacts/${contactId}/quotes`, apiKey);\n },\n\n /** Get all quotes associated with a company. */\n async forCompany(companyId: number): Promise<{ data: QuoteSummary[] }> {\n return request(baseUrl, `/api/v1/companies/${companyId}/quotes`, apiKey);\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\n/**\n * Stage Checklist v2 pipelines. A \"pipeline\" here is a multi-stage workflow\n * (with evidence requirements and per-channel scripts) that lives parallel\n * to deal pipelines. Use `g8.deals.pipelines()` for deal-pipeline reads.\n */\nexport interface StagePipelineStage {\n id: string;\n name: string;\n position: number;\n /** Evidence keys that must be collected before the stage can be marked done. */\n required_elements: string[];\n /** Evidence keys that are recommended but not enforced. */\n recommended_elements: string[];\n /** Channel-specific scripts (call, email, linkedin, etc.). */\n channel_scripts: Record<string, string>;\n color: string | null;\n stage_type: string | null;\n}\n\nexport interface StagePipeline {\n id: string;\n name: string;\n target: string | null;\n is_default: boolean;\n stages: StagePipelineStage[];\n created_at: string | null;\n updated_at: string | null;\n}\n\nexport interface StagePipelineCreateParams {\n name: string;\n target?: string;\n /** If true, creates an empty pipeline (no default stages). */\n blank?: boolean;\n}\n\nexport interface StagePipelineUpdateParams {\n name?: string;\n target?: string;\n}\n\nexport interface StageCreateParams {\n name: string;\n position?: number;\n required_elements?: string[];\n recommended_elements?: string[];\n channel_scripts?: Record<string, string>;\n color?: string;\n stage_type?: string;\n}\n\nexport interface StageUpdateParams {\n name?: string;\n position?: number;\n required_elements?: string[];\n recommended_elements?: string[];\n channel_scripts?: Record<string, string>;\n color?: string;\n}\n\nexport interface EvidenceKey {\n key: string;\n label: string;\n description: string;\n category: string;\n}\n\nexport interface PipelineSuggestion {\n suggestion_id: string;\n name: string;\n target: string;\n stages: Array<{ name: string; required_elements: string[]; channel_scripts: Record<string, string> }>;\n rationale: string;\n}\n\n/**\n * Stage Checklist Pipelines API - workflow pipelines with evidence + scripts.\n * Requires API key (server-side). On the hardened HTTP core: throws a typed\n * `G8Error` on failure and retries transient errors.\n *\n * Backed by:\n * GET /api/v1/pipelines\n * GET /api/v1/pipelines/{id}\n * GET /api/v1/pipelines/evidence-library\n * POST /api/v1/pipelines\n * PUT /api/v1/pipelines/{id}\n * DELETE /api/v1/pipelines/{id}\n * POST /api/v1/pipelines/{id}/stages\n * PUT /api/v1/pipelines/{id}/stages/{stage_id}\n * DELETE /api/v1/pipelines/{id}/stages/{stage_id}\n * PUT /api/v1/pipelines/{id}/stages/reorder\n * POST /api/v1/pipelines/suggest\n * POST /api/v1/pipelines/from-suggestion\n */\nexport const createPipelinesClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n /** List all stage-checklist pipelines with stages, evidence, scripts. */\n async list(): Promise<{ data: StagePipeline[] }> {\n return request(baseUrl, \"/api/v1/pipelines\", apiKey);\n },\n\n /** Get a single pipeline by ID. */\n async get(pipelineId: string): Promise<StagePipeline> {\n const resp = await request<{ data?: StagePipeline }>(baseUrl, `/api/v1/pipelines/${pipelineId}`, apiKey);\n return resp.data ?? (resp as unknown as StagePipeline);\n },\n\n /** Get the canonical evidence-key library (used when defining stages). */\n async evidenceLibrary(): Promise<{ data: EvidenceKey[] }> {\n return request(baseUrl, \"/api/v1/pipelines/evidence-library\", apiKey);\n },\n\n /** Create a new pipeline. Defaults to a templated set of stages; pass blank=true for empty. */\n async create(params: StagePipelineCreateParams): Promise<StagePipeline> {\n const resp = await request<{ data?: StagePipeline }>(baseUrl, \"/api/v1/pipelines\", apiKey, {\n method: \"POST\",\n body: params,\n });\n return resp.data ?? (resp as unknown as StagePipeline);\n },\n\n /** Update pipeline metadata (name, target). */\n async update(pipelineId: string, fields: StagePipelineUpdateParams): Promise<StagePipeline> {\n const resp = await request<{ data?: StagePipeline }>(baseUrl, `/api/v1/pipelines/${pipelineId}`, apiKey, {\n method: \"PUT\",\n body: fields,\n });\n return resp.data ?? (resp as unknown as StagePipeline);\n },\n\n /** Delete a pipeline. Only allowed when no deals reference it. */\n async delete(pipelineId: string): Promise<{ data: { deleted: boolean } }> {\n return request(baseUrl, `/api/v1/pipelines/${pipelineId}`, apiKey, { method: \"DELETE\" });\n },\n\n /** Add a new stage to a pipeline. */\n async createStage(pipelineId: string, stage: StageCreateParams): Promise<StagePipelineStage> {\n const resp = await request<{ data?: StagePipelineStage }>(\n baseUrl,\n `/api/v1/pipelines/${pipelineId}/stages`,\n apiKey,\n { method: \"POST\", body: stage },\n );\n return resp.data ?? (resp as unknown as StagePipelineStage);\n },\n\n /** Update a stage (evidence, scripts, position). */\n async updateStage(pipelineId: string, stageId: string, fields: StageUpdateParams): Promise<StagePipelineStage> {\n const resp = await request<{ data?: StagePipelineStage }>(\n baseUrl,\n `/api/v1/pipelines/${pipelineId}/stages/${stageId}`,\n apiKey,\n { method: \"PUT\", body: fields },\n );\n return resp.data ?? (resp as unknown as StagePipelineStage);\n },\n\n /** Reorder stages within a pipeline (pass full ordered list of stage IDs). */\n async reorderStages(pipelineId: string, stageIds: string[]): Promise<{ data: StagePipelineStage[] }> {\n return request(baseUrl, `/api/v1/pipelines/${pipelineId}/stages/reorder`, apiKey, {\n method: \"PUT\",\n body: { stage_ids: stageIds },\n });\n },\n\n /** Delete a stage from a pipeline. */\n async deleteStage(pipelineId: string, stageId: string): Promise<{ data: { deleted: boolean } }> {\n return request(baseUrl, `/api/v1/pipelines/${pipelineId}/stages/${stageId}`, apiKey, { method: \"DELETE\" });\n },\n\n /** Get an AI-suggested pipeline based on org context (brand, ICP, messaging). */\n async suggest(): Promise<{ data: PipelineSuggestion }> {\n return request(baseUrl, \"/api/v1/pipelines/suggest\", apiKey, { method: \"POST\", body: {} });\n },\n\n /** Create a real pipeline from an AI suggestion (optionally with overrides). */\n async fromSuggestion(\n suggestionId: string,\n overrides?: Partial<{ name: string; target: string }>,\n ): Promise<StagePipeline> {\n const resp = await request<{ data?: StagePipeline }>(baseUrl, \"/api/v1/pipelines/from-suggestion\", apiKey, {\n method: \"POST\",\n body: { suggestion_id: suggestionId, ...(overrides || {}) },\n });\n return resp.data ?? (resp as unknown as StagePipeline);\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface WorkflowNode {\n id: string;\n type: string;\n config: Record<string, unknown>;\n position?: { x: number; y: number };\n /** Mapping from this node's output to the next node's input. */\n input_mappings?: Record<string, string>;\n}\n\nexport interface WorkflowConnection {\n from_node_id: string;\n to_node_id: string;\n condition?: string | null;\n}\n\nexport interface WorkflowConfig {\n nodes: WorkflowNode[];\n connections: WorkflowConnection[];\n trigger?: Record<string, unknown> | null;\n}\n\nexport interface Workflow {\n id: string;\n name: string;\n description: string | null;\n config: WorkflowConfig;\n trigger_config: Record<string, unknown> | null;\n is_active: boolean;\n created_at: string | null;\n updated_at: string | null;\n}\n\nexport interface WorkflowCreateParams {\n name: string;\n description?: string;\n config?: WorkflowConfig;\n trigger_config?: Record<string, unknown>;\n}\n\nexport interface WorkflowUpdateParams {\n name?: string;\n description?: string;\n config?: WorkflowConfig;\n trigger_config?: Record<string, unknown>;\n is_active?: boolean;\n}\n\nexport interface WorkflowExecution {\n id: string;\n workflow_id: string;\n status: \"pending\" | \"running\" | \"paused\" | \"completed\" | \"failed\" | \"stopped\";\n trigger_payload: Record<string, unknown>;\n outputs: Record<string, unknown>;\n error: string | null;\n started_at: string | null;\n ended_at: string | null;\n}\n\nexport interface NodeTypeSchema {\n type: string;\n label: string;\n description: string;\n config_schema: Record<string, unknown>;\n output_schema: Record<string, unknown>;\n}\n\nexport interface WorkflowListParams {\n page?: number;\n limit?: number;\n is_active?: boolean;\n search?: string;\n}\n\n/**\n * Workflows API - automation workflows with nodes, connections, and execution lifecycle.\n * Requires API key (server-side). On the hardened HTTP core: throws a typed\n * `G8Error` on failure and retries transient errors.\n *\n * The graph8 workflow surface treats the whole workflow definition as a single\n * record updated via `update()` — there are no per-node CRUD endpoints. To edit\n * nodes or connections, fetch the workflow, mutate `config.nodes` / `config.connections`,\n * and PUT it back.\n *\n * Backed by:\n * GET /api/v1/workflows\n * GET /api/v1/workflows/{id}\n * POST /api/v1/workflows\n * PUT /api/v1/workflows/{id}\n * DELETE /api/v1/workflows/{id}\n * POST /api/v1/workflows/validate\n * POST /api/v1/workflows/{id}/execute\n * GET /api/v1/workflows/executions/{exec_id}\n * POST /api/v1/workflows/executions/{exec_id}/pause\n * POST /api/v1/workflows/executions/{exec_id}/resume\n * POST /api/v1/workflows/executions/{exec_id}/stop\n * GET /api/v1/workflows/{id}/trigger-status\n * POST /api/v1/workflows/{id}/trigger-reset\n * GET /api/v1/workflows/node-types/schema\n * GET /api/v1/workflows/integrations/slack/users|channels\n * GET /api/v1/workflows/integrations/roam/users|groups\n * GET /api/v1/workflows/mcp-servers\n * GET /api/v1/workflows/dispositions\n * GET /api/v1/workflows/forms/{form_id}/fields\n */\nexport const createWorkflowsClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n /** List workflows org-wide. */\n async list(params: WorkflowListParams = {}): Promise<{ data: Workflow[] }> {\n return request(baseUrl, \"/api/v1/workflows\", apiKey, { query: params as Record<string, unknown> });\n },\n\n /** Get full workflow definition (nodes, connections, trigger, execution state). */\n async get(workflowId: string): Promise<Workflow> {\n const resp = await request<{ data?: Workflow }>(baseUrl, `/api/v1/workflows/${workflowId}`, apiKey);\n return resp.data ?? (resp as unknown as Workflow);\n },\n\n /** Create a new workflow. */\n async create(params: WorkflowCreateParams): Promise<Workflow> {\n const resp = await request<{ data?: Workflow }>(baseUrl, \"/api/v1/workflows\", apiKey, {\n method: \"POST\",\n body: params,\n });\n return resp.data ?? (resp as unknown as Workflow);\n },\n\n /**\n * Update a workflow. Pass the full `config` (nodes + connections) to edit\n * the graph — node-level CRUD is performed client-side by mutating the\n * config and submitting the updated record.\n */\n async update(workflowId: string, fields: WorkflowUpdateParams): Promise<Workflow> {\n const resp = await request<{ data?: Workflow }>(baseUrl, `/api/v1/workflows/${workflowId}`, apiKey, {\n method: \"PUT\",\n body: fields,\n });\n return resp.data ?? (resp as unknown as Workflow);\n },\n\n /** Delete a workflow. */\n async delete(workflowId: string): Promise<{ data: { deleted: boolean } }> {\n return request(baseUrl, `/api/v1/workflows/${workflowId}`, apiKey, { method: \"DELETE\" });\n },\n\n /** Validate a workflow definition (orphans, dangling connections, required fields). */\n async validate(workflow: { config: WorkflowConfig; trigger_config?: Record<string, unknown> }): Promise<{\n data: { valid: boolean; errors: Array<{ node_id?: string; field?: string; message: string }> };\n }> {\n return request(baseUrl, \"/api/v1/workflows/validate\", apiKey, { method: \"POST\", body: workflow });\n },\n\n /** Execute a workflow immediately with a trigger payload. */\n async execute(workflowId: string, triggerPayload: Record<string, unknown> = {}): Promise<WorkflowExecution> {\n const resp = await request<{ data?: WorkflowExecution }>(\n baseUrl,\n `/api/v1/workflows/${workflowId}/execute`,\n apiKey,\n { method: \"POST\", body: { trigger_payload: triggerPayload } },\n );\n return resp.data ?? (resp as unknown as WorkflowExecution);\n },\n\n /** Get the status + outputs of a single execution. */\n async getExecution(executionId: string): Promise<WorkflowExecution> {\n const resp = await request<{ data?: WorkflowExecution }>(\n baseUrl,\n `/api/v1/workflows/executions/${executionId}`,\n apiKey,\n );\n return resp.data ?? (resp as unknown as WorkflowExecution);\n },\n\n /** Pause a running execution. */\n async pauseExecution(executionId: string): Promise<{ data: { paused: boolean } }> {\n return request(baseUrl, `/api/v1/workflows/executions/${executionId}/pause`, apiKey, { method: \"POST\" });\n },\n\n /** Resume a paused execution. */\n async resumeExecution(executionId: string): Promise<{ data: { resumed: boolean } }> {\n return request(baseUrl, `/api/v1/workflows/executions/${executionId}/resume`, apiKey, { method: \"POST\" });\n },\n\n /** Stop an execution. */\n async stopExecution(executionId: string): Promise<{ data: { stopped: boolean } }> {\n return request(baseUrl, `/api/v1/workflows/executions/${executionId}/stop`, apiKey, { method: \"POST\" });\n },\n\n /** Get the trigger status (e.g. schedule / webhook wiring) for a workflow. */\n async getTriggerStatus(workflowId: string): Promise<{ data: { status: string; details: Record<string, unknown> } }> {\n return request(baseUrl, `/api/v1/workflows/${workflowId}/trigger-status`, apiKey);\n },\n\n /** Reset a workflow's trigger cursor / state. */\n async resetTrigger(workflowId: string): Promise<{ data: { reset: boolean } }> {\n return request(baseUrl, `/api/v1/workflows/${workflowId}/trigger-reset`, apiKey, { method: \"POST\" });\n },\n\n /** Catalog of available workflow node types with config + output schemas. */\n async nodeTypes(params: { type?: string } = {}): Promise<{ data: NodeTypeSchema[] }> {\n return request(baseUrl, \"/api/v1/workflows/node-types/schema\", apiKey, {\n query: params as Record<string, unknown>,\n });\n },\n\n /** Slack users available to workflow nodes. */\n async listSlackUsers(): Promise<{ data: Array<{ id: string; name: string; email: string | null }> }> {\n return request(baseUrl, \"/api/v1/workflows/integrations/slack/users\", apiKey);\n },\n\n /** Slack channels available to workflow nodes. */\n async listSlackChannels(): Promise<{ data: Array<{ id: string; name: string; is_private: boolean }> }> {\n return request(baseUrl, \"/api/v1/workflows/integrations/slack/channels\", apiKey);\n },\n\n /** Roam users available to workflow nodes. */\n async listRoamUsers(): Promise<{ data: Array<{ id: string; name: string; email: string | null }> }> {\n return request(baseUrl, \"/api/v1/workflows/integrations/roam/users\", apiKey);\n },\n\n /** Roam groups available to workflow nodes. */\n async listRoamGroups(): Promise<{ data: Array<{ id: string; name: string }> }> {\n return request(baseUrl, \"/api/v1/workflows/integrations/roam/groups\", apiKey);\n },\n\n /** MCP servers available to workflow nodes. */\n async listMcpServers(): Promise<{ data: Array<{ id: string; name: string; url: string }> }> {\n return request(baseUrl, \"/api/v1/workflows/mcp-servers\", apiKey);\n },\n\n /** Disposition options available to workflow nodes. */\n async listDispositions(): Promise<{ data: Array<{ id: string; label: string }> }> {\n return request(baseUrl, \"/api/v1/workflows/dispositions\", apiKey);\n },\n\n /** Form field definitions for a given form (used by form-trigger nodes). */\n async listFormFields(formId: string): Promise<{ data: Array<{ name: string; type: string; required: boolean }> }> {\n return request(baseUrl, `/api/v1/workflows/forms/${formId}/fields`, apiKey);\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport type SkillType = \"llm\" | \"api\";\n\nexport interface SkillInputField {\n name: string;\n type: string;\n required: boolean;\n description?: string;\n}\n\nexport interface Skill {\n id: string;\n title: string;\n description: string | null;\n type: SkillType;\n /** Only present on `type=llm`. */\n prompt?: string;\n /** Only present on `type=llm`. */\n model?: string;\n /** Only present on `type=api`. */\n method?: \"GET\" | \"POST\" | \"PATCH\" | \"PUT\" | \"DELETE\";\n /** Only present on `type=api`. */\n url?: string;\n /** Only present on `type=api`. */\n headers?: Record<string, string>;\n /** Only present on `type=api`. */\n body_template?: string;\n input_schema: { fields: SkillInputField[] };\n output_schema: Record<string, unknown> | null;\n created_at: string | null;\n updated_at: string | null;\n}\n\nexport interface SkillCreateLLMParams {\n title: string;\n description?: string;\n prompt: string;\n model: string;\n input_schema: { fields: SkillInputField[] };\n output_schema?: Record<string, unknown>;\n}\n\nexport interface SkillCreateAPIParams {\n title: string;\n description?: string;\n method: \"GET\" | \"POST\" | \"PATCH\" | \"PUT\" | \"DELETE\";\n url: string;\n headers?: Record<string, string>;\n body_template?: string;\n response_mapping?: Record<string, string>;\n input_schema: { fields: SkillInputField[] };\n}\n\nexport interface SkillUpdateLLMParams {\n title?: string;\n description?: string;\n prompt?: string;\n model?: string;\n input_schema?: { fields: SkillInputField[] };\n output_schema?: Record<string, unknown>;\n}\n\nexport interface SkillUpdateAPIParams {\n title?: string;\n description?: string;\n method?: \"GET\" | \"POST\" | \"PATCH\" | \"PUT\" | \"DELETE\";\n url?: string;\n headers?: Record<string, string>;\n body_template?: string;\n response_mapping?: Record<string, string>;\n input_schema?: { fields: SkillInputField[] };\n}\n\nexport interface SkillTemplate {\n id: string;\n title: string;\n description: string;\n type: SkillType;\n use_cases: string[];\n}\n\nexport interface SkillListParams {\n page?: number;\n limit?: number;\n type?: SkillType;\n search?: string;\n}\n\n/**\n * Skills API - LLM and API building blocks that workflows compose into runs.\n * Requires API key (server-side).\n *\n * Backed by:\n * GET /api/v1/skills\n * GET /api/v1/skills/{id}\n * GET /api/v1/skills/{id}/variables\n * GET /api/v1/skills/models\n * GET /api/v1/skills/templates\n * POST /api/v1/skills (both llm + api types via `type` field)\n * POST /api/v1/skills/from-template\n * POST /api/v1/skills/from-node\n * PUT /api/v1/skills/{id}\n * DELETE /api/v1/skills/{id}\n * POST /api/v1/skills/validate\n * POST /api/v1/skills/{id}/execute\n */\nexport const createSkillsClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n return {\n /** List skills. */\n async list(params: SkillListParams = {}): Promise<{ data: Skill[] }> {\n return request(baseUrl, \"/api/v1/skills\", apiKey, { query: params as Record<string, unknown> });\n },\n\n /** Get a skill by ID. */\n async get(skillId: string): Promise<Skill> {\n const resp = await request<{ data?: Skill }>(baseUrl, `/api/v1/skills/${skillId}`, apiKey);\n return resp.data ?? (resp as unknown as Skill);\n },\n\n /** Get the variables required by a skill (extracted from prompt or body template). */\n async getVariables(skillId: string): Promise<{ data: SkillInputField[] }> {\n return request(baseUrl, `/api/v1/skills/${skillId}/variables`, apiKey);\n },\n\n /** List available LLM models. */\n async listModels(): Promise<{ data: Array<{ id: string; label: string; provider: string }> }> {\n return request(baseUrl, \"/api/v1/skills/models\", apiKey);\n },\n\n /** List skill templates. */\n async listTemplates(params: { type?: SkillType } = {}): Promise<{ data: SkillTemplate[] }> {\n return request(baseUrl, \"/api/v1/skills/templates\", apiKey, { query: params as Record<string, unknown> });\n },\n\n /** Create an LLM skill (prompt + model + schemas). */\n async createLLM(params: SkillCreateLLMParams): Promise<Skill> {\n const resp = await request<{ data?: Skill }>(baseUrl, \"/api/v1/skills\", apiKey, {\n method: \"POST\",\n body: { ...params, type: \"llm\" },\n });\n return resp.data ?? (resp as unknown as Skill);\n },\n\n /** Create an API skill (HTTP request wrapper). */\n async createAPI(params: SkillCreateAPIParams): Promise<Skill> {\n const resp = await request<{ data?: Skill }>(baseUrl, \"/api/v1/skills\", apiKey, {\n method: \"POST\",\n body: { ...params, type: \"api\" },\n });\n return resp.data ?? (resp as unknown as Skill);\n },\n\n /** Create a skill from a built-in template. */\n async createFromTemplate(params: { title: string; template_id: string; overrides?: Record<string, unknown> }): Promise<Skill> {\n const resp = await request<{ data?: Skill }>(baseUrl, \"/api/v1/skills/from-template\", apiKey, {\n method: \"POST\",\n body: params,\n });\n return resp.data ?? (resp as unknown as Skill);\n },\n\n /** Lift a workflow node into a reusable skill. */\n async createFromNode(params: {\n title: string;\n description?: string;\n node_id: string;\n input_mapping?: Record<string, string>;\n output_mapping?: Record<string, string>;\n }): Promise<Skill> {\n const resp = await request<{ data?: Skill }>(baseUrl, \"/api/v1/skills/from-node\", apiKey, {\n method: \"POST\",\n body: params,\n });\n return resp.data ?? (resp as unknown as Skill);\n },\n\n /** Update an LLM skill. */\n async updateLLM(skillId: string, fields: SkillUpdateLLMParams): Promise<Skill> {\n const resp = await request<{ data?: Skill }>(baseUrl, `/api/v1/skills/${skillId}`, apiKey, {\n method: \"PUT\",\n body: { ...fields, type: \"llm\" },\n });\n return resp.data ?? (resp as unknown as Skill);\n },\n\n /** Update an API skill. */\n async updateAPI(skillId: string, fields: SkillUpdateAPIParams): Promise<Skill> {\n const resp = await request<{ data?: Skill }>(baseUrl, `/api/v1/skills/${skillId}`, apiKey, {\n method: \"PUT\",\n body: { ...fields, type: \"api\" },\n });\n return resp.data ?? (resp as unknown as Skill);\n },\n\n /** Delete a skill (irreversible if in-use workflows exist). */\n async delete(skillId: string): Promise<{ data: { deleted: boolean } }> {\n return request(baseUrl, `/api/v1/skills/${skillId}`, apiKey, { method: \"DELETE\" });\n },\n\n /** Validate a skill definition (without saving). */\n async validate(skill: Partial<Skill>): Promise<{ data: { valid: boolean; errors: string[] } }> {\n return request(baseUrl, \"/api/v1/skills/validate\", apiKey, { method: \"POST\", body: skill });\n },\n\n /** Execute a skill immediately with an input payload (test / preview). */\n async execute(skillId: string, inputPayload: Record<string, unknown>): Promise<{\n data: { output: Record<string, unknown>; latency_ms: number; tokens?: number; error: string | null };\n }> {\n return request(baseUrl, `/api/v1/skills/${skillId}/execute`, apiKey, { method: \"POST\", body: inputPayload });\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface IntentKeyword {\n id: string;\n keyword: string;\n domain: string | null;\n include_patterns: string[];\n exclude_patterns: string[];\n page_count: number;\n visitor_count: number;\n company_count: number;\n contact_count: number;\n created_at: string | null;\n}\n\nexport interface IntentPage {\n url: string;\n title: string | null;\n keyword: string | null;\n domain: string;\n visitor_count: number;\n last_seen: string | null;\n}\n\nexport interface IntentVisitor {\n visitor_id: string;\n company_domain: string | null;\n company_name: string | null;\n page_url: string;\n first_seen: string | null;\n last_seen: string | null;\n visit_count: number;\n}\n\nexport interface IntentCompany {\n domain: string;\n name: string | null;\n industry: string | null;\n employee_count: string | null;\n visit_count: number;\n last_seen: string | null;\n}\n\n/** One company row from `intent.urlCompanies()`. Richer than {@link IntentCompany},\n * which describes the keyword-companies surface. */\nexport interface IntentUrlCompany {\n company_id: number | null;\n name: string | null;\n domain: string | null;\n industry: string | null;\n employee_count: number | null;\n logo_url: string | null;\n audience: string;\n visit_count: number;\n contacts_seen: number;\n last_seen: string | null;\n}\n\n/** Response of `intent.urlCompanies()`. This endpoint returns a bare aggregate,\n * not the `{ data }` envelope the rest of the Developer API uses. */\nexport interface IntentUrlCompaniesResponse {\n url: string;\n total_companies: number;\n b2b_count: number;\n b2c_count: number;\n companies: IntentUrlCompany[];\n}\n\nexport interface IntentContact {\n contact_id: number | null;\n email: string | null;\n full_name: string | null;\n company: string | null;\n job_title: string | null;\n last_seen: string | null;\n}\n\nexport interface IntentStats {\n total_keywords: number;\n total_pages: number;\n total_visitors_30d: number;\n total_companies_30d: number;\n}\n\n/**\n * Intent + visitor tracking. Distinct from `g8.signals` (which is summary scoring) —\n * this module exposes keyword tracking, page-level visitor data, and account-level\n * intent signals from your own site (or from competitor URLs you've added).\n *\n * Requires API key (server-side).\n *\n * Backed by:\n * GET /api/v1/intent/stats\n * POST /api/v1/intent/keywords/list\n * POST /api/v1/intent/keywords/create-from-domain\n * DELETE /api/v1/intent/keywords/{keyword_id}\n * POST /api/v1/intent/keywords/{keyword_id}/companies\n * POST /api/v1/intent/keywords/{keyword_id}/contacts\n * POST /api/v1/intent/keywords/{keyword_id}/urls\n * POST /api/v1/intent/pages-by-domain\n * POST /api/v1/intent/pages/search\n * POST /api/v1/intent/pages/visitors\n * POST /api/v1/intent/pages/contacts\n * POST /api/v1/intent/pages/visitor-counts\n * POST /api/v1/intent/url-companies\n */\nexport const createIntentClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n const get = (path: string) => request<any>(baseUrl, `/api/v1${path}`, apiKey);\n const post = (path: string, body: Record<string, unknown> = {}) =>\n request<any>(baseUrl, `/api/v1${path}`, apiKey, { method: \"POST\", body });\n const del = (path: string) => request<any>(baseUrl, `/api/v1${path}`, apiKey, { method: \"DELETE\" });\n\n return {\n /** Org-level intent stats (totals over the last 30 days). */\n async stats(): Promise<{ data: IntentStats }> {\n return get(\"/intent/stats\");\n },\n\n /** List tracked keywords with optional pagination. */\n async listKeywords(params: { page?: number; limit?: number; search?: string } = {}): Promise<{ data: IntentKeyword[] }> {\n return post(\"/intent/keywords/list\", params);\n },\n\n /** Create a keyword group from a domain (auto-tracks all pages). */\n async createFromDomain(domain: string): Promise<{ data: IntentKeyword }> {\n return post(\"/intent/keywords/create-from-domain\", { domain });\n },\n\n /** Stop tracking a keyword (irreversible — historical data is retained). */\n async deleteKeyword(keywordId: string): Promise<{ data: { deleted: boolean } }> {\n return del(`/intent/keywords/${keywordId}`);\n },\n\n /** Companies showing interest in a tracked keyword. */\n async keywordCompanies(keywordId: string, params: { limit?: number; date_from?: string; date_to?: string } = {}): Promise<{ data: IntentCompany[] }> {\n return post(`/intent/keywords/${keywordId}/companies`, params);\n },\n\n /** Contacts showing interest in a tracked keyword. */\n async keywordContacts(keywordId: string, params: { limit?: number; date_from?: string; date_to?: string } = {}): Promise<{ data: IntentContact[] }> {\n return post(`/intent/keywords/${keywordId}/contacts`, params);\n },\n\n /** URLs associated with a keyword (pages visitors landed on while interested). */\n async keywordUrls(keywordId: string, params: { limit?: number } = {}): Promise<{ data: IntentPage[] }> {\n return post(`/intent/keywords/${keywordId}/urls`, params);\n },\n\n /** Pages tracked on a specific domain. */\n async pagesByDomain(domain: string, params: { limit?: number } = {}): Promise<{ data: IntentPage[] }> {\n return post(\"/intent/pages-by-domain\", { domain, ...params });\n },\n\n /** Search tracked pages by URL fragment or keyword. */\n async searchPages(query: string, params: { limit?: number } = {}): Promise<{ data: IntentPage[] }> {\n return post(\"/intent/pages/search\", { query, ...params });\n },\n\n /** Get visitor records for a specific page URL. */\n async pageVisitors(pageUrl: string, params: { limit?: number; date_from?: string; date_to?: string } = {}): Promise<{ data: IntentVisitor[] }> {\n return post(\"/intent/pages/visitors\", { url: pageUrl, ...params });\n },\n\n /** Get contacts who visited a specific page URL. */\n async pageContacts(pageUrl: string, params: { limit?: number } = {}): Promise<{ data: IntentContact[] }> {\n return post(\"/intent/pages/contacts\", { url: pageUrl, ...params });\n },\n\n /** Visitor count aggregates by page (pass an array of URLs). */\n async pageVisitorCounts(urls: string[]): Promise<{ data: Array<{ url: string; visitor_count: number }> }> {\n return post(\"/intent/pages/visitor-counts\", { urls });\n },\n\n /**\n * Find companies whose users visited a specific URL (intent search).\n *\n * Previously posted to the bare-host `/intent-search/url-companies`, bypassing\n * the shared `post()` helper. That route is JWT-only, so this method could\n * never actually work with an API key. It now goes through\n * `/api/v1/intent/url-companies` like the rest of the intent surface (g8 issue\n * #16536).\n *\n * Returns a bare aggregate, NOT the `{ data }` envelope — the previous\n * `{ data: IntentCompany[] }` signature never matched what the API sends.\n *\n * `date_from` / `date_to` are accepted for backwards compatibility but have\n * never been read by this endpoint; use `days` to set the lookback window.\n */\n async urlCompanies(\n url: string,\n params: { limit?: number; days?: number; date_from?: string; date_to?: string } = {},\n ): Promise<IntentUrlCompaniesResponse> {\n return post(\"/intent/url-companies\", { url, ...params });\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface GlobalContextDocument {\n id: string;\n category: string;\n title: string;\n content: string;\n metadata: Record<string, unknown> | null;\n created_at: string | null;\n updated_at: string | null;\n}\n\nexport interface Persona {\n id: string;\n name: string;\n description: string | null;\n status: string;\n pain_points: string[];\n goals: string[];\n channels: string[];\n created_at: string | null;\n}\n\nexport interface ICP {\n id: string;\n name: string;\n description: string | null;\n status: string;\n industry: string[] | null;\n employee_count_min: number | null;\n employee_count_max: number | null;\n revenue_min: number | null;\n revenue_max: number | null;\n countries: string[] | null;\n created_at: string | null;\n}\n\nexport interface IntelligenceData {\n id: string;\n source_type: string;\n url: string | null;\n title: string | null;\n content_summary: string | null;\n collected_at: string | null;\n}\n\nexport interface ResearchReport {\n id: string;\n category: \"buyer_psychology\" | \"competitive_teardown\" | \"gtm_channel\" | \"industry_analyst\" | \"review_sentiment\" | \"voice_of_customer\";\n title: string;\n summary: string | null;\n created_at: string | null;\n}\n\n/**\n * Studio context — org-level intelligence documents. The 44-doc engine that\n * powers campaign ideation: brand brief, value props, messaging house, ICPs,\n * personas, intelligence (scrapes + enrichment), and AI research reports.\n *\n * Requires API key (server-side).\n *\n * Backed by:\n * GET /api/v1/global-context/documents\n * GET /api/v1/icps POST /api/v1/icps PATCH /api/v1/icps/{id} POST /api/v1/icps/{id}/archive\n * GET /api/v1/personas POST /api/v1/personas PATCH /api/v1/personas/{id} POST /api/v1/personas/{id}/archive\n * GET /api/v1/intelligence-data\n * GET /api/v1/research-reports\n */\nexport const createStudioClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n const get = (path: string, params: object = {}) =>\n request<any>(baseUrl, `/api/v1${path}`, apiKey, { query: params as Record<string, unknown> });\n const post = (path: string, body: object = {}) =>\n request<any>(baseUrl, `/api/v1${path}`, apiKey, { method: \"POST\", body });\n const patch = (path: string, body: object = {}) =>\n request<any>(baseUrl, `/api/v1${path}`, apiKey, { method: \"PATCH\", body });\n\n return {\n /** Org-level Studio documents (brand_brief, value_props, messaging_house, etc.).\n * `include_content` defaults to true server-side, so each document includes\n * its `content` body unless you explicitly pass `include_content: false`. */\n async globalContext(\n params: { category?: string; limit?: number; include_content?: boolean } = {}\n ): Promise<{ data: GlobalContextDocument[] }> {\n return get(\"/global-context/documents\", params);\n },\n\n /** ICP definitions. */\n async icps(params: { status?: string; limit?: number } = {}): Promise<{ data: ICP[] }> {\n return get(\"/icps\", params);\n },\n\n /** Buyer persona definitions. */\n async personas(params: { status?: string; limit?: number } = {}): Promise<{ data: Persona[] }> {\n return get(\"/personas\", params);\n },\n\n /** Intelligence data (website scrapes, enrichment, competitor research). */\n async intelligenceData(params: { source_type?: string; limit?: number } = {}): Promise<{ data: IntelligenceData[] }> {\n return get(\"/intelligence-data\", params);\n },\n\n /** AI research reports (buyer psychology, competitive teardown, GTM channel, etc.). */\n async researchReports(params: { category?: string; limit?: number } = {}): Promise<{ data: ResearchReport[] }> {\n return get(\"/research-reports\", params);\n },\n\n /** Create an ICP manually (no AI scoring). Requires name + website_url. */\n async createIcp(body: {\n name: string;\n website_url: string;\n description?: string;\n firmographics?: Record<string, unknown>;\n tech_stack?: Record<string, unknown>;\n buying_signals?: unknown[];\n estimated_market_size?: number;\n }): Promise<{ data: ICP }> {\n return post(\"/icps\", body);\n },\n\n /** Update an ICP (partial - send only fields to change). */\n async updateIcp(\n icpId: string,\n body: {\n name?: string;\n description?: string;\n firmographics?: Record<string, unknown>;\n tech_stack?: Record<string, unknown>;\n buying_signals?: unknown[];\n estimated_market_size?: number;\n }\n ): Promise<{ data: ICP }> {\n return patch(`/icps/${icpId}`, body);\n },\n\n /** Archive an ICP (soft delete - sets status to \"archived\"). */\n async archiveIcp(icpId: string): Promise<{ data: ICP }> {\n return post(`/icps/${icpId}/archive`);\n },\n\n /** Create a buyer persona manually (no AI generation). Requires title + website_url. */\n async createPersona(body: {\n title: string;\n website_url: string;\n priority?: number;\n confidence?: string;\n why_target?: string;\n key_signals?: unknown[];\n expected_receptivity?: string;\n campaign_approach?: string;\n recommended_goal?: string;\n source?: string;\n }): Promise<{ data: Persona }> {\n return post(\"/personas\", body);\n },\n\n /** Update a persona (partial). Passing status \"archived\" is equivalent to archivePersona. */\n async updatePersona(\n personaId: string,\n body: {\n title?: string;\n priority?: number;\n confidence?: string;\n why_target?: string;\n key_signals?: unknown[];\n expected_receptivity?: string;\n campaign_approach?: string;\n recommended_goal?: string;\n status?: string;\n }\n ): Promise<{ data: Persona }> {\n return patch(`/personas/${personaId}`, body);\n },\n\n /** Archive a persona (soft delete - sets status to \"archived\"). */\n async archivePersona(personaId: string): Promise<{ data: Persona }> {\n return post(`/personas/${personaId}/archive`);\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface MeetingAttendee {\n name: string | null;\n email: string;\n role: \"host\" | \"prospect\" | \"guest\" | string;\n contact_id?: number | null;\n}\n\nexport interface MeetingTranscriptLine {\n speaker: string;\n text: string;\n timestamp: number;\n}\n\nexport interface MeetingAnalysis {\n sentiment: \"positive\" | \"neutral\" | \"negative\";\n summary: string;\n next_steps: string[];\n objections: string[];\n talk_listen_ratio: number | null;\n}\n\nexport interface MeetingSummary {\n id: string;\n status: \"scheduled\" | \"completed\" | \"cancelled\" | \"no_show\";\n event_type: string | null;\n scheduled_at: string;\n duration_minutes: number;\n attendees: MeetingAttendee[];\n contact_id: number | null;\n conferencing_provider: string | null;\n conferencing_url: string | null;\n recording_url: string | null;\n created_at: string;\n}\n\nexport interface MeetingDetail extends MeetingSummary {\n transcript: MeetingTranscriptLine[] | null;\n analysis: MeetingAnalysis | null;\n}\n\nexport interface MeetingListParams {\n status?: \"scheduled\" | \"completed\" | \"cancelled\" | \"no_show\";\n date_from?: string;\n date_to?: string;\n attendee_id?: string;\n event_type?: string;\n contact_id?: number;\n page?: number;\n limit?: number;\n}\n\n/**\n * Meetings API - read-only access to scheduled, completed, and cancelled meetings.\n * Booking happens via `g8.calendar.*` (write-key safe, browser only) — this surface\n * is for reading meeting records, transcripts, and AI analysis.\n *\n * Requires API key (server-side).\n *\n * Backed by:\n * GET /api/v1/inbox/meetings\n * GET /api/v1/inbox/meetings/{meeting_id}\n */\nexport const createMeetingsClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n /** List meetings with optional filters. Returns summary rows without transcript / analysis. */\n async list(params: MeetingListParams = {}): Promise<{ data: MeetingSummary[]; pagination?: { page: number; limit: number; total: number; has_next: boolean } }> {\n return request(baseUrl, \"/api/v1/inbox/meetings\", apiKey, { query: params as Record<string, unknown> });\n },\n\n /** Get full meeting detail including transcript + AI analysis (transcript available 1-5 min after meeting ends). */\n async get(meetingId: string): Promise<MeetingDetail> {\n const resp = await request<{ data?: MeetingDetail }>(baseUrl, `/api/v1/inbox/meetings/${meetingId}`, apiKey);\n return resp.data ?? (resp as unknown as MeetingDetail);\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\n/** Ad platform an audience can sync to. */\nexport type AudienceSyncPlatform = \"meta\" | \"linkedin\" | \"google\" | \"x\";\n/** Sync mode: full mirror, or only ever add members. */\nexport type AudienceSyncMode = \"mirror\" | \"append_only\";\n\nexport interface AudienceSync {\n id: number;\n audience_id: number;\n platform: string;\n platform_audience_name: string | null;\n mode: string;\n refresh_cadence_hours: number;\n is_active: boolean;\n status: string | null;\n last_sync_at: string | null;\n created_at: string | null;\n}\n\nexport interface AudienceSyncCreateParams {\n /** Audience list ID to sync. */\n audience_id: number;\n platform: AudienceSyncPlatform;\n platform_audience_name?: string;\n /** Default \"mirror\". */\n mode?: AudienceSyncMode;\n /** Refresh cadence in hours (0-720; default 24). */\n refresh_cadence_hours?: number;\n /** Platform-specific config (OAuth creds, ad-account ids, etc.). */\n platform_config?: Record<string, unknown>;\n /** Audience list IDs whose members should be suppressed from the sync. */\n suppression_list_ids?: number[];\n}\n\nexport interface AudienceSyncUpdateParams {\n mode?: AudienceSyncMode;\n refresh_cadence_hours?: number;\n is_active?: boolean;\n suppression_list_ids?: number[];\n}\n\nexport interface AudienceSyncRun {\n id: number;\n started_at: string | null;\n finished_at: string | null;\n status: string | null;\n members_added: number | null;\n members_removed: number | null;\n total_members: number | null;\n error_message: string | null;\n}\n\nexport interface AudienceSyncError {\n id: number;\n started_at: string | null;\n error_message: string | null;\n details: Record<string, unknown> | null;\n}\n\n/**\n * Audiences API — sync an audience list to ad platforms (Meta, LinkedIn,\n * Google, X). Requires API key (server-side). Built on the hardened HTTP core,\n * so every method throws a typed `G8Error` on failure and retries transient\n * 429/5xx/network errors.\n *\n * Backed by `/api/v1/audience-syncs`.\n */\nexport const createAudiencesClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n const base = \"/api/v1/audience-syncs\";\n\n return {\n /** List all audience syncs for the organization. */\n async list(): Promise<{ data: AudienceSync[] }> {\n return request(baseUrl, base, apiKey);\n },\n\n /** Create a new audience sync to an ad platform. */\n async create(params: AudienceSyncCreateParams): Promise<AudienceSync> {\n const resp = await request<{ data?: AudienceSync }>(baseUrl, base, apiKey, {\n method: \"POST\",\n body: params,\n });\n return resp.data ?? (resp as unknown as AudienceSync);\n },\n\n /** Get a single audience sync by ID. */\n async get(configId: number): Promise<AudienceSync> {\n const resp = await request<{ data?: AudienceSync }>(baseUrl, `${base}/${configId}`, apiKey);\n return resp.data ?? (resp as unknown as AudienceSync);\n },\n\n /** Update an audience sync (partial). */\n async update(configId: number, fields: AudienceSyncUpdateParams): Promise<AudienceSync> {\n const resp = await request<{ data?: AudienceSync }>(baseUrl, `${base}/${configId}`, apiKey, {\n method: \"PATCH\",\n body: fields,\n });\n return resp.data ?? (resp as unknown as AudienceSync);\n },\n\n /** Delete an audience sync. */\n async delete(configId: number): Promise<{ data: Record<string, unknown> }> {\n return request(baseUrl, `${base}/${configId}`, apiKey, { method: \"DELETE\" });\n },\n\n /** Trigger an immediate sync run for a config. */\n async trigger(configId: number): Promise<{ data: Record<string, unknown> }> {\n return request(baseUrl, `${base}/${configId}/trigger`, apiKey, { method: \"POST\" });\n },\n\n /** List recent sync runs for a config (most recent first). */\n async runs(configId: number): Promise<{ data: AudienceSyncRun[] }> {\n return request(baseUrl, `${base}/${configId}/runs`, apiKey);\n },\n\n /** List recent sync errors for a config. */\n async errors(configId: number): Promise<{ data: AudienceSyncError[] }> {\n return request(baseUrl, `${base}/${configId}/errors`, apiKey);\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\n/** Filter operators supported by open-data search. */\nexport type SearchOperator =\n | \"any_of\"\n | \"contains\"\n | \"all_of\"\n | \"none_of\"\n | \"is_empty\"\n | \"is_not_empty\"\n | \"between\"\n | \"exists\";\n\n/** A single search filter condition (named to avoid colliding with enrich's `SearchFilter`). */\nexport interface SearchCondition {\n /** Field to filter on, e.g. \"job_title\", \"country\", \"industry\". */\n field: string;\n operator: SearchOperator;\n /** Filter values (operator-dependent; e.g. `[\"VP\", \"Director\"]` for any_of). */\n value?: unknown[];\n}\n\nexport interface SearchParams {\n /** Filter conditions, combined with AND. */\n filters?: SearchCondition[];\n /** Page number, 1-indexed (1-100). */\n page?: number;\n /** Results per page (1-100, default 25). */\n limit?: number;\n}\n\nexport interface SearchSaveParams extends SearchParams {\n /** Title for the new list the matched records are saved into. */\n list_title: string;\n /** Max records to save (1-10,000, default 1,000). */\n max_results?: number;\n}\n\nexport interface SearchContactItem {\n first_name?: string | null;\n last_name?: string | null;\n middle_name?: string | null;\n work_email?: string | null;\n personal_emails?: string | null;\n direct_phone?: string | null;\n mobile_phone?: string | null;\n job_title?: string | null;\n job_department?: string | null;\n seniority_level?: string | null;\n role?: string | null;\n linkedin_url?: string | null;\n linkedin_headline?: string | null;\n city?: string | null;\n state?: string | null;\n country?: string | null;\n company_name?: string | null;\n company_domain?: string | null;\n company_industry?: string | null;\n company_employee_count?: string | null;\n company_country?: string | null;\n confidence_score?: number | null;\n}\n\nexport interface SearchCompanyItem {\n name?: string | null;\n domain?: string | null;\n website?: string | null;\n description?: string | null;\n industry?: string | null;\n industry_group?: string | null;\n employee_count?: string | null;\n revenue?: string | null;\n founded_year?: number | null;\n phone?: string | null;\n address?: string | null;\n city?: string | null;\n state?: string | null;\n country?: string | null;\n zip?: number | null;\n linkedin_url?: string | null;\n linkedin_followers?: string | null;\n facebook_url?: string | null;\n twitter_url?: string | null;\n crunchbase_url?: string | null;\n logo_url?: string | null;\n}\n\nexport interface SearchSaveResult {\n list_id: number;\n list_title: string;\n estimated_total: number;\n status: string;\n}\n\n/**\n * Search API — prospect the open-data graph for new contacts and companies by\n * filter, and optionally save the matches straight into a list. Requires API\n * key (server-side). Built on the hardened HTTP core, so every method throws a\n * typed `G8Error` on failure and retries transient errors.\n *\n * Backed by `/api/v1/search`.\n */\nexport const createSearchClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n const body = (p: SearchParams) => ({ filters: [], page: 1, limit: 25, ...p });\n\n return {\n /** Search open-data contacts by filter. */\n async contacts(params: SearchParams = {}): Promise<{ data: SearchContactItem[] }> {\n return request(baseUrl, \"/api/v1/search/contacts\", apiKey, { method: \"POST\", body: body(params) });\n },\n\n /** Search open-data companies by filter. */\n async companies(params: SearchParams = {}): Promise<{ data: SearchCompanyItem[] }> {\n return request(baseUrl, \"/api/v1/search/companies\", apiKey, { method: \"POST\", body: body(params) });\n },\n\n /** Search contacts and save the matches into a new list. */\n async saveContacts(params: SearchSaveParams): Promise<SearchSaveResult> {\n const resp = await request<{ data?: SearchSaveResult }>(baseUrl, \"/api/v1/search/contacts/save\", apiKey, {\n method: \"POST\",\n body: { filters: [], page: 1, limit: 25, max_results: 1000, ...params },\n });\n return resp.data ?? (resp as unknown as SearchSaveResult);\n },\n\n /** Search companies and save the matches into a new list. */\n async saveCompanies(params: SearchSaveParams): Promise<SearchSaveResult> {\n const resp = await request<{ data?: SearchSaveResult }>(baseUrl, \"/api/v1/search/companies/save\", apiKey, {\n method: \"POST\",\n body: { filters: [], page: 1, limit: 25, max_results: 1000, ...params },\n });\n return resp.data ?? (resp as unknown as SearchSaveResult);\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface AgencyInfo {\n agency_org_id: string;\n agency_org_name: string | null;\n is_agency: boolean;\n client_count: number;\n /** Request header to set when operating a client org (e.g. \"X-Target-Org-Id\"). */\n target_header: string;\n}\n\nexport interface AgencyClient {\n /** Pass this as the `X-Target-Org-Id` header to operate this client org. */\n org_id: string;\n}\n\n/**\n * Agency API — for agency-scoped API keys (minted with `is_agency: true`).\n * Discover the agency credential and the client orgs it may operate. To act on a\n * client org, set the `X-Target-Org-Id` header (see `target_header`) to that\n * client's `org_id` on your subsequent requests. Requires an agency API key\n * (server-side); a non-agency key gets a `403`.\n *\n * Built on the hardened HTTP core (typed `G8Error` + retries). Backed by\n * `/api/v1/agency`.\n */\nexport const createAgencyClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n /** Describe the agency credential: agency org + authorized client count. */\n async me(): Promise<AgencyInfo> {\n const resp = await request<{ data?: AgencyInfo }>(baseUrl, \"/api/v1/agency/me\", apiKey);\n return resp.data ?? (resp as unknown as AgencyInfo);\n },\n\n /** List the client orgs this agency key may target via `X-Target-Org-Id`. */\n async clients(): Promise<{ data: AgencyClient[] }> {\n return request(baseUrl, \"/api/v1/agency/clients\", apiKey);\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface MarketplaceProfile {\n id: string;\n email: string;\n first_name?: string | null;\n last_name?: string | null;\n marketplace_role?: string | null;\n country_code?: string | null;\n monthly_rate_usd?: string | null;\n per_meeting_rate_usd?: string | null;\n availability_status?: string | null;\n bio?: string | null;\n is_complete: boolean;\n}\n\nexport interface MarketplaceOffer {\n id: string;\n org_id: string;\n org_name?: string | null;\n sdr_id: string;\n status: string;\n monthly_rate_usd?: string | null;\n per_meeting_rate_usd?: string | null;\n created_at?: string | null;\n}\n\nexport interface MarketplaceHiring {\n id: string;\n org_id: string;\n sdr_id: string;\n status: string;\n sdr_name?: string | null;\n sdr_role?: string | null;\n sdr_email?: string | null;\n monthly_rate_usd?: string | null;\n per_meeting_rate_usd?: string | null;\n started_at?: string | null;\n}\n\n/**\n * Marketplace API — for SDR/AE talent on the graph8 marketplace. View your\n * profile, see pending hire offers and respond to them, and list your active\n * hirings. Requires a personal API key (server-side). Built on the hardened\n * HTTP core, so every method throws a typed `G8Error` on failure and retries\n * transient errors.\n *\n * Backed by `/api/v1/marketplace`.\n */\nexport const createMarketplaceClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n const base = \"/api/v1/marketplace\";\n\n return {\n /** Your own marketplace SDR profile. */\n async profile(): Promise<MarketplaceProfile> {\n const resp = await request<{ data?: MarketplaceProfile }>(baseUrl, `${base}/me/profile`, apiKey);\n return resp.data ?? (resp as unknown as MarketplaceProfile);\n },\n\n /** Pending hire offers you can accept or reject. */\n async offers(): Promise<{ offers: MarketplaceOffer[]; count: number }> {\n const resp = await request<{ data?: { offers: MarketplaceOffer[]; count: number } }>(\n baseUrl,\n `${base}/me/offers`,\n apiKey,\n );\n return resp.data ?? (resp as unknown as { offers: MarketplaceOffer[]; count: number });\n },\n\n /** Accept a pending hire offer (by hiring id). */\n async acceptOffer(hiringId: string): Promise<Record<string, unknown>> {\n const resp = await request<{ data?: Record<string, unknown> }>(\n baseUrl,\n `${base}/me/offers/${hiringId}/accept`,\n apiKey,\n { method: \"POST\" },\n );\n return resp.data ?? (resp as unknown as Record<string, unknown>);\n },\n\n /** Reject a pending hire offer (by hiring id). */\n async rejectOffer(hiringId: string): Promise<Record<string, unknown>> {\n const resp = await request<{ data?: Record<string, unknown> }>(\n baseUrl,\n `${base}/me/offers/${hiringId}/reject`,\n apiKey,\n { method: \"POST\" },\n );\n return resp.data ?? (resp as unknown as Record<string, unknown>);\n },\n\n /** Your active hiring contracts. */\n async hirings(): Promise<{ hirings: MarketplaceHiring[]; count: number }> {\n const resp = await request<{ data?: { hirings: MarketplaceHiring[]; count: number } }>(\n baseUrl,\n `${base}/me/hirings`,\n apiKey,\n );\n return resp.data ?? (resp as unknown as { hirings: MarketplaceHiring[]; count: number });\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface Snippet {\n write_key: string;\n tracking_host: string;\n domains: string[];\n /** Ready-to-use React component import for @graph8/nextjs. */\n react_snippet: string;\n /** Vanilla JS script tag for non-React apps. */\n script_tag: string;\n /** Full configuration object (gtm/config.json). */\n config: Record<string, unknown>;\n}\n\n/**\n * Snippet API — fetch your org's graph8 tracking snippet programmatically, to\n * embed the tracker into an app or a customer's site (the write key, a React\n * component, a vanilla `<script>` tag, allowed domains, and the full config).\n * Requires an API key (server-side). Built on the hardened HTTP core, so it\n * throws a typed `G8Error` on failure and retries transient errors.\n *\n * Backed by `GET /api/v1/snippet`.\n */\nexport const createSnippetClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n /** Get your org's tracking snippet (write key + React/script-tag embeds + config). */\n async get(): Promise<Snippet> {\n const resp = await request<{ data?: Snippet }>(baseUrl, \"/api/v1/snippet\", apiKey);\n return resp.data ?? (resp as unknown as Snippet);\n },\n };\n};\n","/**\n * App-platform token exchange + refresh core (M6-9).\n *\n * The hosted-app clients never carry a permanent org API key. Instead they mint\n * SHORT-LIVED app tokens by EXCHANGING a longer-lived credential, and refresh\n * them on/near expiry:\n *\n * - browser apps exchange the caller's PropelAuth login at\n * `POST /api/v1/app-sessions/exchange` (identity is derived server-side from\n * the validated principal — the body only names the target app + scopes);\n * - hosted backends exchange service client-credentials at\n * `POST /api/v1/service-token`.\n *\n * Both endpoints answer with the reused `ApiResponse` envelope wrapping a\n * `TokenResponse` (see developer_api/app_platform/app_tokens/dtos.py). This\n * module owns:\n *\n * - `AppTokenResponse` — the minted-token payload shape (frozen wire contract);\n * - `createTokenManager` — caches a token, refreshes it on/near expiry, and\n * de-dupes concurrent refreshes into one in-flight exchange;\n * - `createAppRequester` — binds the managed token onto the hardened\n * `request()` core (typed `G8Error`, retries, idempotency, pagination),\n * attaching `Authorization: Bearer <app-token>` (plus any static header such\n * as a service client's `X-Target-Org-Id`) and refreshing once on a 401.\n *\n * The backend app-token API is INERT/unmounted today (behind a deny-by-default\n * flag), so these paths are exercised against mocked HTTP, never a live server.\n */\n\nimport { request, G8Error, type RequestOptions } from \"./http\";\n\nexport const DEFAULT_APP_API = \"https://be.graph8.com\";\n\n/** The minted app token + the non-secret metadata a client reads back.\n * Mirrors `TokenResponse` in developer_api/app_platform/app_tokens/dtos.py. */\nexport interface AppTokenResponse {\n /** The signed app JWT (attach as `Authorization: Bearer <access_token>`). */\n access_token: string;\n /** Always \"Bearer\". */\n token_type: string;\n /** Lifetime in seconds from issuance. */\n expires_in: number;\n /** The distinct-per-app, per-environment audience. */\n audience: string;\n /** App the token is bound to (dapp_-prefixed). */\n app_id: string;\n /** Signing key id (matches a JWKS entry). */\n kid: string;\n}\n\n/** Per-request options accepted by an {@link AppRequest}. A subset of the\n * hardened core's `RequestOptions` — auth (the app token) and the injected\n * fetch/sleep seams are bound by the requester, not passed per call. */\nexport interface AppRequestOptions {\n method?: string;\n body?: unknown;\n headers?: Record<string, string>;\n query?: Record<string, unknown>;\n /** Sent as `Idempotency-Key` for safe POST/PATCH retries. */\n idempotencyKey?: string;\n /** Max retry attempts on 429/5xx/network (default 2). */\n maxRetries?: number;\n /** Base backoff in ms (default 200). */\n retryBaseMs?: number;\n signal?: AbortSignal;\n}\n\n/** A token-bound request function: same ergonomics as the raw `request()` core\n * minus the credential, which the token manager supplies. */\nexport type AppRequest = <T = unknown>(path: string, opts?: AppRequestOptions) => Promise<T>;\n\n/** Caches + refreshes one app token. */\nexport interface TokenManager {\n /** Return a valid token, refreshing on/near expiry. `force` refreshes now. */\n getToken(force?: boolean): Promise<string>;\n /** Current token expiry as an epoch-ms timestamp (0 before the first mint). */\n expiresAt(): number;\n}\n\n/**\n * Build a token manager over a `refresh` exchange.\n *\n * Freshness rule: a cached token is reused until it is within `refreshSkewMs`\n * of expiry, then a refresh is triggered. Concurrent callers during a refresh\n * share ONE in-flight exchange (no stampede). `now` is injectable for tests.\n */\nexport function createTokenManager(args: {\n refresh: () => Promise<AppTokenResponse>;\n now?: () => number;\n refreshSkewMs?: number;\n}): TokenManager {\n const now = args.now ?? (() => Date.now());\n const skewMs = args.refreshSkewMs ?? 60_000;\n let token: string | null = null;\n let expiresAtMs = 0;\n let inflight: Promise<string> | null = null;\n\n const doRefresh = async (): Promise<string> => {\n const resp = await args.refresh();\n token = resp.access_token;\n expiresAtMs = now() + Math.max(0, resp.expires_in) * 1000;\n return token;\n };\n\n return {\n async getToken(force = false): Promise<string> {\n const stillFresh = !force && token !== null && now() < expiresAtMs - skewMs;\n if (stillFresh) return token as string;\n if (!inflight) {\n inflight = doRefresh().finally(() => {\n inflight = null;\n });\n }\n return inflight;\n },\n expiresAt(): number {\n return expiresAtMs;\n },\n };\n}\n\n/**\n * Bind a {@link TokenManager} onto the hardened `request()` core.\n *\n * Every call fetches the current token, attaches `Authorization: Bearer …`\n * (plus any `extraHeaders`, e.g. a service client's `X-Target-Org-Id`), and\n * reuses all of the core's typed errors / retries / idempotency. On a 401 —\n * a token revoked or gone stale mid-window — it force-refreshes ONCE and retries.\n */\nexport function createAppRequester(args: {\n baseUrl: string;\n tokens: TokenManager;\n extraHeaders?: Record<string, string>;\n fetchImpl?: typeof fetch;\n sleepImpl?: (ms: number) => Promise<void>;\n}): AppRequest {\n const { baseUrl, tokens, extraHeaders, fetchImpl, sleepImpl } = args;\n\n // Written as a `const` arrow ON PURPOSE: the SDK contract-extractor\n // (tests/contract/extract-routes.ts) recognises a local arrow that wraps\n // request() around its own `path` parameter as a *helper* and skips this\n // generic pass-through, so it never mis-reads it as a concrete dead route.\n const appRequest = async <T = unknown>(path: string, opts: AppRequestOptions = {}): Promise<T> => {\n const headers = { ...(extraHeaders ?? {}), ...(opts.headers ?? {}) };\n const reqOpts: RequestOptions = { ...opts, headers, fetchImpl, sleepImpl };\n const token = await tokens.getToken();\n try {\n return await request<T>(baseUrl, path, token, reqOpts);\n } catch (err) {\n if (err instanceof G8Error && err.status === 401) {\n const fresh = await tokens.getToken(true);\n return await request<T>(baseUrl, path, fresh, reqOpts);\n }\n throw err;\n }\n };\n\n return appRequest;\n}\n\n/**\n * Exchange the caller's PropelAuth token for a short-lived BROWSER app token.\n * `POST /api/v1/app-sessions/exchange` (body = target app + scopes only;\n * identity/role/tenant are derived server-side from the validated principal).\n */\nexport async function exchangeBrowserToken(args: {\n baseUrl: string;\n appId: string;\n scopes?: string[];\n getPropelAuthToken: () => string | Promise<string>;\n fetchImpl?: typeof fetch;\n sleepImpl?: (ms: number) => Promise<void>;\n}): Promise<AppTokenResponse> {\n const propelToken = await Promise.resolve(args.getPropelAuthToken());\n if (!propelToken) {\n throw new G8Error({\n message: \"getPropelAuthToken() returned no token — cannot exchange a browser app session\",\n status: 401,\n type: \"app_token_invalid\",\n code: \"app_token_invalid\",\n });\n }\n const resp = await request<{ data?: AppTokenResponse } & Partial<AppTokenResponse>>(\n args.baseUrl,\n \"/api/v1/app-sessions/exchange\",\n propelToken,\n {\n method: \"POST\",\n body: { app_id: args.appId, scopes: args.scopes ?? [] },\n fetchImpl: args.fetchImpl,\n sleepImpl: args.sleepImpl,\n },\n );\n return (resp.data ?? (resp as AppTokenResponse)) as AppTokenResponse;\n}\n\n/**\n * Exchange service client-credentials for a SERVICE app token.\n * `POST /api/v1/service-token` — credentials travel in the body; this endpoint\n * has no bearer auth, so no `Authorization` is sent on the exchange itself.\n */\nexport async function exchangeServiceToken(args: {\n baseUrl: string;\n clientId: string;\n clientSecret: string;\n scopes?: string[];\n ttlSeconds?: number;\n fetchImpl?: typeof fetch;\n sleepImpl?: (ms: number) => Promise<void>;\n}): Promise<AppTokenResponse> {\n const resp = await request<{ data?: AppTokenResponse } & Partial<AppTokenResponse>>(\n args.baseUrl,\n \"/api/v1/service-token\",\n \"\",\n {\n method: \"POST\",\n body: {\n client_id: args.clientId,\n client_secret: args.clientSecret,\n scopes: args.scopes ?? [],\n ttl_seconds: args.ttlSeconds ?? null,\n },\n fetchImpl: args.fetchImpl,\n sleepImpl: args.sleepImpl,\n },\n );\n return (resp.data ?? (resp as AppTokenResponse)) as AppTokenResponse;\n}\n","/**\n * Hosted app-platform clients (M6-9).\n *\n * Two entry points, both additive to `@graph8/sdk` and independent of the\n * `g8` tracking/API-key singleton:\n *\n * - `createGraph8AppClient` — BROWSER client. Exchanges the caller's\n * PropelAuth token for a short-lived app token, refreshes it on/near expiry,\n * and attaches `Authorization: Bearer <app-token>` to every call. It NEVER\n * accepts a permanent org API key for browser init (the whole reason the\n * token-exchange exists).\n * - `createGraph8ServiceClient` — BACKEND client. Exchanges service\n * client-credentials for a service app token and sends `X-Target-Org-Id`\n * for the single configured tenant on every call.\n *\n * Both expose a token-bound `request()` — the hardened `request()` core with the\n * minted app token attached — so callers reach Developer API routes with the\n * app token and reuse the core's typed `G8Error`, retries, idempotency, and\n * cursor pagination. The typed `apps` (control-plane) and `objects`\n * (custom-object CRUD) resource modules are DEFERRED until their backend routes\n * ship and freeze (M6-3 / M6-5); only the auth clients ship here.\n */\n\nimport {\n createTokenManager,\n createAppRequester,\n exchangeBrowserToken,\n exchangeServiceToken,\n DEFAULT_APP_API,\n type AppRequest,\n} from \"./appTokens\";\n\nexport type { AppTokenResponse, TokenManager, AppRequest, AppRequestOptions } from \"./appTokens\";\n\n/** Config for {@link createGraph8AppClient} (browser). No API key is accepted. */\nexport interface Graph8AppClientConfig {\n /** Target app id (dapp_-prefixed). */\n appId: string;\n /** The client org this browser session acts in (informational; the server\n * derives the authoritative tenant from the validated PropelAuth principal). */\n tenantOrgId: string;\n /** Returns the caller's current PropelAuth token (sync or async). */\n getPropelAuthToken: () => string | Promise<string>;\n /** Scopes to embed in the minted app token. */\n scopes?: string[];\n /** Backend API URL (default https://be.graph8.com). */\n apiUrl?: string;\n /** Refresh this many ms before expiry (default 60_000). */\n refreshSkewMs?: number;\n /** Injected for tests; defaults to global fetch. */\n fetchImpl?: typeof fetch;\n /** Injected for tests; defaults to a real setTimeout sleep. */\n sleepImpl?: (ms: number) => Promise<void>;\n /** Injected for tests; defaults to Date.now. */\n now?: () => number;\n}\n\n/** A browser app-platform client. */\nexport interface Graph8AppClient {\n readonly appId: string;\n readonly tenantOrgId: string;\n /** Token-bound requester: mints/refreshes the app token and attaches\n * `Authorization: Bearer <app-token>` to every Developer API call. */\n readonly request: AppRequest;\n /** Return the current app token, refreshing on/near expiry (`force` refreshes now). */\n getAppToken(force?: boolean): Promise<string>;\n}\n\n/**\n * Create a BROWSER app-platform client backed by PropelAuth->app-token exchange.\n * Refuses an org API key / write key — those must never reach a browser.\n */\nexport function createGraph8AppClient(config: Graph8AppClientConfig): Graph8AppClient {\n if (typeof config?.getPropelAuthToken !== \"function\") {\n throw new Error(\n \"createGraph8AppClient requires getPropelAuthToken() — a function returning the caller's PropelAuth token\",\n );\n }\n const asRecord = config as unknown as Record<string, unknown>;\n if (\"apiKey\" in asRecord || \"writeKey\" in asRecord) {\n throw new Error(\n \"createGraph8AppClient does not accept an org API key or write key in the browser — pass getPropelAuthToken() instead\",\n );\n }\n\n const baseUrl = config.apiUrl || DEFAULT_APP_API;\n const tokens = createTokenManager({\n now: config.now,\n refreshSkewMs: config.refreshSkewMs,\n refresh: () =>\n exchangeBrowserToken({\n baseUrl,\n appId: config.appId,\n scopes: config.scopes,\n getPropelAuthToken: config.getPropelAuthToken,\n fetchImpl: config.fetchImpl,\n sleepImpl: config.sleepImpl,\n }),\n });\n const req = createAppRequester({ baseUrl, tokens, fetchImpl: config.fetchImpl, sleepImpl: config.sleepImpl });\n\n return {\n appId: config.appId,\n tenantOrgId: config.tenantOrgId,\n request: req,\n getAppToken: (force = false) => tokens.getToken(force),\n };\n}\n\n/** Config for {@link createGraph8ServiceClient} (backend / server-to-server). */\nexport interface Graph8ServiceClientConfig {\n /** Service credential client id. */\n clientId: string;\n /** Service credential client secret (checked against the stored hash). */\n clientSecret: string;\n /** The single consented client org this backend acts for (sent as X-Target-Org-Id). */\n tenantOrgId: string;\n /** Scopes to embed in the minted service token. */\n scopes?: string[];\n /** Requested token TTL; the server clamps it into its service band. */\n ttlSeconds?: number;\n /** Backend API URL (default https://be.graph8.com). */\n apiUrl?: string;\n /** Refresh this many ms before expiry (default 60_000). */\n refreshSkewMs?: number;\n /** Injected for tests; defaults to global fetch. */\n fetchImpl?: typeof fetch;\n /** Injected for tests; defaults to a real setTimeout sleep. */\n sleepImpl?: (ms: number) => Promise<void>;\n /** Injected for tests; defaults to Date.now. */\n now?: () => number;\n}\n\n/** A backend service app-platform client. */\nexport interface Graph8ServiceClient {\n readonly tenantOrgId: string;\n /** Token-bound requester: mints/refreshes the service token, attaches it, and\n * sends `X-Target-Org-Id` for the one consented tenant on every call. */\n readonly request: AppRequest;\n /** Return the current service token, refreshing on/near expiry (`force` refreshes now). */\n getServiceToken(force?: boolean): Promise<string>;\n}\n\n/**\n * Create a BACKEND service client backed by client-credentials->service-token\n * exchange. Every request carries `X-Target-Org-Id: <tenantOrgId>` for the one\n * configured consented tenant.\n */\nexport function createGraph8ServiceClient(config: Graph8ServiceClientConfig): Graph8ServiceClient {\n if (!config?.clientId || !config?.clientSecret) {\n throw new Error(\"createGraph8ServiceClient requires clientId and clientSecret\");\n }\n if (!config?.tenantOrgId) {\n throw new Error(\n \"createGraph8ServiceClient requires tenantOrgId — the single consented client org this backend acts for\",\n );\n }\n\n const baseUrl = config.apiUrl || DEFAULT_APP_API;\n const tokens = createTokenManager({\n now: config.now,\n refreshSkewMs: config.refreshSkewMs,\n refresh: () =>\n exchangeServiceToken({\n baseUrl,\n clientId: config.clientId,\n clientSecret: config.clientSecret,\n scopes: config.scopes,\n ttlSeconds: config.ttlSeconds,\n fetchImpl: config.fetchImpl,\n sleepImpl: config.sleepImpl,\n }),\n });\n const req = createAppRequester({\n baseUrl,\n tokens,\n extraHeaders: { \"X-Target-Org-Id\": config.tenantOrgId },\n fetchImpl: config.fetchImpl,\n sleepImpl: config.sleepImpl,\n });\n\n return {\n tenantOrgId: config.tenantOrgId,\n request: req,\n getServiceToken: (force = false) => tokens.getToken(force),\n };\n}\n"],"mappings":"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","names":["DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","request","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","module"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/core.ts","../src/forms.ts","../src/utils.ts","../src/visitors.ts","../src/copilot.ts","../src/chat.ts","../src/calendar.ts","../src/http.ts","../src/enrich.ts","../src/sequences.ts","../src/campaigns.ts","../src/signals.ts","../src/voice.ts","../src/webhooks.ts","../src/contacts.ts","../src/companies.ts","../src/lists.ts","../src/notes.ts","../src/tasks.ts","../src/apps.ts","../src/fields.ts","../src/objects.ts","../src/deals.ts","../src/inbox.ts","../src/quotes.ts","../src/pipelines.ts","../src/workflows.ts","../src/skills.ts","../src/intent.ts","../src/studio.ts","../src/meetings.ts","../src/audiences.ts","../src/search.ts","../src/agency.ts","../src/marketplace.ts","../src/snippet.ts","../src/appTokens.ts","../src/appClient.ts"],"sourcesContent":["export { g8 } from \"./core\";\n\n// HTTP core (B2): typed errors, automatic retries, idempotency, auto-pagination.\nexport { G8Error, request, paginate, isRetryableStatus, parseRetryAfter, backoffDelayMs } from \"./http\";\nexport type { RequestOptions, PaginatedResponse } from \"./http\";\n\n// Core types\nexport type { G8Config, G8PrivacyConfig, TrackProperties, IdentifyProperties, EnrichLookupResult } from \"./types\";\n\n// Module types\nexport type { VisitorCompany, VisitorScore } from \"./visitors\";\nexport type { CopilotConfig } from \"./copilot\";\nexport type { ChatConfig } from \"./chat\";\nexport type { CalendarConfig, TimeSlot, Booking, BookingRequest } from \"./calendar\";\nexport type { PersonEnrichment, CompanyEnrichment, EmailVerification, SearchFilter, SearchResults } from \"./enrich\";\nexport type {\n Sequence,\n AddToSequenceConfig,\n SequenceListItem,\n SequenceListParams,\n SequenceDetail,\n SequenceContactItem,\n SequenceContactsParams,\n SequenceActionResult,\n SequenceKind,\n SequenceStepType,\n SequenceStepInputType,\n SequenceStepConfig,\n SequenceChannelConfig,\n SequenceCreateParams,\n SequenceCreateResult,\n SequenceUpdateParams,\n SequenceStepUpdateParams,\n SequencePreviewStep,\n SequencePreviewChannel,\n SequencePreview,\n SequenceAnalytics,\n} from \"./sequences\";\nexport type {\n InboxChannel,\n InboxContact,\n InboxTag,\n InboxAssignee,\n InboxMessage,\n InboxThread,\n InboxListParams,\n InboxAssignResult,\n InboxTagResult,\n InboxDraft,\n InboxSendParams,\n InboxSendResult,\n} from \"./inbox\";\nexport type {\n Campaign,\n CampaignCreateConfig,\n CampaignLaunchExecution,\n CampaignLaunchResult,\n} from \"./campaigns\";\nexport type {\n App,\n AppInstallation,\n AppUsageSummary,\n AppLimit,\n AppCreateParams,\n AppStatus,\n InstallStatus,\n} from \"./apps\";\nexport type {\n CustomObject,\n CustomObjectAttribute,\n CustomObjectRecord,\n CustomObjectHistory,\n CustomObjectHistoryEntry,\n ListRecordsParams,\n ObjectPagination,\n} from \"./objects\";\nexport type { IntentSignals } from \"./signals\";\nexport type {\n VoicePagination,\n DialerSessionSummary,\n DialerSessionsListResult,\n DialerSessionsListParams,\n DialerStatsParams,\n DialerReportFilters,\n DialerReportMetric,\n DialerStatsResult,\n DialerNumberInfo,\n DialerNumbersListResult,\n MissedCallback,\n MissedCallbacksResult,\n CallGradingResult,\n DialerAgentSummary,\n DialerAgentsListResult,\n DialerAgentsListParams,\n DialerSessionCreateParams,\n DialerSessionCreateResult,\n DialerSessionStatus,\n DialerSessionStatusUpdateResult,\n DialerSessionResumeResult,\n} from \"./voice\";\nexport type { WebhookEvent, WebhookEventPayload, ConstructEventOptions } from \"./webhooks\";\nexport { constructEvent, WebhookSignatureError, KNOWN_WEBHOOK_EVENTS } from \"./webhooks\";\nexport type {\n Contact,\n ContactListParams,\n ContactCreateParams,\n ContactUpdateParams,\n ContactColumn,\n ContactColumnCreateParams,\n} from \"./contacts\";\nexport type {\n Company,\n CompanyListParams,\n CompanyUpdateParams,\n CompanyContact,\n CompanyColumn,\n CompanyColumnCreateParams,\n} from \"./companies\";\nexport type { ContactList, ListContact } from \"./lists\";\nexport type { Note } from \"./notes\";\nexport type { Task, TaskCreateParams, TaskUpdateParams, TaskListParams } from \"./tasks\";\nexport type {\n Field,\n CreatedField,\n FieldCreateParams,\n FieldDeleteParams,\n SetFieldValueParams,\n} from \"./fields\";\nexport type {\n Deal,\n DealCreateParams,\n DealUpdateParams,\n DealListParams,\n Pipeline,\n PipelineStage,\n ContactDeal,\n PaginationMeta,\n} from \"./deals\";\nexport type {\n QuoteSummary,\n QuoteDetail,\n QuoteLineItem,\n QuoteStatus,\n QuoteCreateParams,\n QuoteUpdateParams,\n QuoteSendParams,\n QuoteListParams,\n QuotableProduct,\n QuoteSettings,\n} from \"./quotes\";\nexport type {\n StagePipeline,\n StagePipelineStage,\n StagePipelineCreateParams,\n StagePipelineUpdateParams,\n StageCreateParams,\n StageUpdateParams,\n EvidenceKey,\n PipelineSuggestion,\n} from \"./pipelines\";\nexport type {\n Workflow,\n WorkflowNode,\n WorkflowConnection,\n WorkflowConfig,\n WorkflowCreateParams,\n WorkflowUpdateParams,\n WorkflowExecution,\n WorkflowListParams,\n NodeTypeSchema,\n} from \"./workflows\";\nexport type {\n Skill,\n SkillType,\n SkillInputField,\n SkillCreateLLMParams,\n SkillCreateAPIParams,\n SkillUpdateLLMParams,\n SkillUpdateAPIParams,\n SkillTemplate,\n SkillListParams,\n} from \"./skills\";\nexport type {\n IntentKeyword,\n IntentPage,\n IntentVisitor,\n IntentCompany,\n IntentContact,\n IntentStats,\n} from \"./intent\";\nexport type {\n GlobalContextDocument,\n Persona,\n ICP,\n IntelligenceData,\n ResearchReport,\n} from \"./studio\";\nexport type {\n MeetingSummary,\n MeetingDetail,\n MeetingAttendee,\n MeetingTranscriptLine,\n MeetingAnalysis,\n MeetingListParams,\n} from \"./meetings\";\nexport type {\n AudienceSync,\n AudienceSyncPlatform,\n AudienceSyncMode,\n AudienceSyncCreateParams,\n AudienceSyncUpdateParams,\n AudienceSyncRun,\n AudienceSyncError,\n} from \"./audiences\";\nexport type {\n SearchOperator,\n SearchCondition,\n SearchParams,\n SearchSaveParams,\n SearchContactItem,\n SearchCompanyItem,\n SearchSaveResult,\n} from \"./search\";\nexport type { AgencyInfo, AgencyClient } from \"./agency\";\nexport type { MarketplaceProfile, MarketplaceOffer, MarketplaceHiring } from \"./marketplace\";\nexport type { Snippet } from \"./snippet\";\n\n// App Platform (M6-9): hosted-app browser + service auth clients over\n// short-lived app tokens. Additive — independent of the g8 tracking/API-key\n// singleton. Each client exposes a token-bound `request()` that attaches the\n// minted app token to Developer API calls. The typed `apps` (control-plane) and\n// `objects` (custom-object CRUD) resource modules are DEFERRED until their\n// backend routes ship and freeze (M6-3 / M6-5).\nexport { createGraph8AppClient, createGraph8ServiceClient } from \"./appClient\";\nexport type {\n Graph8AppClient,\n Graph8AppClientConfig,\n Graph8ServiceClient,\n Graph8ServiceClientConfig,\n} from \"./appClient\";\nexport {\n createTokenManager,\n createAppRequester,\n exchangeBrowserToken,\n exchangeServiceToken,\n DEFAULT_APP_API,\n} from \"./appTokens\";\nexport type { AppTokenResponse, TokenManager, AppRequest, AppRequestOptions } from \"./appTokens\";\n","import { jitsuAnalytics } from \"@jitsu/js\";\nimport type { AnalyticsInterface } from \"@jitsu/js\";\nimport type { G8Config, TrackProperties, IdentifyProperties } from \"./types\";\nimport { createFormsClient } from \"./forms\";\nimport { createVisitorsClient } from \"./visitors\";\nimport { createCopilotClient } from \"./copilot\";\nimport { createChatClient } from \"./chat\";\nimport { createCalendarClient } from \"./calendar\";\nimport { createEnrichClient } from \"./enrich\";\nimport { createSequencesClient } from \"./sequences\";\nimport { createCampaignsClient } from \"./campaigns\";\nimport { createSignalsClient } from \"./signals\";\nimport { createVoiceClient } from \"./voice\";\nimport { createWebhooksClient } from \"./webhooks\";\nimport { createContactsClient } from \"./contacts\";\nimport { createCompaniesClient } from \"./companies\";\nimport { createListsClient } from \"./lists\";\nimport { createNotesClient } from \"./notes\";\nimport { createTasksClient } from \"./tasks\";\nimport { createAppsClient } from \"./apps\";\nimport { createFieldsClient } from \"./fields\";\nimport { createObjectsClient } from \"./objects\";\nimport { createDealsClient } from \"./deals\";\nimport { createInboxClient } from \"./inbox\";\nimport { createQuotesClient } from \"./quotes\";\nimport { createPipelinesClient } from \"./pipelines\";\nimport { createWorkflowsClient } from \"./workflows\";\nimport { createSkillsClient } from \"./skills\";\nimport { createIntentClient } from \"./intent\";\nimport { createStudioClient } from \"./studio\";\nimport { createMeetingsClient } from \"./meetings\";\nimport { createAudiencesClient } from \"./audiences\";\nimport { createSearchClient } from \"./search\";\nimport { createAgencyClient } from \"./agency\";\nimport { createMarketplaceClient } from \"./marketplace\";\nimport { createSnippetClient } from \"./snippet\";\nimport { isServer } from \"./utils\";\n\nconst DEFAULT_HOST = \"https://t.graph8.com\";\nconst DEFAULT_API = \"https://be.graph8.com\";\n\n/**\n * graph8 SDK client.\n *\n * Handles event tracking, identity, and progressive forms.\n *\n * Usage:\n * import { g8 } from '@graph8/sdk';\n * g8.init({ writeKey: 'your_write_key' });\n * g8.track('page_view', { page: '/pricing' });\n * g8.identify('user@acme.com', { name: 'John', company: 'Acme' });\n */\nclass G8 {\n /** @internal */ client: AnalyticsInterface | null = null;\n /** @internal */ config: G8Config | null = null;\n /** @internal */ _forms: ReturnType<typeof createFormsClient> | null = null;\n /** @internal */ _visitors: ReturnType<typeof createVisitorsClient> | null = null;\n /** @internal */ _copilot: ReturnType<typeof createCopilotClient> | null = null;\n /** @internal */ _chat: ReturnType<typeof createChatClient> | null = null;\n /** @internal */ _calendar: ReturnType<typeof createCalendarClient> | null = null;\n /** @internal */ _enrich: ReturnType<typeof createEnrichClient> | null = null;\n /** @internal */ _sequences: ReturnType<typeof createSequencesClient> | null = null;\n /** @internal */ _campaigns: ReturnType<typeof createCampaignsClient> | null = null;\n /** @internal */ _signals: ReturnType<typeof createSignalsClient> | null = null;\n /** @internal */ _voice: ReturnType<typeof createVoiceClient> | null = null;\n /** @internal */ _webhooks: ReturnType<typeof createWebhooksClient> | null = null;\n /** @internal */ _contacts: ReturnType<typeof createContactsClient> | null = null;\n /** @internal */ _companies: ReturnType<typeof createCompaniesClient> | null = null;\n /** @internal */ _lists: ReturnType<typeof createListsClient> | null = null;\n /** @internal */ _notes: ReturnType<typeof createNotesClient> | null = null;\n /** @internal */ _tasks: ReturnType<typeof createTasksClient> | null = null;\n /** @internal */ _fields: ReturnType<typeof createFieldsClient> | null = null;\n /** @internal */ _apps: ReturnType<typeof createAppsClient> | null = null;\n /** @internal */ _objects: ReturnType<typeof createObjectsClient> | null = null;\n /** @internal */ _deals: ReturnType<typeof createDealsClient> | null = null;\n /** @internal */ _inbox: ReturnType<typeof createInboxClient> | null = null;\n /** @internal */ _quotes: ReturnType<typeof createQuotesClient> | null = null;\n /** @internal */ _pipelines: ReturnType<typeof createPipelinesClient> | null = null;\n /** @internal */ _workflows: ReturnType<typeof createWorkflowsClient> | null = null;\n /** @internal */ _skills: ReturnType<typeof createSkillsClient> | null = null;\n /** @internal */ _intent: ReturnType<typeof createIntentClient> | null = null;\n /** @internal */ _studio: ReturnType<typeof createStudioClient> | null = null;\n /** @internal */ _meetings: ReturnType<typeof createMeetingsClient> | null = null;\n /** @internal */ _audiences: ReturnType<typeof createAudiencesClient> | null = null;\n /** @internal */ _search: ReturnType<typeof createSearchClient> | null = null;\n /** @internal */ _agency: ReturnType<typeof createAgencyClient> | null = null;\n /** @internal */ _marketplace: ReturnType<typeof createMarketplaceClient> | null = null;\n /** @internal */ _snippet: ReturnType<typeof createSnippetClient> | null = null;\n\n /**\n * Initialize the graph8 SDK. Must be called before any other method.\n * Safe to call on the server (SSR) - becomes a no-op for tracking.\n */\n init(config: G8Config): void {\n this.config = config;\n\n if (!isServer) {\n this.client = jitsuAnalytics({\n host: config.host || DEFAULT_HOST,\n writeKey: config.writeKey || \"\",\n debug: config.debug,\n });\n }\n\n const apiUrl = config.apiUrl || DEFAULT_API;\n const writeKey = config.writeKey || \"\";\n const apiKey = config.apiKey || \"\";\n\n // Public (write key) clients\n if (writeKey) {\n this._forms = createFormsClient(writeKey, apiUrl);\n this._visitors = createVisitorsClient(writeKey, apiUrl);\n this._copilot = createCopilotClient(writeKey, apiUrl);\n this._chat = createChatClient(writeKey, apiUrl);\n this._calendar = createCalendarClient(apiUrl);\n this._signals = createSignalsClient(writeKey, false, apiUrl);\n }\n\n // Private (API key) clients\n if (apiKey) {\n this._enrich = createEnrichClient(apiKey, apiUrl);\n this._sequences = createSequencesClient(apiKey, apiUrl);\n this._campaigns = createCampaignsClient(apiKey, apiUrl);\n this._voice = createVoiceClient(apiKey, apiUrl);\n this._webhooks = createWebhooksClient(apiKey, apiUrl);\n this._contacts = createContactsClient(apiKey, apiUrl);\n this._companies = createCompaniesClient(apiKey, apiUrl);\n this._lists = createListsClient(apiKey, apiUrl);\n this._notes = createNotesClient(apiKey, apiUrl);\n this._tasks = createTasksClient(apiKey, apiUrl);\n this._fields = createFieldsClient(apiKey, apiUrl);\n this._apps = createAppsClient(apiKey, apiUrl);\n this._objects = createObjectsClient(apiKey, apiUrl);\n this._deals = createDealsClient(apiKey, apiUrl);\n this._inbox = createInboxClient(apiKey, apiUrl);\n this._quotes = createQuotesClient(apiKey, apiUrl);\n this._pipelines = createPipelinesClient(apiKey, apiUrl);\n this._workflows = createWorkflowsClient(apiKey, apiUrl);\n this._skills = createSkillsClient(apiKey, apiUrl);\n this._intent = createIntentClient(apiKey, apiUrl);\n this._studio = createStudioClient(apiKey, apiUrl);\n this._meetings = createMeetingsClient(apiKey, apiUrl);\n this._audiences = createAudiencesClient(apiKey, apiUrl);\n this._search = createSearchClient(apiKey, apiUrl);\n this._agency = createAgencyClient(apiKey, apiUrl);\n this._marketplace = createMarketplaceClient(apiKey, apiUrl);\n this._snippet = createSnippetClient(apiKey, apiUrl);\n // Override signals with API key version for full access\n this._signals = createSignalsClient(apiKey, true, apiUrl);\n }\n }\n\n // -- Core tracking (via Jitsu) --\n\n /** Track a custom event. */\n track(event: string, properties?: TrackProperties): void { this.client?.track(event, properties); }\n\n /** Identify a user with properties. */\n identify(userId: string, properties?: IdentifyProperties): void { this.client?.identify(userId, properties); }\n\n /** Track a page view. */\n page(properties?: TrackProperties): void { this.client?.page(properties); }\n\n /** Clear user identity (on logout). */\n reset(): void { this.client?.reset(); }\n\n // -- Module accessors --\n\n /** Progressive form helpers. */\n get forms() { this._assertInit(); return this._forms!; }\n\n /** Visitor intelligence - IP to company, engagement scoring. */\n get visitors() { this._assertInit(); return this._visitors!; }\n\n /** AI copilot widget. */\n get copilot() { this._assertInit(); return this._copilot!; }\n\n /** Webchat widget. */\n get chat() { this._assertInit(); return this._chat!; }\n\n /** Calendar and booking. */\n get calendar() { this._assertInit(); return this._calendar!; }\n\n /** Enrichment API (requires API key). */\n get enrich() { this._assertKey(\"enrich\"); return this._enrich!; }\n\n /** Sequences (requires API key). */\n get sequences() { this._assertKey(\"sequences\"); return this._sequences!; }\n\n /** Campaigns (requires API key). */\n get campaigns() { this._assertKey(\"campaigns\"); return this._campaigns!; }\n\n /** Intent signals. */\n get signals() { this._assertInit(); return this._signals!; }\n\n /** Voice AI (requires API key). */\n get voice() { this._assertKey(\"voice\"); return this._voice!; }\n\n /** Webhook event listeners (requires API key). */\n get webhooks() { this._assertKey(\"webhooks\"); return this._webhooks!; }\n\n /** Contacts CRUD (requires API key). */\n get contacts() { this._assertKey(\"contacts\"); return this._contacts!; }\n\n /** Companies CRUD (requires API key). */\n get companies() { this._assertKey(\"companies\"); return this._companies!; }\n\n /** Lists management (requires API key). */\n get lists() { this._assertKey(\"lists\"); return this._lists!; }\n\n /** Notes on contacts (requires API key). */\n get notes() { this._assertKey(\"notes\"); return this._notes!; }\n\n /** Tasks on contacts (requires API key). */\n get tasks() { this._assertKey(\"tasks\"); return this._tasks!; }\n\n /** Custom fields management (requires API key). */\n get fields() { this._assertKey(\"fields\"); return this._fields!; }\n\n /** Apps you build on graph8 — lifecycle, installs, usage, limits (requires API key). PREVIEW. */\n get apps() { this._assertKey(\"apps\"); return this._apps!; }\n\n /** Custom object types, their schema, and their records (requires API key). PREVIEW. */\n get objects() { this._assertKey(\"objects\"); return this._objects!; }\n\n /** Deals and pipelines (requires API key). */\n get deals() { this._assertKey(\"deals\"); return this._deals!; }\n\n /** Multi-channel inbox — read + reply across email, SMS, LinkedIn (requires API key). */\n get inbox() { this._assertKey(\"inbox\"); return this._inbox!; }\n\n /** Quote-to-cash: draft, send, sign, payment-link quotes (requires API key). */\n get quotes() { this._assertKey(\"quotes\"); return this._quotes!; }\n\n /** Stage Checklist v2 pipelines: workflow stages with evidence + scripts (requires API key). */\n get pipelines() { this._assertKey(\"pipelines\"); return this._pipelines!; }\n\n /** Workflow builder — multi-node automation graphs with execution lifecycle (requires API key). */\n get workflows() { this._assertKey(\"workflows\"); return this._workflows!; }\n\n /** Skill authoring — LLM and API building blocks that workflows compose (requires API key). */\n get skills() { this._assertKey(\"skills\"); return this._skills!; }\n\n /** Intent tracking — keyword groups, page visitors, account-level intent (requires API key). */\n get intent() { this._assertKey(\"intent\"); return this._intent!; }\n\n /** Studio context — ICPs, personas, brand briefs, intelligence, AI research reports (requires API key). */\n get studio() { this._assertKey(\"studio\"); return this._studio!; }\n\n /** Meetings — read scheduled, completed, cancelled meetings with transcripts + AI analysis (requires API key). */\n get meetings() { this._assertKey(\"meetings\"); return this._meetings!; }\n\n /** Audiences — sync audience lists to ad platforms (Meta, LinkedIn, Google, X) (requires API key). */\n get audiences() { this._assertKey(\"audiences\"); return this._audiences!; }\n\n /** Search — prospect open-data contacts + companies by filter, optionally save to a list (requires API key). */\n get search() { this._assertKey(\"search\"); return this._search!; }\n\n /** Agency — for agency keys: discover the agency credential + the client orgs it may target (requires API key). */\n get agency() { this._assertKey(\"agency\"); return this._agency!; }\n\n /** Marketplace — for SDR/AE talent: profile, hire offers (accept/reject), active hirings (requires API key). */\n get marketplace() { this._assertKey(\"marketplace\"); return this._marketplace!; }\n\n /** Snippet — fetch your org's tracking snippet (write key + React/script-tag embeds + config) for embedding (requires API key). */\n get snippet() { this._assertKey(\"snippet\"); return this._snippet!; }\n\n /** Whether the SDK has been initialized. */\n get initialized(): boolean { return this.config !== null; }\n\n /** @internal */\n _assertInit(): void {\n if (!this.config) throw new Error(\"g8.init() must be called first\");\n }\n\n /** @internal */\n _assertKey(module: string): void {\n this._assertInit();\n if (!this.config!.apiKey) throw new Error(`g8.${module} requires an API key. Use g8.init({ apiKey: '...' })`);\n }\n}\n\n/** Singleton g8 client instance. */\nexport const g8 = new G8();\n","import type { EnrichLookupResult } from \"./types\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\n/**\n * Progressive form enrichment - checks what fields graph8 already knows\n * about a contact so your form can skip those fields.\n *\n * Calls POST /api/v1/public/enrich/lookup (authenticated via write key).\n */\nexport const createFormsClient = (writeKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n /**\n * Look up known fields for an email address.\n *\n * Use this in progressive forms: after the user enters their email,\n * check what graph8 already knows and only show missing fields.\n */\n async lookup(email: string): Promise<EnrichLookupResult> {\n const resp = await fetch(`${baseUrl}/api/v1/public/enrich/lookup`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Write-Key\": writeKey,\n },\n body: JSON.stringify({ email }),\n });\n\n if (!resp.ok) {\n return { found: false, known_fields: {}, missing_fields: [] };\n }\n\n const data = await resp.json();\n return data.data || data;\n },\n };\n};\n","/** Check if running on the server (SSR). */\nexport const isServer = typeof window === \"undefined\";\n\n/** Safely get the window object (returns undefined on server). */\nexport const getWindow = (): Window | undefined =>\n isServer ? undefined : window;\n\n/**\n * Derive the APP origin (which serves the embed pages) from the API origin.\n *\n * `apiUrl` addresses the FastAPI backend — `be.graph8.com` in production — and\n * that host does not serve any page routes: `/webchat/embed` and\n * `/copilot/embed` are Next.js pages on `app.graph8.com`. The two hosts are\n * disjoint, so a single base URL cannot address both; an iframe pointed at\n * `${apiUrl}/webchat/embed` renders the backend's `{\"detail\":\"Not Found\"}`.\n *\n * This is the inverse of `detectApiBaseUrl` in\n * `frontend/chat-script/src/config.ts`, which maps `app.` → `be.` to find the\n * API from the page. Same host convention, read the other way.\n *\n * Anything that is not a recognised graph8 or localhost origin is returned\n * unchanged, so a self-hosted deployment keeps serving both from one host.\n */\nexport const resolveAppBaseUrl = (apiBaseUrl: string): string => {\n try {\n const url = new URL(apiBaseUrl);\n if (url.hostname === \"localhost\" || url.hostname === \"127.0.0.1\") {\n // Backend dev ports (8000 http / 8011 https) → Next dev server on 3000.\n url.port = \"3000\";\n return url.origin;\n }\n if (url.hostname.endsWith(\"graph8.com\")) {\n url.hostname = url.hostname.replace(/^be\\./, \"app.\");\n return url.origin;\n }\n } catch {\n // Not a parseable URL — leave the caller's value alone.\n }\n return apiBaseUrl;\n};\n","import type { G8Config } from \"./types\";\nimport { isServer } from \"./utils\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface VisitorCompany {\n company_name: string | null;\n company_domain: string | null;\n industry: string | null;\n employee_count: string | null;\n city: string | null;\n country: string | null;\n confidence: number;\n}\n\nexport interface VisitorScore {\n engagement: number;\n intent: \"low\" | \"medium\" | \"high\";\n signals: string[];\n}\n\ntype IntentCallback = (visitor: VisitorCompany & VisitorScore) => void;\n\n/**\n * Visitor intelligence - identify anonymous visitors by IP → company.\n *\n * Requires write key auth. Works client-side only.\n */\nexport const createVisitorsClient = (writeKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n let intentInterval: ReturnType<typeof setInterval> | null = null;\n\n const headers = () => ({\n \"Content-Type\": \"application/json\",\n \"X-Write-Key\": writeKey,\n });\n\n return {\n /** Identify the current visitor's company from their IP address. */\n async identify(): Promise<VisitorCompany> {\n if (isServer) return { company_name: null, company_domain: null, industry: null, employee_count: null, city: null, country: null, confidence: 0 };\n\n const resp = await fetch(`${baseUrl}/api/v1/public/visitors/company`, { headers: headers() });\n if (!resp.ok) return { company_name: null, company_domain: null, industry: null, employee_count: null, city: null, country: null, confidence: 0 };\n const data = await resp.json();\n return data.data || data;\n },\n\n /** Get engagement score for the current visitor. */\n async score(): Promise<VisitorScore> {\n if (isServer) return { engagement: 0, intent: \"low\", signals: [] };\n\n const resp = await fetch(`${baseUrl}/api/v1/public/visitors/score`, { headers: headers() });\n if (!resp.ok) return { engagement: 0, intent: \"low\", signals: [] };\n const data = await resp.json();\n return data.data || data;\n },\n\n /** Listen for visitors matching an intent level. Polls every 30 seconds. */\n onIntent(level: \"low\" | \"medium\" | \"high\", callback: IntentCallback): () => void {\n if (isServer) return () => {};\n\n const check = async () => {\n try {\n const [company, scoreData] = await Promise.all([\n this.identify(),\n this.score(),\n ]);\n if (scoreData.intent === level || (level === \"medium\" && scoreData.intent === \"high\")) {\n callback({ ...company, ...scoreData });\n }\n } catch { /* silent */ }\n };\n\n check();\n intentInterval = setInterval(check, 30_000);\n\n return () => {\n if (intentInterval) clearInterval(intentInterval);\n };\n },\n };\n};\n","import { isServer } from \"./utils\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface CopilotConfig {\n /** Initial greeting message */\n greeting?: string;\n /** Context passed to the AI */\n context?: Record<string, unknown>;\n /** Widget position */\n position?: \"bottom-right\" | \"bottom-left\";\n /** Widget theme */\n theme?: \"light\" | \"dark\" | \"auto\";\n}\n\ntype CopilotEvent = \"message\" | \"tool_used\" | \"open\" | \"close\" | \"error\";\ntype CopilotCallback = (data: Record<string, unknown>) => void;\ntype ActionHandler = (params: Record<string, unknown>) => void | Promise<void>;\n\n/**\n * Copilot - embeddable AI assistant with org knowledge base access.\n *\n * Client-side (write key) for the widget embed.\n */\nexport const createCopilotClient = (writeKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n const listeners: Map<CopilotEvent, CopilotCallback[]> = new Map();\n const actions: Map<string, ActionHandler> = new Map();\n let widgetEl: HTMLElement | null = null;\n\n const emit = (event: CopilotEvent, data: Record<string, unknown> = {}) => {\n listeners.get(event)?.forEach((cb) => cb(data));\n };\n\n return {\n /** Open the copilot widget. */\n open(config?: CopilotConfig): void {\n if (isServer) return;\n\n // Remove existing widget if any\n if (widgetEl) widgetEl.remove();\n\n const position = config?.position || \"bottom-right\";\n const posStyle = position === \"bottom-left\" ? \"left:16px;\" : \"right:16px;\";\n\n const iframe = document.createElement(\"iframe\");\n iframe.src = `${baseUrl}/copilot/embed?write_key=${writeKey}&theme=${config?.theme || \"auto\"}`;\n iframe.style.cssText = `position:fixed;bottom:16px;${posStyle}width:400px;height:600px;border:none;border-radius:12px;box-shadow:0 8px 32px rgba(0,0,0,0.15);z-index:99998;`;\n iframe.id = \"g8-copilot-widget\";\n\n document.body.appendChild(iframe);\n widgetEl = iframe;\n emit(\"open\");\n },\n\n /** Send a message to the copilot programmatically. */\n async ask(message: string): Promise<string> {\n const resp = await fetch(`${baseUrl}/api/v1/public/copilot/chat`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", \"X-Write-Key\": writeKey },\n body: JSON.stringify({ message }),\n });\n if (!resp.ok) return \"Sorry, I could not process that request.\";\n const data = await resp.json();\n return data.response || data.data?.response || \"\";\n },\n\n /** Register a custom action the copilot can trigger. */\n registerAction(name: string, handler: ActionHandler): void {\n actions.set(name, handler);\n },\n\n /** Listen for copilot events. */\n on(event: CopilotEvent, callback: CopilotCallback): void {\n if (!listeners.has(event)) listeners.set(event, []);\n listeners.get(event)!.push(callback);\n },\n\n /** Close the copilot widget. */\n close(): void {\n if (widgetEl) {\n widgetEl.remove();\n widgetEl = null;\n emit(\"close\");\n }\n },\n };\n};\n","import { isServer, resolveAppBaseUrl } from \"./utils\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface ChatConfig {\n /** Agent ID to connect to */\n agentId?: string;\n /** Pre-filled first message */\n message?: string;\n /** Widget position */\n position?: \"bottom-right\" | \"bottom-left\";\n /** Widget theme */\n theme?: \"light\" | \"dark\" | \"auto\";\n /** Custom greeting */\n greeting?: string;\n /** Custom avatar URL */\n avatar?: string;\n}\n\ntype ChatEvent = \"message\" | \"human_transfer\" | \"open\" | \"close\" | \"error\";\ntype ChatCallback = (data: Record<string, unknown>) => void;\n\n/**\n * Webchat - live chat + AI chat embedded in any app.\n *\n * Uses WebSocket connection to the voice/chat agent backend.\n * Public - no API key needed (org/agent IDs are in the config).\n */\nexport const createChatClient = (writeKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n // The embed page is a Next.js route on the APP host, not the API host.\n const appUrl = resolveAppBaseUrl(baseUrl);\n const listeners: Map<ChatEvent, ChatCallback[]> = new Map();\n let widgetEl: HTMLElement | null = null;\n let ws: WebSocket | null = null;\n\n const emit = (event: ChatEvent, data: Record<string, unknown> = {}) => {\n listeners.get(event)?.forEach((cb) => cb(data));\n };\n\n return {\n /** Open the chat widget. */\n open(config?: ChatConfig): void {\n if (isServer) return;\n\n if (widgetEl) widgetEl.remove();\n\n const position = config?.position || \"bottom-right\";\n const posStyle = position === \"bottom-left\" ? \"left:16px;\" : \"right:16px;\";\n\n const iframe = document.createElement(\"iframe\");\n iframe.src = `${appUrl}/webchat/embed?write_key=${writeKey}&theme=${config?.theme || \"auto\"}`;\n iframe.style.cssText = `position:fixed;bottom:16px;${posStyle}width:380px;height:560px;border:none;border-radius:12px;box-shadow:0 8px 32px rgba(0,0,0,0.15);z-index:99997;`;\n iframe.id = \"g8-chat-widget\";\n\n document.body.appendChild(iframe);\n widgetEl = iframe;\n emit(\"open\");\n },\n\n /** Send a message. */\n send(message: string): void {\n if (ws && ws.readyState === WebSocket.OPEN) {\n ws.send(JSON.stringify({ type: \"message\", content: message }));\n }\n },\n\n /** Listen for chat events. */\n on(event: ChatEvent, callback: ChatCallback): void {\n if (!listeners.has(event)) listeners.set(event, []);\n listeners.get(event)!.push(callback);\n },\n\n /** Configure the chat appearance. */\n configure(config: Partial<ChatConfig>): void {\n // Send config to iframe via postMessage\n if (widgetEl && widgetEl instanceof HTMLIFrameElement) {\n widgetEl.contentWindow?.postMessage({ type: \"g8_chat_config\", config }, \"*\");\n }\n },\n\n /** Close the chat widget. */\n close(): void {\n if (ws) { ws.close(); ws = null; }\n if (widgetEl) { widgetEl.remove(); widgetEl = null; }\n emit(\"close\");\n },\n };\n};\n","import { isServer } from \"./utils\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface CalendarConfig {\n /** Event type slug (e.g. \"demo-30min\") */\n eventType: string;\n /** Username of the host */\n username: string;\n /** Pre-fill attendee info */\n prefill?: { name?: string; email?: string; notes?: string };\n}\n\nexport interface TimeSlot {\n start: string;\n end: string;\n available: boolean;\n}\n\nexport interface Booking {\n uid: string;\n confirmation_url: string;\n start_time: string;\n end_time: string;\n}\n\nexport interface BookingRequest {\n event_type_id: number;\n slot: string;\n attendee: { name: string; email: string; notes?: string };\n}\n\ntype CalendarEvent = \"booked\" | \"cancelled\" | \"slot_selected\";\ntype CalendarCallback = (data: Record<string, unknown>) => void;\n\n/**\n * Calendar and booking - show scheduling widgets, get slots, book meetings.\n *\n * Public endpoints - no API key needed.\n */\nexport const createCalendarClient = (apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n const listeners: Map<CalendarEvent, CalendarCallback[]> = new Map();\n\n return {\n /** Show a booking modal overlay. */\n show(config: CalendarConfig): void {\n if (isServer) return;\n\n const url = `${baseUrl}/appointments/${config.username}/${config.eventType}`;\n const params = new URLSearchParams();\n if (config.prefill?.name) params.set(\"name\", config.prefill.name);\n if (config.prefill?.email) params.set(\"email\", config.prefill.email);\n\n const iframe = document.createElement(\"iframe\");\n iframe.src = `${url}?${params.toString()}`;\n iframe.style.cssText = \"position:fixed;top:0;left:0;width:100%;height:100%;border:none;z-index:99999;background:rgba(0,0,0,0.5);\";\n iframe.id = \"g8-calendar-modal\";\n document.body.appendChild(iframe);\n\n // Close on escape\n const onKey = (e: KeyboardEvent) => { if (e.key === \"Escape\") { iframe.remove(); document.removeEventListener(\"keydown\", onKey); } };\n document.addEventListener(\"keydown\", onKey);\n },\n\n /** Embed booking widget inline in a container element. */\n embed(selector: string, config: CalendarConfig): void {\n if (isServer) return;\n\n const container = document.querySelector(selector);\n if (!container) return;\n\n const url = `${baseUrl}/appointments/${config.username}/${config.eventType}`;\n const iframe = document.createElement(\"iframe\");\n iframe.src = url;\n iframe.style.cssText = \"width:100%;min-height:600px;border:none;border-radius:8px;\";\n container.appendChild(iframe);\n },\n\n /** Get available time slots for an event type. */\n async slots(username: string, eventSlug: string, range: { start: string; end: string }): Promise<TimeSlot[]> {\n const params = new URLSearchParams({\n start_date: range.start,\n end_date: range.end,\n });\n const resp = await fetch(`${baseUrl}/appointments/public/slots?username=${username}&event_slug=${eventSlug}&${params.toString()}`);\n if (!resp.ok) return [];\n const data = await resp.json();\n return data.slots || data.data || [];\n },\n\n /** Book a meeting programmatically. */\n async book(request: BookingRequest): Promise<Booking | null> {\n const resp = await fetch(`${baseUrl}/appointments/public/bookings`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(request),\n });\n if (!resp.ok) return null;\n const data = await resp.json();\n const emit = listeners.get(\"booked\");\n if (emit) emit.forEach((cb) => cb(data));\n return data;\n },\n\n /** Listen for calendar events. */\n on(event: CalendarEvent, callback: CalendarCallback): void {\n if (!listeners.has(event)) listeners.set(event, []);\n listeners.get(event)!.push(callback);\n },\n };\n};\n","/**\n * Hardened HTTP core for the graph8 JS SDK (sprint B2).\n *\n * Resource clients historically called `fetch` directly and returned\n * `resp.json()` even on a 4xx/5xx — so API errors surfaced silently as \"data\".\n * This module centralizes the request path with:\n *\n * - a typed `G8Error` (status, type, code, request_id, detail) thrown on any\n * non-2xx response, parsed from the standard ApiError envelope;\n * - automatic retry with exponential backoff + jitter on 429 and 5xx,\n * honoring the `Retry-After` header;\n * - `Idempotency-Key` support for safe POST retries (pairs with the backend\n * idempotency added in A6);\n * - a cursor `paginate()` async-iterator that follows `next_cursor` (A9).\n *\n * `fetch` and `sleep` are injectable so the retry/backoff logic is\n * deterministically unit-testable without real timers or network.\n */\n\nexport class G8Error extends Error {\n readonly status: number;\n readonly type: string;\n readonly code?: string;\n readonly requestId?: string;\n readonly detail?: unknown;\n /** True when the failure class is transient (429/5xx/network). */\n readonly retryable: boolean;\n\n constructor(args: {\n message: string;\n status: number;\n type: string;\n code?: string;\n requestId?: string;\n detail?: unknown;\n retryable?: boolean;\n }) {\n super(args.message);\n this.name = \"G8Error\";\n this.status = args.status;\n this.type = args.type;\n this.code = args.code;\n this.requestId = args.requestId;\n this.detail = args.detail;\n this.retryable = args.retryable ?? false;\n // Restore prototype chain for `instanceof` across the TS->ES5 downlevel.\n Object.setPrototypeOf(this, G8Error.prototype);\n }\n}\n\n/** 429 and 5xx are the transient classes worth retrying. */\nexport function isRetryableStatus(status: number): boolean {\n return status === 429 || (status >= 500 && status <= 599);\n}\n\n/**\n * Methods that are NOT safe to blind-retry.\n *\n * GET/HEAD/OPTIONS are safe, and PUT/DELETE are idempotent by their HTTP\n * definition — resending them cannot create a second thing. POST and PATCH can,\n * so a retry needs an `Idempotency-Key` to be safe.\n */\nfunction isNonIdempotentMethod(method: string): boolean {\n const m = method.toUpperCase();\n return m === \"POST\" || m === \"PATCH\";\n}\n\n/**\n * Mint an idempotency key for a request the SDK might retry on its own.\n *\n * THE BUG THIS FIXES: this client retries 429/5xx AND network errors for every\n * method, POST included, while `idempotencyKey` was optional and unset by\n * default. So a timeout or a 500 on `POST /contacts` would silently be retried\n * and could create the contact TWICE — a duplicate the caller never asked for\n * and could not see. Generating a key here means the server (which now honours\n * the header on every POST) collapses those retries into one operation.\n *\n * The key is generated ONCE per logical request and reused across that\n * request's retries — a fresh key per attempt would defeat the whole point.\n */\nfunction newIdempotencyKey(): string {\n const c: any = (globalThis as any).crypto;\n if (c?.randomUUID) return c.randomUUID();\n if (c?.getRandomValues) {\n const bytes = c.getRandomValues(new Uint8Array(16));\n return Array.from(bytes as Uint8Array, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n }\n // Last-resort fallback for exotic runtimes with no crypto at all. Weaker, but\n // still far better than retrying with no key: collisions only matter within\n // one credential's 24h replay window.\n return `g8-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 12)}`;\n}\n\n/**\n * Parse a `Retry-After` header into milliseconds. Supports both the\n * delta-seconds form (`\"2\"`) and the HTTP-date form. Returns null when absent\n * or unparseable so the caller falls back to computed backoff.\n */\nexport function parseRetryAfter(header: string | null | undefined, nowMs: number = Date.now()): number | null {\n if (!header) return null;\n const secs = Number(header);\n if (Number.isFinite(secs) && secs >= 0) return Math.round(secs * 1000);\n const dateMs = Date.parse(header);\n if (!Number.isNaN(dateMs)) {\n const delta = dateMs - nowMs;\n return delta > 0 ? delta : 0;\n }\n return null;\n}\n\n/** Exponential backoff with full jitter, capped at 10s. */\nexport function backoffDelayMs(attempt: number, baseMs = 200, rand: () => number = Math.random): number {\n const expo = Math.min(baseMs * 2 ** attempt, 10_000);\n return Math.floor(expo / 2 + rand() * (expo / 2));\n}\n\nfunction defaultSleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction buildQuery(query?: Record<string, unknown>): string {\n if (!query) return \"\";\n const qs = new URLSearchParams();\n for (const [k, v] of Object.entries(query)) {\n if (v != null) qs.set(k, String(v));\n }\n const s = qs.toString();\n return s ? `?${s}` : \"\";\n}\n\nasync function toG8Error(resp: Response): Promise<G8Error> {\n let body: unknown = undefined;\n try {\n const text = await resp.text();\n body = text ? JSON.parse(text) : undefined;\n } catch {\n body = undefined;\n }\n const env = (body ?? {}) as Record<string, unknown>;\n const message =\n (typeof env.message === \"string\" && env.message) ||\n (typeof env.error === \"string\" && env.error) ||\n `Request failed with status ${resp.status}`;\n return new G8Error({\n message,\n status: resp.status,\n type: (typeof env.type === \"string\" && env.type) || (typeof env.error === \"string\" && env.error) || \"api_error\",\n code: typeof env.code === \"string\" ? env.code : String(resp.status),\n requestId:\n (typeof env.request_id === \"string\" && env.request_id) || resp.headers.get(\"x-request-id\") || undefined,\n detail: env.detail,\n retryable: isRetryableStatus(resp.status),\n });\n}\n\nexport interface RequestOptions {\n method?: string;\n body?: unknown;\n headers?: Record<string, string>;\n query?: Record<string, unknown>;\n /** Sent as the `Idempotency-Key` header (safe POST retries; see A6). */\n idempotencyKey?: string;\n /** Max retry attempts on 429/5xx/network (default 2 -> up to 3 tries). */\n maxRetries?: number;\n /** Base backoff in ms (default 200). */\n retryBaseMs?: number;\n signal?: AbortSignal;\n /** Injected for tests; defaults to global fetch. */\n fetchImpl?: typeof fetch;\n /** Injected for tests; defaults to a real setTimeout sleep. */\n sleepImpl?: (ms: number) => Promise<void>;\n}\n\n/**\n * Perform a JSON request with retries + typed errors. Returns the parsed JSON\n * envelope (callers unwrap `.data` as today). Throws `G8Error` on any non-2xx\n * after exhausting retries.\n */\nexport async function request<T = unknown>(\n baseUrl: string,\n path: string,\n apiKey: string,\n opts: RequestOptions = {},\n): Promise<T> {\n const {\n method = \"GET\",\n body,\n headers = {},\n query,\n idempotencyKey,\n maxRetries = 2,\n retryBaseMs = 200,\n signal,\n fetchImpl = fetch,\n sleepImpl = defaultSleep,\n } = opts;\n\n const url = baseUrl.replace(/\\/+$/, \"\") + path + buildQuery(query);\n const finalHeaders: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n ...headers,\n };\n // An explicit key always wins. Otherwise mint one for any method this client\n // might retry unsafely, so OUR OWN retry cannot duplicate the caller's write.\n // Skipped entirely when retries are disabled (maxRetries === 0) — with no\n // retry there is nothing to make safe, and the header stays off the wire for\n // callers who never wanted it.\n const effectiveIdempotencyKey =\n idempotencyKey ??\n (maxRetries > 0 && isNonIdempotentMethod(method) ? newIdempotencyKey() : undefined);\n if (effectiveIdempotencyKey) finalHeaders[\"Idempotency-Key\"] = effectiveIdempotencyKey;\n\n let attempt = 0;\n for (;;) {\n let resp: Response;\n try {\n resp = await fetchImpl(url, {\n method,\n headers: finalHeaders,\n body: body == null ? undefined : JSON.stringify(body),\n signal,\n });\n } catch (err) {\n if (attempt < maxRetries) {\n await sleepImpl(backoffDelayMs(attempt, retryBaseMs));\n attempt++;\n continue;\n }\n throw new G8Error({\n message: `Network error: ${(err as Error)?.message ?? \"request failed\"}`,\n status: 0,\n type: \"network_error\",\n retryable: true,\n });\n }\n\n if (resp.ok) {\n const text = await resp.text();\n return (text ? JSON.parse(text) : undefined) as T;\n }\n\n if (isRetryableStatus(resp.status) && attempt < maxRetries) {\n const retryAfter = parseRetryAfter(resp.headers.get(\"retry-after\"));\n const delay = retryAfter != null ? retryAfter : backoffDelayMs(attempt, retryBaseMs);\n await sleepImpl(delay);\n attempt++;\n continue;\n }\n\n throw await toG8Error(resp);\n }\n}\n\nexport interface PaginatedResponse<T> {\n data: T[];\n pagination?: { next_cursor?: string | null; has_next?: boolean; page?: number };\n}\n\n/**\n * Auto-pagination async iterator following `next_cursor` (A9). Yields every\n * item across pages so callers can `for await (const x of paginate(...))`\n * without manual cursor bookkeeping.\n */\nexport async function* paginate<T>(\n fetchPage: (cursor?: string) => Promise<PaginatedResponse<T>>,\n): AsyncGenerator<T, void, unknown> {\n let cursor: string | undefined = undefined;\n for (;;) {\n const page = await fetchPage(cursor);\n for (const item of page.data ?? []) yield item;\n const next = page.pagination?.next_cursor;\n if (!next) break;\n cursor = next;\n }\n}\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface PersonEnrichment {\n found: boolean;\n confidence: number;\n data: Record<string, unknown>;\n}\n\nexport interface CompanyEnrichment {\n found: boolean;\n confidence: number;\n data: Record<string, unknown>;\n}\n\nexport interface EmailVerification {\n email: string;\n valid: boolean;\n deliverable: boolean;\n catch_all: boolean;\n}\n\nexport interface SearchFilter {\n field: string;\n operator: \"any_of\" | \"contains\" | \"all_of\" | \"none_of\" | \"is_empty\" | \"is_not_empty\" | \"between\" | \"exists\";\n value: unknown;\n}\n\nexport interface SearchResults {\n contacts: Record<string, unknown>[];\n total: number;\n page: number;\n}\n\n/**\n * Enrichment API - person/company lookup, email verification, prospecting search.\n *\n * Requires API key (server-side only). Credits charged per call. On the hardened\n * HTTP core: throws a typed `G8Error` on failure and retries transient errors.\n */\nexport const createEnrichClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n /** Look up a person by email, LinkedIn, or name + company. Costs 1 credit. */\n async person(params: {\n email?: string;\n linkedin_url?: string;\n first_name?: string;\n last_name?: string;\n company_domain?: string;\n }): Promise<PersonEnrichment> {\n const resp = await request<{ data?: PersonEnrichment }>(baseUrl, \"/api/v1/enrichment/lookup/person\", apiKey, {\n method: \"POST\",\n body: params,\n });\n return resp.data ?? (resp as unknown as PersonEnrichment);\n },\n\n /** Look up a company by domain or name. Costs 1 credit. */\n async company(params: { domain?: string; name?: string }): Promise<CompanyEnrichment> {\n const resp = await request<{ data?: CompanyEnrichment }>(baseUrl, \"/api/v1/enrichment/lookup/company\", apiKey, {\n method: \"POST\",\n body: params,\n });\n return resp.data ?? (resp as unknown as CompanyEnrichment);\n },\n\n /** Verify an email address. Costs 1 credit. */\n async verifyEmail(email: string): Promise<EmailVerification> {\n const resp = await request<{ data?: EmailVerification }>(baseUrl, \"/api/v1/enrichment/verify-email\", apiKey, {\n method: \"POST\",\n body: { email },\n });\n return resp.data ?? (resp as unknown as EmailVerification);\n },\n\n /** Search 700M+ contacts with filters. Credits charged per result. */\n async search(filters: SearchFilter[], page = 1, limit = 25): Promise<SearchResults> {\n const resp = await request<{ data?: SearchResults }>(baseUrl, \"/api/v1/search/contacts\", apiKey, {\n method: \"POST\",\n body: { filters, page, limit },\n });\n return resp.data ?? (resp as unknown as SearchResults);\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\n// -- Existing types (preserved for backwards compat) --\n\nexport interface Sequence {\n id: string;\n name: string;\n status: string;\n step_count: number | null;\n contact_count: number | null;\n}\n\nexport interface AddToSequenceConfig {\n sequenceId: string;\n contactIds: number[];\n listId: number;\n}\n\n// -- New (0.4.0) types --\n\nexport interface SequenceListItem {\n id: string;\n name: string | null;\n status: string | null;\n user_email: string | null;\n step_count: number | null;\n contact_count: number | null;\n sequence_kind: string | null;\n associated_list_id: number | null;\n created_at: string | null;\n updated_at: string | null;\n}\n\nexport interface SequenceListParams {\n page?: number;\n limit?: number;\n /** Filter by sequence status (e.g. \"live\", \"draft\", \"paused\"). */\n status?: string;\n /** Filter by sequence kind (\"cold_outbound\" or \"nurture\"). Omit to list all kinds. */\n sequence_kind?: SequenceKind;\n}\n\nexport interface SequenceDetail {\n id: string;\n name: string | null;\n description: string | null;\n status: string | null;\n user_email: string | null;\n associated_list_id: number | null;\n finish_on_reply: boolean;\n send_in_same_thread: boolean;\n wait_for_new_contacts: boolean;\n paused_at: string | null;\n resumed_at: string | null;\n /** Sequence kind. Immutable after creation. \"cold_outbound\" (default) or \"nurture\". */\n sequence_kind?: string | null;\n /** Pinned mailbox for nurture sequences; null for cold_outbound. Immutable. */\n pinned_mailbox_id?: number | null;\n created_at: string | null;\n updated_at: string | null;\n}\n\nexport interface SequenceContactItem {\n id: string | null;\n contact_id: number | null;\n state: string | null;\n current_step_order: number | null;\n created_at: string | null;\n updated_at: string | null;\n}\n\nexport interface SequenceContactsParams {\n page?: number;\n limit?: number;\n /** Filter by contact state (e.g. \"active\", \"completed\", \"replied\"). */\n state?: string;\n}\n\nexport interface SequenceActionResult {\n sequence_id: string;\n status: string;\n contacts_affected: number;\n}\n\n/**\n * Step type. Note: LinkedIn flows use \"HEYREACH\" — there is no \"LINKEDIN\" step type.\n * Server is case-insensitive on input but stores uppercase.\n */\nexport type SequenceStepType =\n | \"EMAIL\"\n | \"PHONE\"\n | \"SMS\"\n | \"WHATSAPP\"\n | \"HEYREACH\"\n | \"MANUAL_DIALER\";\n\nexport type SequenceStepInputType =\n | \"ON_DEMAND\"\n | \"MANUAL_TEMPLATE\"\n | \"AI_GENERATED_TEMPLATE\";\n\nexport interface SequenceStepConfig {\n step_order: number;\n step_type: SequenceStepType | string;\n /** Defaults to \"ON_DEMAND\". */\n input_type?: SequenceStepInputType | string;\n /** Wait time before this step in seconds. Defaults to 0. */\n time_interval?: number;\n /** Template body, AI prompt, or other step-type-specific config. */\n step_data?: Record<string, unknown>;\n}\n\nexport interface SequenceChannelConfig {\n channel_id: number;\n channel_value: string;\n channel_type: string;\n channel_data?: Record<string, unknown>;\n}\n\n/**\n * Sequence kind. A \"nurture\" is an ordinary sequence with `sequence_kind:\n * \"nurture\"` — same step types as cold outreach, but pinned to a single mailbox\n * with a higher daily email cap. There is no separate nurture client; create one\n * via `sequences.create({ sequence_kind: \"nurture\", pinned_mailbox_id })`.\n */\nexport type SequenceKind = \"cold_outbound\" | \"nurture\";\n\nexport interface SequenceCreateParams {\n name: string;\n user_email: string;\n description?: string;\n finish_on_reply?: boolean;\n send_in_same_thread?: boolean;\n wait_for_new_contacts?: boolean;\n associated_list_id?: number;\n steps?: SequenceStepConfig[];\n channels?: SequenceChannelConfig[];\n campaign_id?: string;\n /**\n * Sequence kind. Defaults to \"cold_outbound\" server-side. Immutable after\n * creation. \"nurture\" requires `pinned_mailbox_id` and is gated by the\n * ENABLE_NURTURE feature flag.\n */\n sequence_kind?: SequenceKind;\n /**\n * Mailbox to pin for a nurture sequence. Required when\n * `sequence_kind: \"nurture\"`; all email channels must use this mailbox.\n * Ignored for cold_outbound sequences.\n */\n pinned_mailbox_id?: number;\n}\n\nexport interface SequenceCreateResult {\n id: string;\n name: string;\n status: string;\n created_at: string | null;\n}\n\nexport interface SequenceUpdateParams {\n name?: string;\n description?: string;\n is_shared?: boolean;\n finish_on_reply?: boolean;\n send_in_same_thread?: boolean;\n wait_for_new_contacts?: boolean;\n schedule_id?: string;\n appointment_id?: number;\n}\n\nexport interface SequenceStepUpdateParams {\n step_data?: Record<string, unknown>;\n time_interval?: number;\n step_type?: SequenceStepType | string;\n input_type?: SequenceStepInputType | string;\n}\n\nexport interface SequencePreviewStep {\n id: string;\n step_order: number;\n step_type: string;\n input_type: string;\n time_interval: number | null;\n step_data: Record<string, unknown> | null;\n}\n\nexport interface SequencePreviewChannel {\n id: string;\n channel_id: number | null;\n channel_value: string | null;\n channel_type: string | null;\n}\n\nexport interface SequencePreview {\n id: string;\n name: string | null;\n status: string | null;\n description: string | null;\n steps: SequencePreviewStep[];\n channels: SequencePreviewChannel[];\n}\n\nexport interface SequenceAnalytics {\n overview: Record<string, unknown>;\n performance: Record<string, unknown>;\n engagement: Record<string, unknown>;\n timeline: Record<string, unknown>[];\n contact_distribution: Record<string, unknown>;\n step_breakdown: Record<string, unknown>[];\n step_creation_methods: Record<string, unknown>[];\n sender_distribution: Record<string, unknown>[];\n}\n\nexport interface PaginationMeta {\n page: number;\n limit: number;\n total: number;\n has_next: boolean;\n}\n\n/**\n * Sequences API — list, create, run, pause, update, analyze multi-channel sequences.\n * Requires API key (server-side).\n *\n * Backed by:\n * GET /api/v1/sequences\n * POST /api/v1/sequences\n * GET /api/v1/sequences/{sequence_id}\n * PATCH /api/v1/sequences/{sequence_id}\n * DELETE /api/v1/sequences/{sequence_id}\n * GET /api/v1/sequences/{sequence_id}/contacts\n * POST /api/v1/sequences/{sequence_id}/contacts\n * POST /api/v1/sequences/{sequence_id}/run\n * POST /api/v1/sequences/{sequence_id}/pause\n * POST /api/v1/sequences/{sequence_id}/resume\n * PATCH /api/v1/sequences/{sequence_id}/steps/{step_id}\n * GET /api/v1/sequences/{sequence_id}/preview\n * GET /api/v1/sequences/{sequence_id}/analytics\n */\nexport const createSequencesClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n // -- Overloads on list() preserve backwards compat with v0.3.0's positional signature --\n\n async function list(): Promise<SequenceListItem[]>;\n async function list(page: number, limit?: number): Promise<SequenceListItem[]>;\n async function list(params: SequenceListParams): Promise<SequenceListItem[]>;\n async function list(\n pageOrParams?: number | SequenceListParams,\n limit?: number,\n ): Promise<SequenceListItem[]> {\n let params: SequenceListParams;\n if (typeof pageOrParams === \"number\") {\n params = { page: pageOrParams, limit: limit ?? 50 };\n } else {\n params = pageOrParams ?? {};\n }\n const resp = await request<{ data?: SequenceListItem[] }>(baseUrl, \"/api/v1/sequences\", apiKey, {\n query: params as Record<string, unknown>,\n });\n return (resp.data ?? (resp as unknown as SequenceListItem[])) as SequenceListItem[];\n }\n\n return {\n /** List sequences with pagination + optional status filter. */\n list,\n\n /** Get full sequence details by ID. */\n async get(sequenceId: string): Promise<SequenceDetail> {\n const resp = await request<{ data?: SequenceDetail }>(baseUrl, `/api/v1/sequences/${sequenceId}`, apiKey);\n return resp.data ?? (resp as unknown as SequenceDetail);\n },\n\n /** List contacts enrolled in a sequence. Filter by state (e.g. \"active\", \"replied\"). */\n async contacts(\n sequenceId: string,\n params: SequenceContactsParams = {},\n ): Promise<{ data: SequenceContactItem[]; pagination?: PaginationMeta }> {\n return request(baseUrl, `/api/v1/sequences/${sequenceId}/contacts`, apiKey, {\n query: params as Record<string, unknown>,\n });\n },\n\n /**\n * Add contacts to a sequence (V2 queuing). Live or drafted sequences only.\n * Pass `idempotencyKey` to make a retry safe — the same key returns the\n * first result instead of re-enrolling on a 5xx-then-success (A6).\n */\n async add(config: AddToSequenceConfig, idempotencyKey?: string): Promise<SequenceActionResult> {\n const resp = await request<{ data?: SequenceActionResult }>(\n baseUrl,\n `/api/v1/sequences/${config.sequenceId}/contacts`,\n apiKey,\n { method: \"POST\", body: { contact_ids: config.contactIds, list_id: config.listId }, idempotencyKey },\n );\n return resp.data ?? (resp as unknown as SequenceActionResult);\n },\n\n /**\n * Create a new sequence with optional steps + channels. Pass `idempotencyKey`\n * to make a retry safe — the same key returns the first result instead of\n * creating a duplicate sequence on a 5xx-then-success (A6).\n */\n async create(payload: SequenceCreateParams, idempotencyKey?: string): Promise<SequenceCreateResult> {\n const resp = await request<{ data?: SequenceCreateResult }>(baseUrl, \"/api/v1/sequences\", apiKey, {\n method: \"POST\",\n body: payload,\n idempotencyKey,\n });\n return resp.data ?? (resp as unknown as SequenceCreateResult);\n },\n\n /** Update sequence metadata. Rejected (409) if sequence is in a transitional status. */\n async update(sequenceId: string, fields: SequenceUpdateParams): Promise<SequenceActionResult> {\n const resp = await request<{ data?: SequenceActionResult }>(baseUrl, `/api/v1/sequences/${sequenceId}`, apiKey, {\n method: \"PATCH\",\n body: fields,\n });\n return resp.data ?? (resp as unknown as SequenceActionResult);\n },\n\n /** Update a single step within a sequence. */\n async updateStep(\n sequenceId: string,\n stepId: string,\n fields: SequenceStepUpdateParams,\n ): Promise<SequenceActionResult> {\n const resp = await request<{ data?: SequenceActionResult }>(\n baseUrl,\n `/api/v1/sequences/${sequenceId}/steps/${stepId}`,\n apiKey,\n { method: \"PATCH\", body: fields },\n );\n return resp.data ?? (resp as unknown as SequenceActionResult);\n },\n\n /** Soft-delete (archive) a sequence. */\n async delete(sequenceId: string): Promise<SequenceActionResult> {\n const resp = await request<{ data?: SequenceActionResult }>(baseUrl, `/api/v1/sequences/${sequenceId}`, apiKey, {\n method: \"DELETE\",\n });\n return resp.data ?? (resp as unknown as SequenceActionResult);\n },\n\n /**\n * Run/start a DRAFTED sequence (V2 orchestration). Pass `idempotencyKey` to\n * make a retry safe — the same key won't re-trigger the run on a\n * 5xx-then-success (A6).\n */\n async run(sequenceId: string, idempotencyKey?: string): Promise<SequenceActionResult> {\n const resp = await request<{ data?: SequenceActionResult }>(\n baseUrl,\n `/api/v1/sequences/${sequenceId}/run`,\n apiKey,\n { method: \"POST\", idempotencyKey },\n );\n return resp.data ?? (resp as unknown as SequenceActionResult);\n },\n\n /**\n * Pause a live sequence. Pass `idempotencyKey` to make a retry safe — the\n * same key won't double-apply on a 5xx-then-success (A6).\n */\n async pause(sequenceId: string, idempotencyKey?: string): Promise<SequenceActionResult> {\n const resp = await request<{ data?: SequenceActionResult }>(\n baseUrl,\n `/api/v1/sequences/${sequenceId}/pause`,\n apiKey,\n { method: \"POST\", idempotencyKey },\n );\n return resp.data ?? (resp as unknown as SequenceActionResult);\n },\n\n /**\n * Resume a paused sequence. Pass `idempotencyKey` to make a retry safe — the\n * same key won't double-apply on a 5xx-then-success (A6).\n */\n async resume(sequenceId: string, idempotencyKey?: string): Promise<SequenceActionResult> {\n const resp = await request<{ data?: SequenceActionResult }>(\n baseUrl,\n `/api/v1/sequences/${sequenceId}/resume`,\n apiKey,\n { method: \"POST\", idempotencyKey },\n );\n return resp.data ?? (resp as unknown as SequenceActionResult);\n },\n\n /** Read-only sequence preview with all steps + channels (no enrollment). */\n async preview(sequenceId: string): Promise<SequencePreview> {\n const resp = await request<{ data?: SequencePreview }>(baseUrl, `/api/v1/sequences/${sequenceId}/preview`, apiKey);\n return resp.data ?? (resp as unknown as SequencePreview);\n },\n\n /** Comprehensive analytics for a sequence. */\n async analytics(sequenceId: string): Promise<SequenceAnalytics> {\n const resp = await request<{ data?: SequenceAnalytics }>(\n baseUrl,\n `/api/v1/sequences/${sequenceId}/analytics`,\n apiKey,\n );\n return resp.data ?? (resp as unknown as SequenceAnalytics);\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface Campaign {\n id: string;\n name: string;\n slug: string;\n status: string;\n category: string | null;\n goal: string | null;\n target_persona: string | null;\n}\n\nexport interface CampaignCreateConfig {\n name: string;\n category?: string;\n brief?: string;\n core_concept?: string;\n primary_hook?: string;\n target_persona?: string;\n goal?: string;\n}\n\nexport interface CampaignLaunchExecution {\n status: string;\n contacts_total?: number;\n contacts_scheduled?: number;\n contacts_queued?: number;\n job_id?: string;\n polling_url?: string;\n orchestrator_version?: string;\n}\n\nexport interface CampaignLaunchResult {\n status: string;\n campaign_id: string;\n sequence_id: string;\n sequence_name: string;\n steps_created: number;\n link_id: string;\n warnings: string[];\n channels_attached: Array<{\n mailbox_id: number;\n email: string;\n channel_type: string;\n auto_picked: boolean;\n }>;\n agent_assigned: {\n textual_agent_name: string | null;\n voice_agent_name: string | null;\n };\n schedule_id: string | null;\n appointment_id: number | null;\n execution: CampaignLaunchExecution;\n message: string;\n}\n\n/**\n * Campaigns - create, manage, launch campaigns. Requires API key (server-side).\n * On the hardened HTTP core: throws a typed `G8Error` on failure and retries\n * transient errors.\n */\nexport const createCampaignsClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n async list(page = 1, limit = 50): Promise<Campaign[]> {\n const resp = await request<{ data?: Campaign[] }>(baseUrl, \"/api/v1/campaigns\", apiKey, {\n query: { page, limit },\n });\n return resp.data ?? (resp as unknown as Campaign[]);\n },\n\n async get(campaignId: string): Promise<Campaign> {\n const resp = await request<{ data?: Campaign }>(baseUrl, `/api/v1/campaigns/${campaignId}`, apiKey);\n return resp.data ?? (resp as unknown as Campaign);\n },\n\n async create(config: CampaignCreateConfig): Promise<Campaign> {\n const resp = await request<{ data?: Campaign }>(baseUrl, \"/api/v1/campaigns\", apiKey, {\n method: \"POST\",\n body: config,\n });\n return resp.data ?? (resp as unknown as Campaign);\n },\n\n async launch(campaignId: string): Promise<CampaignLaunchResult> {\n const resp = await request<{ data?: CampaignLaunchResult }>(\n baseUrl,\n `/api/v1/campaigns/${campaignId}/launch`,\n apiKey,\n { method: \"POST\" },\n );\n return resp.data ?? (resp as unknown as CampaignLaunchResult);\n },\n };\n};\n","const DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface IntentSignals {\n domain: string;\n score: number;\n intent: \"low\" | \"medium\" | \"high\";\n signals: string[];\n last_seen: string | null;\n}\n\n/**\n * Intent signals - company-level buying signals. Write key for public, API key for full access.\n */\nexport const createSignalsClient = (key: string, isApiKey: boolean, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n const headers = (): Record<string, string> => isApiKey\n ? { \"Content-Type\": \"application/json\", \"Authorization\": `Bearer ${key}` }\n : { \"Content-Type\": \"application/json\", \"X-Write-Key\": key };\n\n // There is exactly ONE intent-signals route in the platform:\n // `/api/v1/public/signals/company` (developer_api/interfaces/v1/public_signals_router.py).\n //\n // This used to branch to `/api/v1/signals/company` for API-key callers. That\n // route has never existed at any prefix — the request 404'd, and the `!resp.ok`\n // guard below swallowed it into an all-zeros result, so a server-side caller\n // saw \"no intent\" rather than an error. Pointing both credential types at the\n // route that exists removes the phantom endpoint.\n //\n // KNOWN GAP (not fixed here, needs a product decision): the live route\n // authenticates with `validate_write_key`, which requires an `X-Write-Key`\n // header and rejects `Authorization: Bearer`. So an API-key-only caller still\n // cannot read signals — it now gets a 401 instead of a 404, both swallowed to\n // zeros. Closing that needs a server-side signals endpoint that accepts API\n // keys; until then, pass a `writeKey` to `g8.init()` to use this surface.\n const endpoint = \"/api/v1/public/signals/company\";\n\n return {\n /** Get intent signals for a specific company domain. */\n async company(domain: string): Promise<IntentSignals> {\n const resp = await fetch(`${baseUrl}${endpoint}?domain=${encodeURIComponent(domain)}`, { headers: headers() });\n if (!resp.ok) return { domain, score: 0, intent: \"low\", signals: [], last_seen: null };\n const data = await resp.json();\n return data.data || data;\n },\n\n /** Stream intent signals - calls callback every 30s with latest data. */\n stream(domains: string[], callback: (signals: IntentSignals[]) => void): () => void {\n const check = async () => {\n try {\n const results = await Promise.all(domains.map((d) => this.company(d)));\n callback(results);\n } catch { /* silent */ }\n };\n\n check();\n const interval = setInterval(check, 30_000);\n return () => clearInterval(interval);\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\ntype VoiceEvent = \"connected\" | \"transcription\" | \"ended\" | \"error\";\ntype VoiceCallback = (data: Record<string, unknown>) => void;\n\n// -- New (0.4.0) dialer types — backed by /api/v1/voice/dialer/* --\n\nexport interface VoicePagination {\n total_items: number;\n total_pages: number;\n current_page: number;\n page_size: number;\n}\n\nexport interface DialerSessionSummary {\n session_id: string;\n status: string | null;\n user_id: string | null;\n user_email: string | null;\n org_id: string | null;\n from_phone: string | null;\n campaign_id: string | null;\n campaign_builder_campaign_id: string | null;\n name: string | null;\n session_metadata: Record<string, unknown> | null;\n total_calls: number | null;\n created_at: string | null;\n updated_at: string | null;\n contact_ids: unknown[];\n}\n\nexport interface DialerSessionsListResult {\n sessions: DialerSessionSummary[];\n pagination: VoicePagination;\n}\n\nexport interface DialerSessionsListParams {\n page?: number;\n /** Default 50, max 200. */\n page_size?: number;\n /** Comma-separated status list (e.g. \"ACTIVE,PAUSED\"). */\n status?: string;\n user_email?: string;\n /** Substring search on session name. */\n name?: string;\n campaign_id?: string;\n list_id?: string;\n list_name?: string;\n /** ISO 8601 timestamp. */\n date_from?: string;\n /** ISO 8601 timestamp. */\n date_to?: string;\n sort_by?: string;\n /** Default \"desc\". */\n sort_order?: \"asc\" | \"desc\";\n}\n\nexport interface DialerStatsParams {\n session_id?: string;\n user_email?: string;\n /** YYYY-MM-DD. */\n date_from?: string;\n /** YYYY-MM-DD. */\n date_to?: string;\n /** Default \"DAILY\". */\n aggregation?: \"DAILY\" | \"TOTAL\";\n}\n\nexport interface DialerReportFilters {\n session_id: string | null;\n user_email: string | null;\n org_id: string | null;\n date_from: string | null;\n date_to: string | null;\n aggregation: string | null;\n}\n\nexport interface DialerReportMetric {\n date: string | null;\n sdr_name: string | null;\n org_id: string | null;\n list_title: string | null;\n total_dials: number;\n total_connections: number;\n connection_rate: number;\n total_voicemails: number;\n voicemail_rate: number;\n talk_time_minutes: number;\n avg_call_duration_minutes: number;\n dispositions: Record<string, unknown> | null;\n success_rate: number;\n unique_sessions: number;\n avg_calls_per_session: number;\n redial_count: number;\n redial_success_rate: number | null;\n peak_calling_hour: number | null;\n total_callbacks: number;\n}\n\nexport interface DialerStatsResult {\n filters: DialerReportFilters;\n metrics: DialerReportMetric[];\n summary: DialerReportMetric | null;\n}\n\nexport interface DialerNumberInfo {\n number: string;\n /** Default \"ai\". */\n inbound_type: string;\n /** Default \"twilio\". */\n telephony_provider: string;\n calls_today: number;\n calls_7d: number;\n connect_rate_7d: number;\n is_valid: boolean;\n daily_limit: number;\n assigned_to: string | null;\n created_at: string | null;\n}\n\nexport interface DialerNumbersListResult {\n numbers: DialerNumberInfo[];\n}\n\nexport interface MissedCallback {\n id: string | number | null;\n caller_phone: string | null;\n inbound_number: string | null;\n first_name: string | null;\n last_name: string | null;\n email: string | null;\n contact_id: string | number | null;\n parallel_session_id: string | null;\n created_at: string | null;\n call_duration: number | null;\n is_callback: boolean;\n}\n\nexport interface MissedCallbacksResult {\n missed_callbacks: MissedCallback[];\n count: number;\n}\n\nexport interface CallGradingResult {\n room_name: string;\n /** \"ready\" or \"pending\". */\n status: string;\n grading: Record<string, unknown> | null;\n reviewed: boolean;\n reviewed_at: string | null;\n}\n\nexport interface DialerAgentSummary {\n agent_id: string;\n agent_name: string | null;\n /** \"SDR\", etc. */\n role: string | null;\n agent_status: string | null;\n /** \"agent\" or \"twin\". */\n entity_type: string | null;\n description: string | null;\n phone: string | null;\n is_template: boolean;\n}\n\nexport interface DialerAgentsListResult {\n agents: DialerAgentSummary[];\n total_count: number;\n}\n\nexport interface DialerAgentsListParams {\n /** e.g. \"SDR\". */\n role?: string;\n agent_status?: string;\n /** \"agent\" or \"twin\". */\n entity_type?: \"agent\" | \"twin\";\n /** Substring filter on name/description. */\n search?: string;\n}\n\nexport interface DialerSessionCreateParams {\n /** 1-200 chars. */\n name: string;\n /** Org's dialer phone (E.164, 9-16 chars). */\n from_phone: string;\n /** Contact list ID (stored in session metadata). */\n list_id?: string;\n /** Display title for the source list. */\n list_title?: string;\n /** V2 agent UUID. */\n agent_id?: string;\n /** V1 fallback name. Defaults to \"default_agent\". */\n agent_name?: string;\n /** \"agent\" or \"twin\". */\n entity_type?: \"agent\" | \"twin\";\n /** Campaign Builder UUID. */\n studio_campaign_id?: string;\n /** IANA timezone (e.g. \"America/New_York\"). */\n user_timezone?: string;\n /** Per-session voicemail-skip override. null/omit = use SDR default. */\n skip_voicemails?: boolean;\n}\n\nexport interface DialerSessionCreateResult {\n session_id: string;\n status: string;\n name: string | null;\n org_id: string | null;\n user_id: string | null;\n user_email: string | null;\n from_phone: string | null;\n session_metadata: Record<string, unknown> | null;\n total_calls: number;\n created_at: string | null;\n message: string | null;\n}\n\n/** \"ACTIVE\" resumes, \"PAUSED\" pauses, \"COMPLETED\" stops. \"FAILED\" is rejected. */\nexport type DialerSessionStatus = \"ACTIVE\" | \"PAUSED\" | \"COMPLETED\";\n\nexport interface DialerSessionStatusUpdateResult {\n session_id: string;\n status: string;\n message: string | null;\n name: string | null;\n org_id: string | null;\n user_id: string | null;\n user_email: string | null;\n from_phone: string | null;\n session_metadata: Record<string, unknown> | null;\n total_calls: number;\n created_at: string | null;\n updated_at: string | null;\n}\n\nexport interface DialerSessionResumeResult extends DialerSessionStatusUpdateResult {\n /** Contacts actually placed in this batch (voice caps at 4). */\n dialed_count: number;\n}\n\n/**\n * Voice AI — start AI voice calls, get transcriptions and analysis.\n * Plus full parallel-dialer session control via the `dialer` namespace.\n * Requires API key (server-side).\n *\n * Backed by:\n * GET /api/v1/voice/dialer/sessions\n * POST /api/v1/voice/dialer/sessions\n * PATCH /api/v1/voice/dialer/sessions/{session_id}/status\n * POST /api/v1/voice/dialer/sessions/{session_id}/resume\n * GET /api/v1/voice/dialer/stats\n * GET /api/v1/voice/dialer/numbers\n * GET /api/v1/voice/dialer/missed-callbacks\n * GET /api/v1/voice/dialer/calls/{room_name}/grading\n * GET /api/v1/voice/dialer/agents\n */\nexport const createVoiceClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n const listeners: Map<VoiceEvent, VoiceCallback[]> = new Map();\n\n const dialer = {\n /** List parallel-dialer sessions with filters + pagination. */\n async listSessions(params: DialerSessionsListParams = {}): Promise<DialerSessionsListResult> {\n const resp = await request<{ data?: DialerSessionsListResult }>(\n baseUrl,\n \"/api/v1/voice/dialer/sessions\",\n apiKey,\n { query: params as Record<string, unknown> },\n );\n return resp.data ?? (resp as unknown as DialerSessionsListResult);\n },\n\n /** Create a parallel-dialer session in PAUSED state. SDR opens UI to start dialing. */\n async createSession(payload: DialerSessionCreateParams): Promise<DialerSessionCreateResult> {\n const resp = await request<{ data?: DialerSessionCreateResult }>(\n baseUrl,\n \"/api/v1/voice/dialer/sessions\",\n apiKey,\n { method: \"POST\", body: payload },\n );\n return resp.data ?? (resp as unknown as DialerSessionCreateResult);\n },\n\n /** Pause / resume / stop a dialer session via status flip. */\n async updateSessionStatus(\n sessionId: string,\n status: DialerSessionStatus,\n ): Promise<DialerSessionStatusUpdateResult> {\n const resp = await request<{ data?: DialerSessionStatusUpdateResult }>(\n baseUrl,\n `/api/v1/voice/dialer/sessions/${sessionId}/status`,\n apiKey,\n { method: \"PATCH\", body: { status } },\n );\n return resp.data ?? (resp as unknown as DialerSessionStatusUpdateResult);\n },\n\n /**\n * Resume a PAUSED dialer session. Auto-fetches the next batch from the source list,\n * filters already-called + phoneless rows, and forwards to voice's start-session.\n * @param maxContacts 1-4 (voice caps parallel dialing at 4). Default 4.\n */\n async resumeSession(sessionId: string, maxContacts = 4): Promise<DialerSessionResumeResult> {\n const resp = await request<{ data?: DialerSessionResumeResult }>(\n baseUrl,\n `/api/v1/voice/dialer/sessions/${sessionId}/resume`,\n apiKey,\n { method: \"POST\", body: { max_contacts: maxContacts } },\n );\n return resp.data ?? (resp as unknown as DialerSessionResumeResult);\n },\n\n /** Aggregated dialer analytics (daily breakdown or total). */\n async stats(params: DialerStatsParams = {}): Promise<DialerStatsResult> {\n const resp = await request<{ data?: DialerStatsResult }>(\n baseUrl,\n \"/api/v1/voice/dialer/stats\",\n apiKey,\n { query: params as Record<string, unknown> },\n );\n return resp.data ?? (resp as unknown as DialerStatsResult);\n },\n\n /** List dialer-eligible phone numbers with 7-day stats + daily limits. */\n async numbers(userEmail?: string): Promise<DialerNumbersListResult> {\n const resp = await request<{ data?: DialerNumbersListResult }>(\n baseUrl,\n \"/api/v1/voice/dialer/numbers\",\n apiKey,\n { query: userEmail ? { user_email: userEmail } : {} },\n );\n return resp.data ?? (resp as unknown as DialerNumbersListResult);\n },\n\n /** List missed inbound callbacks with caller / contact info. */\n async missedCallbacks(limit = 50): Promise<MissedCallbacksResult> {\n const resp = await request<{ data?: MissedCallbacksResult }>(\n baseUrl,\n \"/api/v1/voice/dialer/missed-callbacks\",\n apiKey,\n { query: { limit } },\n );\n return resp.data ?? (resp as unknown as MissedCallbacksResult);\n },\n\n /** AI grading for a single dialer call (returns \"pending\" while in progress). */\n async callGrading(roomName: string): Promise<CallGradingResult> {\n const resp = await request<{ data?: CallGradingResult }>(\n baseUrl,\n `/api/v1/voice/dialer/calls/${encodeURIComponent(roomName)}/grading`,\n apiKey,\n );\n return resp.data ?? (resp as unknown as CallGradingResult);\n },\n\n /** List voice agents available for dialer sessions (capped at 100; no pagination). */\n async agents(params: DialerAgentsListParams = {}): Promise<DialerAgentsListResult> {\n const resp = await request<{ data?: DialerAgentsListResult }>(\n baseUrl,\n \"/api/v1/voice/dialer/agents\",\n apiKey,\n { query: params as Record<string, unknown> },\n );\n return resp.data ?? (resp as unknown as DialerAgentsListResult);\n },\n\n /** Fetch the full transcript for a single dialer call. */\n async callTranscript(roomName: string): Promise<{ data: { status: string; transcript: Array<{ speaker: string; text: string; timestamp: number }> } }> {\n return request(\n baseUrl,\n `/api/v1/voice/dialer/calls/${encodeURIComponent(roomName)}/transcript`,\n apiKey,\n );\n },\n\n /** List dialer calls — pass `contact_id` or `user_email` to scope. */\n async listCalls(params: { contact_id?: number; user_email?: string; campaign_id?: string; date_from?: string; date_to?: string; limit?: number; page?: number } = {}): Promise<{ data: Array<Record<string, unknown>>; pagination?: VoicePagination }> {\n return request(\n baseUrl,\n \"/api/v1/voice/dialer/calls\",\n apiKey,\n { query: params as Record<string, unknown> },\n );\n },\n\n /** Convenience: list calls for a single contact. */\n async listCallsForContact(contactId: number, extra: { limit?: number } = {}): Promise<{ data: Array<Record<string, unknown>> }> {\n return request(\n baseUrl,\n \"/api/v1/voice/dialer/calls\",\n apiKey,\n { query: { contact_id: contactId, ...extra } },\n );\n },\n\n /** Convenience: list calls placed by a specific SDR. */\n async listCallsForSdr(userEmail: string, extra: { limit?: number; date_from?: string; date_to?: string } = {}): Promise<{ data: Array<Record<string, unknown>> }> {\n return request(\n baseUrl,\n \"/api/v1/voice/dialer/calls\",\n apiKey,\n { query: { user_email: userEmail, ...extra } },\n );\n },\n };\n\n return {\n /** Listen for voice events. */\n on(event: VoiceEvent, callback: VoiceCallback): void {\n if (!listeners.has(event)) listeners.set(event, []);\n listeners.get(event)!.push(callback);\n },\n\n /** Parallel-dialer session control + analytics. */\n dialer,\n };\n};\n","import { createHmac, timingSafeEqual } from \"node:crypto\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\n/**\n * Known graph8 webhook event types.\n *\n * Source of truth is the backend ``WEBHOOK_EVENTS`` catalog\n * (campaign_builder/services/webhook_service.py). Keep in sync when the backend\n * adds events. ``WebhookEvent`` also accepts any string so a newly-added\n * backend event never breaks a client that hasn't upgraded.\n */\nexport const KNOWN_WEBHOOK_EVENTS = [\n \"campaign.created\",\n \"campaign.updated\",\n \"campaign.deleted\",\n \"campaign.launched\",\n \"campaign.paused\",\n \"campaign.completed\",\n \"campaign.status_changed\",\n \"campaign.content_ready\",\n \"document.generated\",\n \"document.failed\",\n \"intelligence.completed\",\n \"intelligence.failed\",\n \"company.enriched\",\n \"company_intelligence.completed\",\n \"audience.ready\",\n \"audience.failed\",\n \"sequence.draft_created\",\n \"sequence.started\",\n \"sequence.paused\",\n \"sequence.completed\",\n \"engagement.email_sent\",\n \"engagement.email_replied\",\n \"engagement.email_bounced\",\n \"engagement.email_skipped\",\n \"engagement.call_dispatched\",\n \"engagement.sms_sent\",\n \"engagement.sms_replied\",\n \"engagement.whatsapp_sent\",\n \"engagement.linkedin_connection_sent\",\n \"engagement.linkedin_message_sent\",\n \"engagement.linkedin_inmail_sent\",\n \"engagement.linkedin_reply_received\",\n \"engagement.linkedin_connection_accepted\",\n \"meeting.booked\",\n \"meeting.cancelled\",\n \"meeting.rescheduled\",\n] as const;\n\n// Union of known events for autocomplete, plus `string` so unknown/new backend\n// events still type-check (Stripe-style forward compatibility).\nexport type WebhookEvent = (typeof KNOWN_WEBHOOK_EVENTS)[number] | (string & {});\n\n/** The decoded body graph8 delivers to a webhook endpoint. */\nexport interface WebhookEventPayload {\n event: WebhookEvent;\n timestamp: string;\n data: Record<string, unknown>;\n org_id: string;\n /** Stable per-delivery id (present once the backend adds it; for consumer dedup). */\n id?: string;\n}\n\nexport interface ConstructEventOptions {\n /**\n * Reject events whose ``X-Studio-Timestamp`` is older (or newer) than this\n * many seconds — replay protection. Disabled when 0/undefined.\n */\n toleranceSeconds?: number;\n}\n\n/** Thrown by {@link constructEvent} when a webhook cannot be verified. */\nexport class WebhookSignatureError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"WebhookSignatureError\";\n }\n}\n\nfunction timingSafeEqualHex(a: string, b: string): boolean {\n // Length check first: timingSafeEqual throws on length mismatch.\n if (a.length !== b.length) return false;\n try {\n const enc = new TextEncoder();\n return timingSafeEqual(enc.encode(a), enc.encode(b));\n } catch {\n return false;\n }\n}\n\n/**\n * Verify the HMAC-SHA256 signature of an incoming graph8 webhook and return the\n * parsed event. Server-side only (needs the per-endpoint signing secret).\n *\n * Mirrors the backend signing scheme exactly: the signature is\n * ``HMAC_SHA256(secret, `${timestamp}.${rawBody}`)`` hex-encoded, delivered in\n * the ``X-Studio-Signature`` header alongside the unix ``X-Studio-Timestamp``.\n *\n * @param payload The RAW request body string (verify before JSON.parse — re-serializing changes bytes).\n * @param signature The ``X-Studio-Signature`` header (an optional ``sha256=`` prefix is tolerated).\n * @param timestamp The ``X-Studio-Timestamp`` header (unix seconds).\n * @param secret The endpoint's signing secret.\n * @throws {WebhookSignatureError} on a missing/invalid signature, stale timestamp, or bad JSON.\n *\n * @example\n * app.post(\"/webhooks/graph8\", (req, res) => {\n * const event = g8.webhooks.constructEvent(\n * req.rawBody,\n * req.header(\"X-Studio-Signature\"),\n * req.header(\"X-Studio-Timestamp\"),\n * process.env.G8_WEBHOOK_SECRET,\n * { toleranceSeconds: 300 },\n * );\n * if (event.event === \"meeting.booked\") { ... }\n * res.sendStatus(200);\n * });\n */\nexport function constructEvent(\n payload: string,\n signature: string,\n timestamp: string | number,\n secret: string,\n opts: ConstructEventOptions = {},\n): WebhookEventPayload {\n if (!signature) throw new WebhookSignatureError(\"Missing X-Studio-Signature header\");\n if (timestamp === undefined || timestamp === null || timestamp === \"\") {\n throw new WebhookSignatureError(\"Missing X-Studio-Timestamp header\");\n }\n if (!secret) throw new WebhookSignatureError(\"Missing signing secret\");\n\n const ts = typeof timestamp === \"number\" ? timestamp : parseInt(timestamp, 10);\n if (!Number.isFinite(ts)) throw new WebhookSignatureError(\"Invalid X-Studio-Timestamp header\");\n\n const tolerance = opts.toleranceSeconds ?? 0;\n if (tolerance > 0) {\n const ageSeconds = Math.floor(Date.now() / 1000) - ts;\n if (Math.abs(ageSeconds) > tolerance) {\n throw new WebhookSignatureError(`Timestamp outside tolerance (${ageSeconds}s > ${tolerance}s)`);\n }\n }\n\n const expected = createHmac(\"sha256\", secret).update(`${ts}.${payload}`).digest(\"hex\");\n const provided = signature.startsWith(\"sha256=\") ? signature.slice(7) : signature;\n if (!timingSafeEqualHex(expected, provided)) {\n throw new WebhookSignatureError(\"Signature verification failed\");\n }\n\n try {\n return JSON.parse(payload) as WebhookEventPayload;\n } catch {\n throw new WebhookSignatureError(\"Invalid JSON payload\");\n }\n}\n\n/**\n * Webhooks client (server-side). graph8 webhooks are PUSH — graph8 POSTs to your\n * endpoint; you verify each delivery with {@link constructEvent}. (The previous\n * polling implementation hit a feed endpoint that never existed and is removed.)\n */\nexport const createWebhooksClient = (_apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n return {\n /** Base URL the webhook subscription API lives under. */\n baseUrl,\n /** Known event types (for autocomplete / validation). */\n knownEvents: KNOWN_WEBHOOK_EVENTS,\n /** Verify an incoming webhook's HMAC signature and return the parsed event. */\n constructEvent(\n payload: string,\n signature: string,\n timestamp: string | number,\n secret: string,\n opts?: ConstructEventOptions,\n ): WebhookEventPayload {\n return constructEvent(payload, signature, timestamp, secret, opts);\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface Contact {\n id: number;\n first_name: string | null;\n last_name: string | null;\n work_email: string | null;\n job_title: string | null;\n seniority_level: string | null;\n company_name: string | null;\n company_domain: string | null;\n linkedin_url: string | null;\n direct_phone: string | null;\n mobile_phone: string | null;\n city: string | null;\n state: string | null;\n country: string | null;\n}\n\nexport interface ContactListParams {\n page?: number;\n limit?: number;\n email?: string;\n name?: string;\n job_title?: string;\n seniority_level?: string;\n company_name?: string;\n country?: string;\n list_id?: number;\n}\n\nexport interface ContactCreateParams {\n work_email: string;\n first_name?: string;\n last_name?: string;\n job_title?: string;\n company_domain?: string;\n linkedin_url?: string;\n direct_phone?: string;\n mobile_phone?: string;\n city?: string;\n state?: string;\n country?: string;\n list_id?: number;\n}\n\nexport interface ContactUpdateParams {\n first_name?: string;\n last_name?: string;\n work_email?: string;\n job_title?: string;\n company_domain?: string;\n linkedin_url?: string;\n direct_phone?: string;\n mobile_phone?: string;\n city?: string;\n state?: string;\n country?: string;\n}\n\n/** A column definition on contacts (base or custom). */\nexport interface ContactColumn {\n id: number | null;\n title: string;\n data_type: string;\n name: string | null;\n /** Stringified list_id when list-scoped, otherwise null/empty for global. */\n object_id: string | null;\n is_global: boolean;\n}\n\nexport interface ContactColumnCreateParams {\n title: string;\n /** Email of the user creating the column. Required by the backend (audit). */\n created_by: string;\n /** \"text\" (default) — additional types may be added later. */\n data_type?: string;\n /** If set, the column is list-specific. Omit for org-wide global column. */\n list_id?: number;\n}\n\n/**\n * Contacts API - full CRUD for CRM contacts. Requires API key (server-side).\n */\nexport const createContactsClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n /** List contacts with optional filters. */\n async list(params: ContactListParams = {}): Promise<{ data: Contact[]; total: number }> {\n return request(baseUrl, \"/api/v1/contacts\", apiKey, {\n query: params as Record<string, unknown>,\n });\n },\n\n /** Get a single contact by ID. */\n async get(contactId: number): Promise<Contact> {\n const resp = await request<{ data?: Contact }>(baseUrl, `/api/v1/contacts/${contactId}`, apiKey);\n return resp.data ?? (resp as unknown as Contact);\n },\n\n /**\n * Create a new contact. Pass `idempotencyKey` to make a retry safe — the\n * same key returns the first result instead of creating a duplicate (A6).\n */\n async create(contact: ContactCreateParams, idempotencyKey?: string): Promise<Contact> {\n const resp = await request<{ data?: Contact }>(baseUrl, \"/api/v1/contacts\", apiKey, {\n method: \"POST\",\n body: contact,\n idempotencyKey,\n });\n return resp.data ?? (resp as unknown as Contact);\n },\n\n /** Update a contact (partial). */\n async update(contactId: number, fields: ContactUpdateParams): Promise<{ updated: number }> {\n return request(baseUrl, `/api/v1/contacts/${contactId}`, apiKey, {\n method: \"PATCH\",\n body: fields,\n });\n },\n\n /** Delete a contact (soft-delete). */\n async delete(contactId: number): Promise<{ deleted: boolean }> {\n return request(baseUrl, `/api/v1/contacts/${contactId}`, apiKey, { method: \"DELETE\" });\n },\n\n /** List custom contact columns. Pass listId to include list-specific columns. */\n async listColumns(listId?: number): Promise<{ data: ContactColumn[] }> {\n return request(baseUrl, \"/api/v1/contacts/columns\", apiKey, {\n query: listId != null ? { list_id: listId } : undefined,\n });\n },\n\n /** Create a custom contact column. Global if list_id is omitted, list-scoped otherwise. */\n async createColumn(params: ContactColumnCreateParams): Promise<ContactColumn> {\n const resp = await request<{ data?: ContactColumn }>(baseUrl, \"/api/v1/contacts/columns/create\", apiKey, {\n method: \"POST\",\n body: {\n title: params.title,\n data_type: params.data_type || \"text\",\n list_id: params.list_id ?? null,\n created_by: params.created_by,\n },\n });\n return resp.data ?? (resp as unknown as ContactColumn);\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface Company {\n id: number;\n name: string | null;\n domain: string | null;\n industry: string | null;\n employee_count: number | null;\n revenue: number | null;\n founded_year: number | null;\n description: string | null;\n linkedin_url: string | null;\n country: string | null;\n state: string | null;\n city: string | null;\n}\n\nexport interface CompanyListParams {\n page?: number;\n limit?: number;\n domain?: string;\n industry?: string;\n name?: string;\n country?: string;\n}\n\nexport interface CompanyUpdateParams {\n name?: string;\n domain?: string;\n industry?: string;\n description?: string;\n linkedin_url?: string;\n country?: string;\n state?: string;\n city?: string;\n}\n\nexport interface CompanyContact {\n id: number;\n first_name: string | null;\n last_name: string | null;\n work_email: string | null;\n job_title: string | null;\n}\n\n/** A column definition on companies (base or custom). */\nexport interface CompanyColumn {\n id: number | null;\n title: string;\n data_type: string;\n name: string | null;\n /** Stringified list_id when list-scoped, otherwise null/empty for global. */\n object_id: string | null;\n is_global: boolean;\n}\n\nexport interface CompanyColumnCreateParams {\n title: string;\n /** Email of the user creating the column. Required by the backend (audit). */\n created_by: string;\n /** \"text\" (default) — additional types may be added later. */\n data_type?: string;\n /** If set, the column is list-specific. Omit for org-wide global column. */\n list_id?: number;\n}\n\n/**\n * Companies API - search and manage CRM companies. Requires API key (server-side).\n */\nexport const createCompaniesClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n /** List companies with optional filters. */\n async list(params: CompanyListParams = {}): Promise<{ data: Company[]; total: number }> {\n return request(baseUrl, \"/api/v1/companies\", apiKey, { query: params as Record<string, unknown> });\n },\n\n /** Get a single company by ID. */\n async get(companyId: number): Promise<Company> {\n const resp = await request<{ data?: Company }>(baseUrl, `/api/v1/companies/${companyId}`, apiKey);\n return resp.data ?? (resp as unknown as Company);\n },\n\n /** Get contacts belonging to a company. */\n async contacts(companyId: number, limit = 50, offset = 0): Promise<{ data: CompanyContact[]; total: number }> {\n return request(baseUrl, `/api/v1/companies/${companyId}/contacts`, apiKey, { query: { limit, offset } });\n },\n\n /** Update a company (partial). */\n async update(companyId: number, fields: CompanyUpdateParams): Promise<{ updated: number }> {\n return request(baseUrl, `/api/v1/companies/${companyId}`, apiKey, { method: \"PATCH\", body: fields });\n },\n\n /** Delete a company (soft-delete). */\n async delete(companyId: number): Promise<{ deleted: boolean }> {\n return request(baseUrl, `/api/v1/companies/${companyId}`, apiKey, { method: \"DELETE\" });\n },\n\n /** List custom company columns. Pass listId to include list-specific columns. */\n async listColumns(listId?: number): Promise<{ data: CompanyColumn[] }> {\n return request(baseUrl, \"/api/v1/companies/columns\", apiKey, {\n query: listId != null ? { list_id: listId } : undefined,\n });\n },\n\n /** Create a custom company column. Global if list_id is omitted, list-scoped otherwise. */\n async createColumn(params: CompanyColumnCreateParams): Promise<CompanyColumn> {\n const resp = await request<{ data?: CompanyColumn }>(baseUrl, \"/api/v1/companies/columns/create\", apiKey, {\n method: \"POST\",\n body: {\n title: params.title,\n data_type: params.data_type || \"text\",\n list_id: params.list_id ?? null,\n created_by: params.created_by,\n },\n });\n return resp.data ?? (resp as unknown as CompanyColumn);\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface ContactList {\n id: number;\n title: string;\n type: \"contacts\" | \"companies\";\n contact_count: number | null;\n created_at: string | null;\n}\n\nexport interface ListContact {\n id: number;\n first_name: string | null;\n last_name: string | null;\n work_email: string | null;\n job_title: string | null;\n company_name: string | null;\n}\n\n/**\n * Lists API - manage contact and company lists. Requires API key (server-side).\n */\nexport const createListsClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n /** List all lists. */\n async list(page = 1, limit = 50): Promise<{ data: ContactList[]; total: number }> {\n return request(baseUrl, \"/api/v1/lists\", apiKey, { query: { page, limit } });\n },\n\n /** Create a new list. */\n async create(title: string, type: \"contacts\" | \"companies\" = \"contacts\"): Promise<ContactList> {\n const resp = await request<{ data?: ContactList }>(baseUrl, \"/api/v1/lists\", apiKey, {\n method: \"POST\",\n body: { title, type },\n });\n return resp.data ?? (resp as unknown as ContactList);\n },\n\n /** Delete a list (soft-delete). */\n async delete(listId: number): Promise<{ deleted: boolean }> {\n return request(baseUrl, `/api/v1/lists/${listId}`, apiKey, { method: \"DELETE\" });\n },\n\n /** Get contacts in a list. */\n async contacts(listId: number, page = 1, limit = 50): Promise<{ data: ListContact[]; total: number }> {\n return request(baseUrl, `/api/v1/lists/${listId}/contacts`, apiKey, { query: { page, limit } });\n },\n\n /** Add contacts to a list. */\n async addContacts(listId: number, contactIds: number[]): Promise<{ added: number }> {\n return request(baseUrl, `/api/v1/lists/${listId}/contacts`, apiKey, {\n method: \"POST\",\n body: { contact_ids: contactIds },\n });\n },\n\n /** Remove contacts from a list. */\n async removeContacts(listId: number, contactIds: number[]): Promise<{ removed: number }> {\n return request(baseUrl, `/api/v1/lists/${listId}/contacts`, apiKey, {\n method: \"DELETE\",\n body: { contact_ids: contactIds },\n });\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface Note {\n id: string;\n entity_type: string;\n entity_id: string;\n content: string;\n created_by: string | null;\n created_by_name: string | null;\n created_at: string | null;\n updated_at: string | null;\n}\n\n/**\n * Notes API - CRUD for notes attached to contacts. Requires API key (server-side).\n *\n * Backed by:\n * GET /api/v1/contacts/{contact_id}/notes\n * POST /api/v1/contacts/{contact_id}/notes\n * PATCH /api/v1/notes/{note_id}\n * DELETE /api/v1/notes/{note_id}\n */\nexport const createNotesClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n /** List all notes on a contact. */\n async list(contactId: number): Promise<{ data: Note[] }> {\n return request(baseUrl, `/api/v1/contacts/${contactId}/notes`, apiKey);\n },\n\n /** Create a note on a contact. */\n async create(contactId: number, content: string): Promise<Note> {\n const resp = await request<{ data?: Note }>(baseUrl, `/api/v1/contacts/${contactId}/notes`, apiKey, {\n method: \"POST\",\n body: { content },\n });\n return resp.data ?? (resp as unknown as Note);\n },\n\n /** Update a note's content. */\n async update(noteId: string, content: string): Promise<Note> {\n const resp = await request<{ data?: Note }>(baseUrl, `/api/v1/notes/${noteId}`, apiKey, {\n method: \"PATCH\",\n body: { content },\n });\n return resp.data ?? (resp as unknown as Note);\n },\n\n /** Delete a note. */\n async delete(noteId: string): Promise<{ data: { deleted: boolean } }> {\n return request(baseUrl, `/api/v1/notes/${noteId}`, apiKey, { method: \"DELETE\" });\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface Task {\n id: string;\n entity_type: string | null;\n entity_id: string | null;\n title: string;\n description: string | null;\n due_date: string | null;\n assignee_id: string | null;\n assignee_name: string | null;\n /** \"open\" | \"completed\" (other values may appear if the backend adds states later) */\n status: string | null;\n /** 0=none, 1=urgent, 2=high, 3=normal, 4=low */\n priority: number | null;\n task_type: string | null;\n created_by: string | null;\n created_at: string | null;\n updated_at: string | null;\n /** Parent task UUID when this row is a subtask, else null. One level deep. */\n parent_task_id?: string | null;\n /** Sort position within the parent's subtasks. 0 when not a subtask. */\n subtask_sort_order?: number | null;\n /** Number of subtasks under this task (top-level rows only). */\n subtask_count?: number | null;\n /** Subset of subtask_count whose status is \"completed\". */\n completed_subtask_count?: number | null;\n}\n\nexport interface TaskCreateParams {\n title: string;\n description?: string;\n /** ISO 8601 date string. */\n due_date?: string;\n assignee_id?: string;\n /** 0=none, 1=urgent, 2=high, 3=normal, 4=low */\n priority?: number;\n /**\n * When set, the new task is created as a subtask of this parent. Parent\n * must exist and must not itself be a subtask (subtasks are one level\n * deep).\n */\n parent_task_id?: string;\n /**\n * Sort position within the parent's subtasks. Omit to append at the end.\n */\n subtask_sort_order?: number;\n}\n\nexport interface TaskUpdateParams {\n title?: string;\n description?: string;\n due_date?: string;\n assignee_id?: string;\n /** \"open\" | \"completed\" */\n status?: string;\n priority?: number;\n /**\n * Re-parent this task into a subtask of the given parent. Parent must\n * exist, must not itself be a subtask, and a task that already has\n * subtasks of its own cannot be demoted into a subtask.\n */\n parent_task_id?: string;\n /** Reorder within the parent's subtasks. Can be sent alone. */\n subtask_sort_order?: number;\n}\n\nexport interface TaskListParams {\n /** \"open\" | \"completed\" */\n status?: string;\n /** 0-4 */\n priority?: number;\n assignee_id?: string;\n /** Substring search on title. */\n search?: string;\n}\n\n/**\n * Tasks API - CRUD for tasks attached to contacts. Requires API key (server-side).\n *\n * Backed by:\n * GET /api/v1/contacts/{contact_id}/tasks\n * GET /api/v1/tasks\n * POST /api/v1/contacts/{contact_id}/tasks\n * PATCH /api/v1/tasks/{task_id}\n * DELETE /api/v1/tasks/{task_id}\n */\nexport const createTasksClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n /** List tasks on a single contact. Optional status filter (\"open\" | \"completed\"). */\n async listForContact(contactId: number, status?: string): Promise<{ data: Task[] }> {\n return request(baseUrl, `/api/v1/contacts/${contactId}/tasks`, apiKey, {\n query: status ? { status } : undefined,\n });\n },\n\n /** List all tasks org-wide with optional filters. */\n async list(params: TaskListParams = {}): Promise<{ data: Task[] }> {\n return request(baseUrl, \"/api/v1/tasks\", apiKey, { query: params as Record<string, unknown> });\n },\n\n /** Create a task on a contact. */\n async create(contactId: number, task: TaskCreateParams): Promise<Task> {\n const resp = await request<{ data?: Task }>(baseUrl, `/api/v1/contacts/${contactId}/tasks`, apiKey, {\n method: \"POST\",\n body: task,\n });\n return resp.data ?? (resp as unknown as Task);\n },\n\n /** Update a task (partial). */\n async update(taskId: string, fields: TaskUpdateParams): Promise<Task> {\n const resp = await request<{ data?: Task }>(baseUrl, `/api/v1/tasks/${taskId}`, apiKey, {\n method: \"PATCH\",\n body: fields,\n });\n return resp.data ?? (resp as unknown as Task);\n },\n\n /** Delete a task. */\n async delete(taskId: string): Promise<{ data: { deleted: boolean } }> {\n return request(baseUrl, `/api/v1/tasks/${taskId}`, apiKey, { method: \"DELETE\" });\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\n/**\n * App lifecycle. `draft` serves no traffic; `published` is live; `suspended` is a\n * platform action and cannot be set through this client; `archived` is retired.\n *\n * `active` is the pre-D12c spelling of `published` and is still accepted on write\n * by the backend, so an older caller keeps working. Prefer `published`.\n */\nexport type AppStatus = \"draft\" | \"published\" | \"suspended\" | \"archived\";\n\n/** Install / consent lifecycle for one client org. */\nexport type InstallStatus = \"pending\" | \"consented\" | \"revoked\" | \"suspended\" | \"expired\";\n\n/** An app you build on graph8, which your customers install into their workspace. */\nexport interface App {\n app_id: string;\n builder_org_id: string;\n /** Stable identifier, unique within your organization — not globally. */\n slug: string;\n name: string;\n status: string;\n /** https-only origins a BROWSER app token may be presented from. */\n registered_origins: string[];\n default_hostname: string | null;\n created_at: string | null;\n archived_at: string | null;\n}\n\n/** One client organization's installation of your app. */\nexport interface AppInstallation {\n install_id: string;\n app_id: string;\n client_org_id: string;\n status: string;\n consented_scopes: string[];\n created_at: string | null;\n revoked_at: string | null;\n}\n\n/** Credits your app consumed in one calendar month. */\nexport interface AppUsageSummary {\n app_id: string;\n /** Calendar month, `YYYY-MM`. */\n period: string;\n total_credits: number;\n event_count: number;\n /**\n * Credits per CLIENT org — whose data was touched, not who paid. You pay for\n * all of them, so a breakdown keyed on the payer would collapse into one row.\n */\n per_client_credits: Record<string, number>;\n}\n\n/** A hard credit cap. `null` from `getLimit` means uncapped. */\nexport interface AppLimit {\n app_id: string;\n window: string;\n credit_cap: number;\n created_at: string | null;\n}\n\nexport interface AppCreateParams {\n name: string;\n /** Unique within your organization. Lowercased on write. */\n slug: string;\n /**\n * https-only origins a browser app token may be presented from. An `http://`\n * origin is rejected at registration — a bearer token sent over plain http is\n * a token disclosed to the network.\n */\n registered_origins?: string[];\n}\n\n/**\n * Apps API — manage the apps you build on graph8 (M6). Requires an API key\n * (server-side). On the hardened HTTP core: throws a typed `G8Error` on failure\n * and retries transient errors.\n *\n * PREVIEW AND GATED. Every endpoint returns 403 `builder_not_allowlisted` unless\n * the platform is enabled AND your organization is on the builder allowlist.\n * Both are off by default, so a call fails fast and loudly rather than returning\n * an empty list that looks like \"you have no apps\".\n *\n * A note on 404s: an app id belonging to ANOTHER organization returns 404, not\n * 403 — the two are deliberately indistinguishable so app ids cannot be\n * enumerated by guessing.\n *\n * Backed by:\n * GET /api/v1/apps — list your apps\n * POST /api/v1/apps — create one (starts in `draft`)\n * GET /api/v1/apps/{app_id} — fetch one\n * POST /api/v1/apps/{app_id}/status — move it through its lifecycle\n * GET /api/v1/apps/{app_id}/installs — who installed it, and their consent\n * GET /api/v1/apps/{app_id}/usage — credits consumed in a month\n * GET /api/v1/apps/{app_id}/limit — the hard cap, or null\n */\nexport const createAppsClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n /** List your organization's apps, newest first. */\n async list(): Promise<{ data: App[] }> {\n return request(baseUrl, \"/api/v1/apps\", apiKey);\n },\n\n /** Fetch one of your apps. Throws `G8Error` (404) if it is not yours. */\n async get(appId: string): Promise<App> {\n const resp = await request<{ data?: App }>(baseUrl, `/api/v1/apps/${appId}`, apiKey);\n return resp.data ?? (resp as unknown as App);\n },\n\n /**\n * Create an app. It starts in `draft` and serves no traffic until published.\n * Throws `G8Error` (409) when the slug is already taken in your org.\n */\n async create(params: AppCreateParams): Promise<App> {\n const resp = await request<{ data?: App }>(baseUrl, \"/api/v1/apps\", apiKey, {\n method: \"POST\",\n body: {\n name: params.name,\n slug: params.slug,\n registered_origins: params.registered_origins ?? [],\n },\n });\n return resp.data ?? (resp as unknown as App);\n },\n\n /**\n * Move an app through its lifecycle: `draft` → `published` → `archived`.\n *\n * `suspended` is excluded from the parameter type on purpose: it is the\n * platform's kill switch for an abusive app, the backend refuses it with a\n * 403, and a builder who could set it could also unset it.\n */\n async setStatus(appId: string, status: Exclude<AppStatus, \"suspended\">): Promise<App> {\n const resp = await request<{ data?: App }>(baseUrl, `/api/v1/apps/${appId}/status`, apiKey, {\n method: \"POST\",\n body: { status },\n });\n return resp.data ?? (resp as unknown as App);\n },\n\n /**\n * The client organizations that installed this app, and their consent state.\n * Includes `pending` and `revoked` installs — if your app cannot reach a\n * tenant, this is where you see why.\n */\n async listInstalls(appId: string): Promise<{ data: AppInstallation[] }> {\n return request(baseUrl, `/api/v1/apps/${appId}/installs`, apiKey);\n },\n\n /**\n * Credits this app consumed in a calendar month, broken down per client org.\n * Defaults to the current month. A month with no usage returns zeroes, not a\n * 404 — \"nothing happened\" is an answer.\n */\n async usage(appId: string, period?: string): Promise<AppUsageSummary> {\n const resp = await request<{ data?: AppUsageSummary }>(\n baseUrl,\n `/api/v1/apps/${appId}/usage`,\n apiKey,\n { query: period ? { period } : undefined },\n );\n return resp.data ?? (resp as unknown as AppUsageSummary);\n },\n\n /**\n * This app's hard credit cap, or `null` when it is uncapped.\n *\n * `null` is the real answer, not an empty object: there is no \"unlimited\"\n * sentinel, so an accidental zero can never read as \"no limit\".\n */\n async getLimit(appId: string): Promise<AppLimit | null> {\n const resp = await request<{ data?: AppLimit | null }>(\n baseUrl,\n `/api/v1/apps/${appId}/limit`,\n apiKey,\n );\n return resp.data ?? null;\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\n/** A field (column) definition on contacts or companies — base or custom. */\nexport interface Field {\n id: number | null;\n title: string;\n /** Internal column name (slug). */\n name: string | null;\n /** Currently only \"text\" is supported; other types may be added later. */\n data_type: string;\n /** True if available org-wide, false if list-specific. */\n is_global: boolean;\n}\n\n/** Created custom field (column). */\nexport interface CreatedField {\n id: number;\n title: string;\n data_type: string;\n name: string;\n list_id: number | null;\n is_global: boolean;\n}\n\nexport interface FieldCreateParams {\n title: string;\n /** \"contacts\" or \"companies\". Defaults to \"contacts\" on the backend. */\n entity?: \"contacts\" | \"companies\";\n /** \"text\" (default) — additional types may be added later. */\n data_type?: string;\n /** If set, the field is list-specific. Omit for org-wide global field. */\n list_id?: number;\n}\n\nexport interface FieldDeleteParams {\n entity?: \"contacts\" | \"companies\";\n /**\n * Optional list scope guard. If provided, the column must belong to this\n * list or the request returns 404. Use this to prevent accidentally\n * deleting a column on a different list in the same org.\n */\n list_id?: number;\n}\n\nexport interface SetFieldValueParams {\n /** Contact ID (entity='contacts') or company ID (entity='companies'). */\n record_id: number;\n /** Value to write. Pass null/undefined to clear the field. */\n value?: string | null;\n entity?: \"contacts\" | \"companies\";\n}\n\n/**\n * Fields API - manage custom fields (columns) on contacts and companies.\n * Requires API key (server-side). On the hardened HTTP core: throws a typed\n * `G8Error` on failure and retries transient errors.\n *\n * Backed by:\n * GET /api/v1/fields — list contact fields\n * GET /api/v1/fields/companies — list company fields\n * POST /api/v1/fields — create a custom field\n * DELETE /api/v1/fields/{column_id} — delete a custom field\n * PATCH /api/v1/fields/{column_id}/values — set value on a single record\n */\nexport const createFieldsClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n /** List contact fields (base + custom). Pass listId to include list-specific custom fields. */\n async listContactFields(listId?: number): Promise<{ data: Field[] }> {\n return request(baseUrl, \"/api/v1/fields\", apiKey, { query: listId != null ? { list_id: listId } : undefined });\n },\n\n /** List company fields (base + custom). Pass listId to include list-specific custom fields. */\n async listCompanyFields(listId?: number): Promise<{ data: Field[] }> {\n return request(baseUrl, \"/api/v1/fields/companies\", apiKey, {\n query: listId != null ? { list_id: listId } : undefined,\n });\n },\n\n /** Create a custom field on contacts (default) or companies. */\n async create(params: FieldCreateParams): Promise<CreatedField> {\n const resp = await request<{ data?: CreatedField }>(baseUrl, \"/api/v1/fields\", apiKey, {\n method: \"POST\",\n body: {\n title: params.title,\n data_type: params.data_type || \"text\",\n list_id: params.list_id ?? null,\n entity: params.entity || \"contacts\",\n },\n });\n return resp.data ?? (resp as unknown as CreatedField);\n },\n\n /** Delete a custom field (soft-delete). Pass list_id to scope-guard against cross-list deletion. */\n async delete(\n columnId: number,\n params: FieldDeleteParams = {},\n ): Promise<{ data: { column_id: number; deleted: boolean } }> {\n const query: Record<string, unknown> = { entity: params.entity || \"contacts\" };\n if (params.list_id != null) query.list_id = params.list_id;\n return request(baseUrl, `/api/v1/fields/${columnId}`, apiKey, { method: \"DELETE\", query });\n },\n\n /** Set a custom field value on a single contact or company row. Pass value=null to clear. */\n async setValue(\n columnId: number,\n params: SetFieldValueParams,\n ): Promise<{ data: { column_id: number; record_id: number; updated: boolean } }> {\n return request(baseUrl, `/api/v1/fields/${columnId}/values`, apiKey, {\n method: \"PATCH\",\n body: { record_id: params.record_id, value: params.value ?? null, entity: params.entity || \"contacts\" },\n });\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\n/** A custom object type in your workspace — a record type you define. */\nexport interface CustomObject {\n /** Stable API slug, e.g. `invoices`. */\n slug: string;\n singular_noun: string;\n plural_noun: string;\n /** True for object types graph8 ships; system objects cannot be deleted. */\n is_system: boolean;\n is_archived: boolean;\n}\n\n/**\n * One attribute on a custom object, and the rules its values must satisfy.\n *\n * `attribute_type` is one of 16 supported types and decides how a value is\n * validated and canonicalized: `text`, `number`, `currency`, `date`,\n * `timestamp`, `checkbox`, `select`, `status`, `email_address`, `phone_number`,\n * `domain`, `location`, `personal_name`, `record_reference`, `rating`,\n * `actor_reference`.\n */\nexport interface CustomObjectAttribute {\n slug: string;\n attribute_type: string;\n is_required: boolean;\n /** No two ACTIVE records may hold the same value. A collision returns 409. */\n is_unique: boolean;\n is_multiselect: boolean;\n /** Type-specific configuration, e.g. the allowed options for a `select`. */\n config: Record<string, unknown>;\n}\n\n/** One record, with its currently active attribute values. */\nexport interface CustomObjectRecord {\n id: string;\n object_slug: string;\n /**\n * Values keyed by attribute slug. Untyped by necessity — the valid keys come\n * from YOUR attribute definitions at runtime, so call `listAttributes` for the\n * schema. Saying `any` here would imply the shape was considered and found to\n * be free.\n */\n values: Record<string, unknown>;\n is_archived: boolean;\n created_at: string | null;\n updated_at: string | null;\n}\n\n/** One generation of one attribute's value. */\nexport interface CustomObjectHistoryEntry {\n attribute: string;\n value: unknown;\n active_from: string | null;\n /** Null means this value is still in force. */\n active_until: string | null;\n actor_id: string | null;\n}\n\nexport interface CustomObjectHistory {\n record_id: string;\n entries: CustomObjectHistoryEntry[];\n}\n\nexport interface ListRecordsParams {\n page?: number;\n /** 1-200, default 50. */\n limit?: number;\n /** Opaque cursor from a prior response. Takes precedence over `page`. */\n cursor?: string;\n}\n\n/**\n * Pagination envelope for record listings.\n *\n * Named distinctly rather than `PaginationMeta`: `deals.ts` and `quotes.ts` each\n * already declare their own structurally identical `PaginationMeta`, and one of\n * them is re-exported from `index.ts` — so a third with that name is a\n * `TS2300: Duplicate identifier` at the package's export site. Consolidating the\n * three into one shared type is worth doing and is not this change.\n */\nexport interface ObjectPagination {\n page: number;\n limit: number;\n total: number;\n has_next: boolean;\n next_cursor: string | null;\n}\n\n/**\n * Custom Objects API — your own record types, their schema, and their records (M6-3).\n * Requires an API key (server-side). On the hardened HTTP core: throws a typed\n * `G8Error` on failure and retries transient errors.\n *\n * PREVIEW AND GATED. Every endpoint returns 403 `app_not_enabled` until the\n * custom-objects surface is switched on for the platform. It is off by default,\n * so a call fails fast rather than returning an empty list that reads as \"you\n * have no objects\".\n *\n * HOW THIS DIFFERS FROM `g8.fields`. A FIELD adds a column to an existing\n * contact or company. A CUSTOM OBJECT is a whole new record type with its own\n * attributes and its own records. Use fields to extend a contact; use custom\n * objects to model an invoice, a shipment, or a subscription.\n *\n * THREE BEHAVIOURS WORTH KNOWING BEFORE YOU WRITE\n *\n * 1. An unknown field is REJECTED (422), not ignored. A typo does not silently\n * lose your data, and the response lists every problem at once so a payload\n * with three mistakes takes one round trip to fix.\n * 2. `update` is a PARTIAL write. Attributes you omit are left alone; sending\n * an explicit `null` CLEARS one. The two are deliberately different.\n * 3. `archive` does not destroy anything. The record leaves listings, stays\n * readable by id, and keeps its history.\n *\n * Backed by:\n * GET /api/v1/objects\n * GET /api/v1/objects/{slug}\n * GET /api/v1/objects/{slug}/attributes\n * GET /api/v1/objects/{slug}/records\n * POST /api/v1/objects/{slug}/records\n * GET /api/v1/objects/{slug}/records/{id}\n * PATCH /api/v1/objects/{slug}/records/{id}\n * DELETE /api/v1/objects/{slug}/records/{id}\n * GET /api/v1/objects/{slug}/records/{id}/history\n */\nexport const createObjectsClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n const encode = (value: string) => encodeURIComponent(value);\n\n return {\n /** List the custom object types in your workspace. */\n async list(): Promise<{ data: CustomObject[] }> {\n return request(baseUrl, \"/api/v1/objects\", apiKey);\n },\n\n /** Fetch one object type by slug. */\n async get(objectSlug: string): Promise<CustomObject> {\n const resp = await request<{ data?: CustomObject }>(\n baseUrl,\n `/api/v1/objects/${encode(objectSlug)}`,\n apiKey,\n );\n return resp.data ?? (resp as unknown as CustomObject);\n },\n\n /** The object's attributes — the schema its records must satisfy. */\n async listAttributes(objectSlug: string): Promise<{ data: CustomObjectAttribute[] }> {\n return request(baseUrl, `/api/v1/objects/${encode(objectSlug)}/attributes`, apiKey);\n },\n\n /** Paginated records with their current values. Archived records are excluded. */\n async listRecords(\n objectSlug: string,\n params: ListRecordsParams = {},\n ): Promise<{ data: CustomObjectRecord[]; pagination?: ObjectPagination }> {\n const query: Record<string, unknown> = {};\n if (params.page != null) query.page = params.page;\n if (params.limit != null) query.limit = params.limit;\n if (params.cursor != null) query.cursor = params.cursor;\n return request(baseUrl, `/api/v1/objects/${encode(objectSlug)}/records`, apiKey, {\n query: Object.keys(query).length ? query : undefined,\n });\n },\n\n /**\n * Create a record. Every field is validated against the object's attributes;\n * an unknown one is a 422 listing every problem at once, and a collision on a\n * unique attribute is a 409.\n */\n async createRecord(\n objectSlug: string,\n values: Record<string, unknown>,\n ): Promise<CustomObjectRecord> {\n const resp = await request<{ data?: CustomObjectRecord }>(\n baseUrl,\n `/api/v1/objects/${encode(objectSlug)}/records`,\n apiKey,\n { method: \"POST\", body: { values } },\n );\n return resp.data ?? (resp as unknown as CustomObjectRecord);\n },\n\n /** Fetch one record with its currently active values. */\n async getRecord(objectSlug: string, recordId: string): Promise<CustomObjectRecord> {\n const resp = await request<{ data?: CustomObjectRecord }>(\n baseUrl,\n `/api/v1/objects/${encode(objectSlug)}/records/${encode(recordId)}`,\n apiKey,\n );\n return resp.data ?? (resp as unknown as CustomObjectRecord);\n },\n\n /**\n * Update a record. PARTIAL: only the attributes you send are touched, so\n * required attributes you omit are left alone rather than reported missing.\n * Sending an explicit `null` CLEARS that attribute.\n *\n * Values are versioned rather than overwritten, so the previous value stays\n * readable through `history`.\n */\n async updateRecord(\n objectSlug: string,\n recordId: string,\n values: Record<string, unknown>,\n ): Promise<CustomObjectRecord> {\n const resp = await request<{ data?: CustomObjectRecord }>(\n baseUrl,\n `/api/v1/objects/${encode(objectSlug)}/records/${encode(recordId)}`,\n apiKey,\n { method: \"PATCH\", body: { values } },\n );\n return resp.data ?? (resp as unknown as CustomObjectRecord);\n },\n\n /**\n * Archive a record. It leaves listings, stays readable by id, and keeps its\n * history and associations. Nothing is destroyed.\n */\n async archiveRecord(objectSlug: string, recordId: string): Promise<CustomObjectRecord> {\n const resp = await request<{ data?: CustomObjectRecord }>(\n baseUrl,\n `/api/v1/objects/${encode(objectSlug)}/records/${encode(recordId)}`,\n apiKey,\n { method: \"DELETE\" },\n );\n return resp.data ?? (resp as unknown as CustomObjectRecord);\n },\n\n /**\n * A record's value timeline, newest first. An entry whose `active_until` is\n * null is the value currently in force.\n */\n async history(\n objectSlug: string,\n recordId: string,\n limit?: number,\n ): Promise<CustomObjectHistory> {\n const resp = await request<{ data?: CustomObjectHistory }>(\n baseUrl,\n `/api/v1/objects/${encode(objectSlug)}/records/${encode(recordId)}/history`,\n apiKey,\n { query: limit != null ? { limit } : undefined },\n );\n return resp.data ?? (resp as unknown as CustomObjectHistory);\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface Deal {\n id: string | null;\n name: string | null;\n description: string | null;\n amount: number | null;\n currency: string | null;\n stage_id: string | null;\n stage_name: string | null;\n pipeline_id: string | null;\n /** Linked company ID (mashup_company_id), if any. */\n company_id: number | string | null;\n owner_id: string | null;\n /** ISO 8601 date. */\n close_date: string | null;\n created_at: string | null;\n updated_at: string | null;\n}\n\nexport interface DealCreateParams {\n name: string;\n /**\n * Deal owner (user id or email). Required.\n */\n owner_id: string;\n /**\n * mashup_contact_ids to attach. Required, at least one. Every contact must\n * belong to the same company, and that company becomes the deal's company.\n * Same id space as the contacts API. Attach is idempotent.\n */\n contact_ids: number[];\n /**\n * Deprecated: the company is derived from `contact_ids`. If supplied it must\n * match the contacts' company, otherwise omit it.\n */\n company_id?: number;\n description?: string;\n /**\n * Monetary value. Required (HTTP 400, marker `amount_required_for_won_stage`)\n * when creating directly in a Closed Won stage; 0 is a valid amount.\n */\n amount?: number;\n /** Default: \"USD\". */\n currency?: string;\n /**\n * Stage ID from the org's pipelines. Must exist and belong to `pipeline_id`\n * when both are sent (HTTP 422 otherwise). Defaults to the default\n * pipeline's first non-closed stage if omitted. Creating directly in a\n * closed stage triggers the closed-stage rules (HTTP 400 with marker\n * `close_date_required_for_won_stage` / `close_date_required_for_lost_stage`\n * / `amount_required_for_won_stage` / `owner_required_for_closed_stage`).\n */\n stage_id?: string;\n /** Pipeline ID. Must exist (HTTP 422 otherwise). Defaults to org's default pipeline if omitted. */\n pipeline_id?: string;\n /**\n * ISO 8601 close date. Required when creating directly in a Closed Won or\n * Closed Lost stage; past dates are allowed (backdating is supported).\n */\n close_date?: string;\n /**\n * Set true to create a deal even if one already exists for the company.\n * By default the API rejects a duplicate with HTTP 409.\n */\n allow_duplicate?: boolean;\n}\n\nexport interface DealUpdateParams {\n name?: string;\n description?: string;\n amount?: number;\n currency?: string;\n /**\n * New stage ID. Must be an existing stage in the deal's pipeline (HTTP 422\n * otherwise). Moving a deal INTO a Closed Won stage requires an effective\n * close_date AND amount; Closed Lost requires an effective close_date;\n * either requires an owner (\"effective\" = the value in this call, else the\n * deal's existing value). Violations return HTTP 400 with a marker-prefixed\n * detail: `close_date_required_for_won_stage`,\n * `close_date_required_for_lost_stage`, `amount_required_for_won_stage`,\n * `owner_required_for_closed_stage`. Re-submitting the deal's current stage\n * is never gated.\n */\n stage_id?: string;\n /**\n * ISO 8601 close date. Explicit null clears it, except on a deal sitting in\n * a closed stage (HTTP 400, closed-stage markers above).\n */\n close_date?: string | null;\n /**\n * Reassign the deal owner (user id or email). Pass null to clear, except on\n * a deal sitting in a closed stage (HTTP 400, marker\n * `owner_required_for_closed_stage`).\n */\n owner_id?: string | null;\n /**\n * mashup_contact_ids to link. Must belong to the deal's company (if the deal\n * has none yet, the added contacts' company is adopted); a cross-company add\n * is rejected. Idempotent.\n */\n add_contact_ids?: number[];\n /** mashup_contact_ids to unlink. Idempotent. */\n remove_contact_ids?: number[];\n}\n\nexport interface DealListParams {\n page?: number;\n limit?: number;\n stage_id?: string;\n pipeline_id?: string;\n /** Substring search on deal name. */\n search?: string;\n}\n\nexport interface PipelineStage {\n id: string;\n name: string;\n probability: number | null;\n position: number | null;\n stage_type: string | null;\n color: string | null;\n required_elements: string[];\n recommended_elements: string[];\n channel_scripts: Record<string, string>;\n}\n\nexport interface Pipeline {\n id: string;\n name: string;\n is_default: boolean;\n stages: PipelineStage[];\n}\n\n/** Trimmed deal shape returned by /contacts/{id}/deals and /companies/{id}/deals. */\nexport interface ContactDeal {\n deal_id: string | null;\n name: string | null;\n stage: string | null;\n value: number | null;\n currency: string | null;\n role: string | null;\n pipeline_id: string | null;\n close_date: string | null;\n owner_id: string | null;\n created_at: string | null;\n}\n\nexport interface PaginationMeta {\n page: number;\n limit: number;\n total: number;\n has_next: boolean;\n}\n\n/**\n * Deals API - CRUD for deals + pipelines. Requires API key (server-side).\n *\n * Backed by:\n * GET /api/v1/deals/pipelines\n * GET /api/v1/deals\n * POST /api/v1/deals\n * GET /api/v1/deals/{deal_id}\n * PATCH /api/v1/deals/{deal_id}\n * DELETE /api/v1/deals/{deal_id}\n * GET /api/v1/contacts/{contact_id}/deals\n * GET /api/v1/companies/{company_id}/deals\n */\nexport const createDealsClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n /** List all deal pipelines and their stages. */\n async pipelines(): Promise<{ data: Pipeline[] }> {\n return request(baseUrl, \"/api/v1/deals/pipelines\", apiKey);\n },\n\n /** List deals org-wide with optional filters and pagination. */\n async list(params: DealListParams = {}): Promise<{ data: Deal[]; pagination?: PaginationMeta }> {\n return request(baseUrl, \"/api/v1/deals\", apiKey, { query: params as Record<string, unknown> });\n },\n\n /** Create a new deal. */\n async create(deal: DealCreateParams): Promise<Deal> {\n const resp = await request<{ data?: Deal }>(baseUrl, \"/api/v1/deals\", apiKey, { method: \"POST\", body: deal });\n return resp.data ?? (resp as unknown as Deal);\n },\n\n /** Get a single deal by ID. */\n async get(dealId: string): Promise<Deal> {\n const resp = await request<{ data?: Deal }>(baseUrl, `/api/v1/deals/${dealId}`, apiKey);\n return resp.data ?? (resp as unknown as Deal);\n },\n\n /** Update a deal (partial). */\n async update(dealId: string, fields: DealUpdateParams): Promise<Deal> {\n const resp = await request<{ data?: Deal }>(baseUrl, `/api/v1/deals/${dealId}`, apiKey, {\n method: \"PATCH\",\n body: fields,\n });\n return resp.data ?? (resp as unknown as Deal);\n },\n\n /** Delete a deal. */\n async delete(dealId: string): Promise<{ data: { deleted: boolean } }> {\n return request(baseUrl, `/api/v1/deals/${dealId}`, apiKey, { method: \"DELETE\" });\n },\n\n /** Get all deals associated with a contact. */\n async forContact(contactId: number): Promise<{ data: ContactDeal[] }> {\n return request(baseUrl, `/api/v1/contacts/${contactId}/deals`, apiKey);\n },\n\n /** Get all deals associated with a company. */\n async forCompany(companyId: number): Promise<{ data: ContactDeal[] }> {\n return request(baseUrl, `/api/v1/companies/${companyId}/deals`, apiKey);\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\n/**\n * Inbox channel — `email`, `sms`, or `linkedin` (HeyReach internally).\n * Defaults to `email` on routes that accept this as a query param.\n */\nexport type InboxChannel = \"email\" | \"sms\" | \"linkedin\";\n\nexport interface InboxContact {\n name?: string | null;\n email?: string | null;\n company?: string | null;\n [key: string]: unknown;\n}\n\nexport interface InboxTag {\n id: string;\n name: string;\n}\n\nexport interface InboxAssignee {\n email: string;\n name?: string | null;\n [key: string]: unknown;\n}\n\nexport interface InboxMessage {\n message_id: string | null;\n from_address: string | null;\n to_addresses: string[];\n content: string | null;\n /** \"USER\", \"AI\", or \"OTHER\". */\n responder: string | null;\n date: string | null;\n is_draft: boolean;\n}\n\nexport interface InboxThread {\n id: string;\n /** \"email\", \"sms\", or \"linkedin\". */\n channel: string;\n subject: string | null;\n contact: InboxContact | null;\n messages: InboxMessage[];\n /** \"open\", \"responded\", \"ai_responded\", etc. */\n status: string | null;\n tags: InboxTag[];\n assignees: InboxAssignee[];\n created_at: string | null;\n updated_at: string | null;\n}\n\nexport interface InboxListParams {\n channel?: InboxChannel;\n sequence_id?: string;\n /** Filter by thread status (e.g. \"open\", \"responded\", \"ai_responded\"). */\n status?: string;\n /** Filter by assignee email. */\n assignee?: string;\n /** Filter by tag ID. */\n tag?: string;\n page?: number;\n /** Default 50, max 100. */\n page_size?: number;\n}\n\nexport interface InboxAssignResult {\n assigned: boolean;\n assignee: string;\n count: number;\n [key: string]: unknown;\n}\n\nexport interface InboxTagResult {\n tagged: boolean;\n tag_count: number;\n [key: string]: unknown;\n}\n\nexport interface InboxDraft {\n /** HTML body. */\n content: string;\n /** Plain-text alternative, if available. */\n plain_content: string | null;\n /** Credits charged for AI generation. */\n credits_charged: number;\n}\n\nexport interface InboxSendParams {\n body: string;\n channel: InboxChannel;\n subject?: string;\n /** Recipient override — email, phone, or LinkedIn ID. */\n to?: string;\n /** Sender override — mailbox email, Twilio number, or LinkedIn account ID. */\n from_address?: string;\n}\n\nexport interface InboxSendResult {\n message_id: string | null;\n status: string;\n channel: string;\n}\n\n/**\n * Inbox API — read + reply to multi-channel inbox threads (email, SMS, LinkedIn/HeyReach).\n * Requires API key (server-side). On the hardened HTTP core: throws a typed\n * `G8Error` on failure (e.g. 402 when an AI draft exceeds your credit balance)\n * and retries transient errors.\n *\n * Backed by:\n * GET /api/v1/inbox\n * GET /api/v1/inbox/{reply_id}\n * POST /api/v1/inbox/{reply_id}/assign\n * POST /api/v1/inbox/{reply_id}/tag\n * GET /api/v1/inbox/{reply_id}/draft (charges credits, 402 on insufficient balance)\n * POST /api/v1/inbox/{reply_id}/send\n */\nexport const createInboxClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n /** List inbox threads across email, SMS, and LinkedIn. */\n async list(params: InboxListParams = {}): Promise<{ data: InboxThread[] }> {\n return request(baseUrl, \"/api/v1/inbox\", apiKey, { query: params as Record<string, unknown> });\n },\n\n /** Get a single inbox thread. Defaults to email channel. */\n async get(replyId: string, channel: InboxChannel = \"email\"): Promise<InboxThread> {\n const resp = await request<{ data?: InboxThread }>(baseUrl, `/api/v1/inbox/${replyId}`, apiKey, {\n query: { channel },\n });\n return resp.data ?? (resp as unknown as InboxThread);\n },\n\n /** Assign a user to an inbox thread. */\n async assign(replyId: string, assigneeEmail: string, channel: InboxChannel = \"email\"): Promise<InboxAssignResult> {\n const resp = await request<{ data?: InboxAssignResult }>(baseUrl, `/api/v1/inbox/${replyId}/assign`, apiKey, {\n method: \"POST\",\n query: { channel },\n body: { assignee_email: assigneeEmail },\n });\n return resp.data ?? (resp as unknown as InboxAssignResult);\n },\n\n /** Attach tag IDs to an inbox thread. */\n async tag(replyId: string, tagIds: string[], channel: InboxChannel = \"email\"): Promise<InboxTagResult> {\n const resp = await request<{ data?: InboxTagResult }>(baseUrl, `/api/v1/inbox/${replyId}/tag`, apiKey, {\n method: \"POST\",\n query: { channel },\n body: { tag_ids: tagIds },\n });\n return resp.data ?? (resp as unknown as InboxTagResult);\n },\n\n /**\n * Generate an AI draft reply for a thread.\n * Charges credits — server returns 402 if balance is insufficient.\n */\n async draft(replyId: string, channel: InboxChannel = \"email\"): Promise<InboxDraft> {\n const resp = await request<{ data?: InboxDraft }>(baseUrl, `/api/v1/inbox/${replyId}/draft`, apiKey, {\n query: { channel },\n });\n return resp.data ?? (resp as unknown as InboxDraft);\n },\n\n /** Send a reply through email, SMS, or LinkedIn. */\n async send(replyId: string, payload: InboxSendParams): Promise<InboxSendResult> {\n const resp = await request<{ data?: InboxSendResult }>(baseUrl, `/api/v1/inbox/${replyId}/send`, apiKey, {\n method: \"POST\",\n body: payload,\n });\n return resp.data ?? (resp as unknown as InboxSendResult);\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface QuoteLineItem {\n product_id?: string | null;\n name?: string | null;\n description?: string | null;\n quantity: number;\n unit_price: number;\n currency?: string | null;\n recurring?: boolean | null;\n}\n\nexport type QuoteStatus = \"draft\" | \"sent\" | \"signed\" | \"declined\" | \"expired\" | \"void\";\n\nexport interface QuoteSummary {\n id: string;\n name: string | null;\n status: QuoteStatus;\n total: number | null;\n currency: string | null;\n contact_id: number | null;\n company_id: number | null;\n expires_at: string | null;\n created_at: string | null;\n updated_at: string | null;\n signing_url: string | null;\n payment_link_url: string | null;\n}\n\nexport interface QuoteDetail extends QuoteSummary {\n line_items: QuoteLineItem[];\n notes: string | null;\n recipient_email: string | null;\n signed_at: string | null;\n sent_at: string | null;\n}\n\nexport interface QuoteCreateParams {\n contact_id?: number;\n company_id?: number;\n line_items: QuoteLineItem[];\n currency?: string;\n notes?: string;\n /** ISO 8601 timestamp. */\n expires_at?: string;\n name?: string;\n}\n\nexport interface QuoteUpdateParams {\n line_items?: QuoteLineItem[];\n notes?: string;\n expires_at?: string;\n name?: string;\n}\n\nexport interface QuoteSendParams {\n recipient_email: string;\n /** Whether to attach the signing link. Defaults to true. */\n send_signing_link?: boolean;\n}\n\nexport interface QuoteListParams {\n status?: QuoteStatus;\n contact_id?: number;\n company_id?: number;\n page?: number;\n limit?: number;\n}\n\nexport interface QuotableProduct {\n id: string;\n name: string;\n description: string | null;\n unit_price: number;\n currency: string;\n recurring: boolean;\n}\n\nexport interface QuoteSettings {\n default_currency: string;\n default_tax_rate: number | null;\n payment_providers: string[];\n logo_url: string | null;\n signature_required: boolean;\n}\n\nexport interface PaginationMeta {\n page: number;\n limit: number;\n total: number;\n has_next: boolean;\n}\n\n/**\n * Quotes API - quote-to-cash lifecycle. Requires API key (server-side). On the\n * hardened HTTP core: throws a typed `G8Error` on failure and retries transient\n * errors.\n *\n * Backed by:\n * GET /api/v1/quotes\n * GET /api/v1/quotes/{id}\n * POST /api/v1/quotes\n * PUT /api/v1/quotes/{id}\n * DELETE /api/v1/quotes/{id}\n * POST /api/v1/quotes/{id}/duplicate\n * POST /api/v1/quotes/{id}/edit-as-draft\n * POST /api/v1/quotes/{id}/send\n * GET /api/v1/quotable-products\n * GET /api/v1/quote-settings\n * GET /api/v1/contacts/{contact_id}/quotes\n * GET /api/v1/companies/{company_id}/quotes\n */\nexport const createQuotesClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n /** List quotes org-wide with optional filters and pagination. */\n async list(params: QuoteListParams = {}): Promise<{ data: QuoteSummary[]; pagination?: PaginationMeta }> {\n return request(baseUrl, \"/api/v1/quotes\", apiKey, { query: params as Record<string, unknown> });\n },\n\n /** Get full details for a single quote. */\n async get(quoteId: string): Promise<QuoteDetail> {\n const resp = await request<{ data?: QuoteDetail }>(baseUrl, `/api/v1/quotes/${quoteId}`, apiKey);\n return resp.data ?? (resp as unknown as QuoteDetail);\n },\n\n /** Create a new quote from line items. */\n async create(quote: QuoteCreateParams): Promise<QuoteDetail> {\n const resp = await request<{ data?: QuoteDetail }>(baseUrl, \"/api/v1/quotes\", apiKey, {\n method: \"POST\",\n body: quote,\n });\n return resp.data ?? (resp as unknown as QuoteDetail);\n },\n\n /** Update a draft quote (line items, expiry, notes). */\n async update(quoteId: string, fields: QuoteUpdateParams): Promise<QuoteDetail> {\n const resp = await request<{ data?: QuoteDetail }>(baseUrl, `/api/v1/quotes/${quoteId}`, apiKey, {\n method: \"PUT\",\n body: fields,\n });\n return resp.data ?? (resp as unknown as QuoteDetail);\n },\n\n /** Delete a draft quote (irreversible). */\n async delete(quoteId: string): Promise<{ data: { deleted: boolean } }> {\n return request(baseUrl, `/api/v1/quotes/${quoteId}`, apiKey, { method: \"DELETE\" });\n },\n\n /** Duplicate an existing quote (all line items copied into a new draft). */\n async duplicate(quoteId: string): Promise<QuoteDetail> {\n const resp = await request<{ data?: QuoteDetail }>(baseUrl, `/api/v1/quotes/${quoteId}/duplicate`, apiKey, {\n method: \"POST\",\n body: {},\n });\n return resp.data ?? (resp as unknown as QuoteDetail);\n },\n\n /** Convert a signed / sent quote back to editable draft state. */\n async editAsDraft(quoteId: string): Promise<{ data: { id: string; status: string } }> {\n return request(baseUrl, `/api/v1/quotes/${quoteId}/edit-as-draft`, apiKey, { method: \"POST\", body: {} });\n },\n\n /** Send a quote to its recipient via email (with signature + optional payment link). */\n async send(quoteId: string, params: QuoteSendParams): Promise<QuoteDetail> {\n const resp = await request<{ data?: QuoteDetail }>(baseUrl, `/api/v1/quotes/${quoteId}/send`, apiKey, {\n method: \"POST\",\n body: params,\n });\n return resp.data ?? (resp as unknown as QuoteDetail);\n },\n\n /** List products available for line items. */\n async products(): Promise<{ data: QuotableProduct[] }> {\n return request(baseUrl, \"/api/v1/quotable-products\", apiKey);\n },\n\n /** Get org-level quote settings (currency, tax rate, payment providers, logo). */\n async settings(): Promise<QuoteSettings> {\n const resp = await request<{ data?: QuoteSettings }>(baseUrl, \"/api/v1/quote-settings\", apiKey);\n return resp.data ?? (resp as unknown as QuoteSettings);\n },\n\n /** Get all quotes associated with a contact. */\n async forContact(contactId: number): Promise<{ data: QuoteSummary[] }> {\n return request(baseUrl, `/api/v1/contacts/${contactId}/quotes`, apiKey);\n },\n\n /** Get all quotes associated with a company. */\n async forCompany(companyId: number): Promise<{ data: QuoteSummary[] }> {\n return request(baseUrl, `/api/v1/companies/${companyId}/quotes`, apiKey);\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\n/**\n * Stage Checklist v2 pipelines. A \"pipeline\" here is a multi-stage workflow\n * (with evidence requirements and per-channel scripts) that lives parallel\n * to deal pipelines. Use `g8.deals.pipelines()` for deal-pipeline reads.\n */\nexport interface StagePipelineStage {\n id: string;\n name: string;\n position: number;\n /** Evidence keys that must be collected before the stage can be marked done. */\n required_elements: string[];\n /** Evidence keys that are recommended but not enforced. */\n recommended_elements: string[];\n /** Channel-specific scripts (call, email, linkedin, etc.). */\n channel_scripts: Record<string, string>;\n color: string | null;\n stage_type: string | null;\n}\n\nexport interface StagePipeline {\n id: string;\n name: string;\n target: string | null;\n is_default: boolean;\n stages: StagePipelineStage[];\n created_at: string | null;\n updated_at: string | null;\n}\n\nexport interface StagePipelineCreateParams {\n name: string;\n target?: string;\n /** If true, creates an empty pipeline (no default stages). */\n blank?: boolean;\n}\n\nexport interface StagePipelineUpdateParams {\n name?: string;\n target?: string;\n}\n\nexport interface StageCreateParams {\n name: string;\n position?: number;\n required_elements?: string[];\n recommended_elements?: string[];\n channel_scripts?: Record<string, string>;\n color?: string;\n stage_type?: string;\n}\n\nexport interface StageUpdateParams {\n name?: string;\n position?: number;\n required_elements?: string[];\n recommended_elements?: string[];\n channel_scripts?: Record<string, string>;\n color?: string;\n}\n\nexport interface EvidenceKey {\n key: string;\n label: string;\n description: string;\n category: string;\n}\n\nexport interface PipelineSuggestion {\n suggestion_id: string;\n name: string;\n target: string;\n stages: Array<{ name: string; required_elements: string[]; channel_scripts: Record<string, string> }>;\n rationale: string;\n}\n\n/**\n * Stage Checklist Pipelines API - workflow pipelines with evidence + scripts.\n * Requires API key (server-side). On the hardened HTTP core: throws a typed\n * `G8Error` on failure and retries transient errors.\n *\n * Backed by:\n * GET /api/v1/pipelines\n * GET /api/v1/pipelines/{id}\n * GET /api/v1/pipelines/evidence-library\n * POST /api/v1/pipelines\n * PUT /api/v1/pipelines/{id}\n * DELETE /api/v1/pipelines/{id}\n * POST /api/v1/pipelines/{id}/stages\n * PUT /api/v1/pipelines/{id}/stages/{stage_id}\n * DELETE /api/v1/pipelines/{id}/stages/{stage_id}\n * PUT /api/v1/pipelines/{id}/stages/reorder\n * POST /api/v1/pipelines/suggest\n * POST /api/v1/pipelines/from-suggestion\n */\nexport const createPipelinesClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n /** List all stage-checklist pipelines with stages, evidence, scripts. */\n async list(): Promise<{ data: StagePipeline[] }> {\n return request(baseUrl, \"/api/v1/pipelines\", apiKey);\n },\n\n /** Get a single pipeline by ID. */\n async get(pipelineId: string): Promise<StagePipeline> {\n const resp = await request<{ data?: StagePipeline }>(baseUrl, `/api/v1/pipelines/${pipelineId}`, apiKey);\n return resp.data ?? (resp as unknown as StagePipeline);\n },\n\n /** Get the canonical evidence-key library (used when defining stages). */\n async evidenceLibrary(): Promise<{ data: EvidenceKey[] }> {\n return request(baseUrl, \"/api/v1/pipelines/evidence-library\", apiKey);\n },\n\n /** Create a new pipeline. Defaults to a templated set of stages; pass blank=true for empty. */\n async create(params: StagePipelineCreateParams): Promise<StagePipeline> {\n const resp = await request<{ data?: StagePipeline }>(baseUrl, \"/api/v1/pipelines\", apiKey, {\n method: \"POST\",\n body: params,\n });\n return resp.data ?? (resp as unknown as StagePipeline);\n },\n\n /** Update pipeline metadata (name, target). */\n async update(pipelineId: string, fields: StagePipelineUpdateParams): Promise<StagePipeline> {\n const resp = await request<{ data?: StagePipeline }>(baseUrl, `/api/v1/pipelines/${pipelineId}`, apiKey, {\n method: \"PUT\",\n body: fields,\n });\n return resp.data ?? (resp as unknown as StagePipeline);\n },\n\n /** Delete a pipeline. Only allowed when no deals reference it. */\n async delete(pipelineId: string): Promise<{ data: { deleted: boolean } }> {\n return request(baseUrl, `/api/v1/pipelines/${pipelineId}`, apiKey, { method: \"DELETE\" });\n },\n\n /** Add a new stage to a pipeline. */\n async createStage(pipelineId: string, stage: StageCreateParams): Promise<StagePipelineStage> {\n const resp = await request<{ data?: StagePipelineStage }>(\n baseUrl,\n `/api/v1/pipelines/${pipelineId}/stages`,\n apiKey,\n { method: \"POST\", body: stage },\n );\n return resp.data ?? (resp as unknown as StagePipelineStage);\n },\n\n /** Update a stage (evidence, scripts, position). */\n async updateStage(pipelineId: string, stageId: string, fields: StageUpdateParams): Promise<StagePipelineStage> {\n const resp = await request<{ data?: StagePipelineStage }>(\n baseUrl,\n `/api/v1/pipelines/${pipelineId}/stages/${stageId}`,\n apiKey,\n { method: \"PUT\", body: fields },\n );\n return resp.data ?? (resp as unknown as StagePipelineStage);\n },\n\n /** Reorder stages within a pipeline (pass full ordered list of stage IDs). */\n async reorderStages(pipelineId: string, stageIds: string[]): Promise<{ data: StagePipelineStage[] }> {\n return request(baseUrl, `/api/v1/pipelines/${pipelineId}/stages/reorder`, apiKey, {\n method: \"PUT\",\n body: { stage_ids: stageIds },\n });\n },\n\n /** Delete a stage from a pipeline. */\n async deleteStage(pipelineId: string, stageId: string): Promise<{ data: { deleted: boolean } }> {\n return request(baseUrl, `/api/v1/pipelines/${pipelineId}/stages/${stageId}`, apiKey, { method: \"DELETE\" });\n },\n\n /** Get an AI-suggested pipeline based on org context (brand, ICP, messaging). */\n async suggest(): Promise<{ data: PipelineSuggestion }> {\n return request(baseUrl, \"/api/v1/pipelines/suggest\", apiKey, { method: \"POST\", body: {} });\n },\n\n /** Create a real pipeline from an AI suggestion (optionally with overrides). */\n async fromSuggestion(\n suggestionId: string,\n overrides?: Partial<{ name: string; target: string }>,\n ): Promise<StagePipeline> {\n const resp = await request<{ data?: StagePipeline }>(baseUrl, \"/api/v1/pipelines/from-suggestion\", apiKey, {\n method: \"POST\",\n body: { suggestion_id: suggestionId, ...(overrides || {}) },\n });\n return resp.data ?? (resp as unknown as StagePipeline);\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface WorkflowNode {\n id: string;\n type: string;\n config: Record<string, unknown>;\n position?: { x: number; y: number };\n /** Mapping from this node's output to the next node's input. */\n input_mappings?: Record<string, string>;\n}\n\nexport interface WorkflowConnection {\n from_node_id: string;\n to_node_id: string;\n condition?: string | null;\n}\n\nexport interface WorkflowConfig {\n nodes: WorkflowNode[];\n connections: WorkflowConnection[];\n trigger?: Record<string, unknown> | null;\n}\n\nexport interface Workflow {\n id: string;\n name: string;\n description: string | null;\n config: WorkflowConfig;\n trigger_config: Record<string, unknown> | null;\n is_active: boolean;\n created_at: string | null;\n updated_at: string | null;\n}\n\nexport interface WorkflowCreateParams {\n name: string;\n description?: string;\n config?: WorkflowConfig;\n trigger_config?: Record<string, unknown>;\n}\n\nexport interface WorkflowUpdateParams {\n name?: string;\n description?: string;\n config?: WorkflowConfig;\n trigger_config?: Record<string, unknown>;\n is_active?: boolean;\n}\n\nexport interface WorkflowExecution {\n id: string;\n workflow_id: string;\n status: \"pending\" | \"running\" | \"paused\" | \"completed\" | \"failed\" | \"stopped\";\n trigger_payload: Record<string, unknown>;\n outputs: Record<string, unknown>;\n error: string | null;\n started_at: string | null;\n ended_at: string | null;\n}\n\nexport interface NodeTypeSchema {\n type: string;\n label: string;\n description: string;\n config_schema: Record<string, unknown>;\n output_schema: Record<string, unknown>;\n}\n\nexport interface WorkflowListParams {\n page?: number;\n limit?: number;\n is_active?: boolean;\n search?: string;\n}\n\n/**\n * Workflows API - automation workflows with nodes, connections, and execution lifecycle.\n * Requires API key (server-side). On the hardened HTTP core: throws a typed\n * `G8Error` on failure and retries transient errors.\n *\n * The graph8 workflow surface treats the whole workflow definition as a single\n * record updated via `update()` — there are no per-node CRUD endpoints. To edit\n * nodes or connections, fetch the workflow, mutate `config.nodes` / `config.connections`,\n * and PUT it back.\n *\n * Backed by:\n * GET /api/v1/workflows\n * GET /api/v1/workflows/{id}\n * POST /api/v1/workflows\n * PUT /api/v1/workflows/{id}\n * DELETE /api/v1/workflows/{id}\n * POST /api/v1/workflows/validate\n * POST /api/v1/workflows/{id}/execute\n * GET /api/v1/workflows/executions/{exec_id}\n * POST /api/v1/workflows/executions/{exec_id}/pause\n * POST /api/v1/workflows/executions/{exec_id}/resume\n * POST /api/v1/workflows/executions/{exec_id}/stop\n * GET /api/v1/workflows/{id}/trigger-status\n * POST /api/v1/workflows/{id}/trigger-reset\n * GET /api/v1/workflows/node-types/schema\n * GET /api/v1/workflows/integrations/slack/users|channels\n * GET /api/v1/workflows/integrations/roam/users|groups\n * GET /api/v1/workflows/mcp-servers\n * GET /api/v1/workflows/dispositions\n * GET /api/v1/workflows/forms/{form_id}/fields\n */\nexport const createWorkflowsClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n /** List workflows org-wide. */\n async list(params: WorkflowListParams = {}): Promise<{ data: Workflow[] }> {\n return request(baseUrl, \"/api/v1/workflows\", apiKey, { query: params as Record<string, unknown> });\n },\n\n /** Get full workflow definition (nodes, connections, trigger, execution state). */\n async get(workflowId: string): Promise<Workflow> {\n const resp = await request<{ data?: Workflow }>(baseUrl, `/api/v1/workflows/${workflowId}`, apiKey);\n return resp.data ?? (resp as unknown as Workflow);\n },\n\n /** Create a new workflow. */\n async create(params: WorkflowCreateParams): Promise<Workflow> {\n const resp = await request<{ data?: Workflow }>(baseUrl, \"/api/v1/workflows\", apiKey, {\n method: \"POST\",\n body: params,\n });\n return resp.data ?? (resp as unknown as Workflow);\n },\n\n /**\n * Update a workflow. Pass the full `config` (nodes + connections) to edit\n * the graph — node-level CRUD is performed client-side by mutating the\n * config and submitting the updated record.\n */\n async update(workflowId: string, fields: WorkflowUpdateParams): Promise<Workflow> {\n const resp = await request<{ data?: Workflow }>(baseUrl, `/api/v1/workflows/${workflowId}`, apiKey, {\n method: \"PUT\",\n body: fields,\n });\n return resp.data ?? (resp as unknown as Workflow);\n },\n\n /** Delete a workflow. */\n async delete(workflowId: string): Promise<{ data: { deleted: boolean } }> {\n return request(baseUrl, `/api/v1/workflows/${workflowId}`, apiKey, { method: \"DELETE\" });\n },\n\n /** Validate a workflow definition (orphans, dangling connections, required fields). */\n async validate(workflow: { config: WorkflowConfig; trigger_config?: Record<string, unknown> }): Promise<{\n data: { valid: boolean; errors: Array<{ node_id?: string; field?: string; message: string }> };\n }> {\n return request(baseUrl, \"/api/v1/workflows/validate\", apiKey, { method: \"POST\", body: workflow });\n },\n\n /** Execute a workflow immediately with a trigger payload. */\n async execute(workflowId: string, triggerPayload: Record<string, unknown> = {}): Promise<WorkflowExecution> {\n const resp = await request<{ data?: WorkflowExecution }>(\n baseUrl,\n `/api/v1/workflows/${workflowId}/execute`,\n apiKey,\n { method: \"POST\", body: { trigger_payload: triggerPayload } },\n );\n return resp.data ?? (resp as unknown as WorkflowExecution);\n },\n\n /** Get the status + outputs of a single execution. */\n async getExecution(executionId: string): Promise<WorkflowExecution> {\n const resp = await request<{ data?: WorkflowExecution }>(\n baseUrl,\n `/api/v1/workflows/executions/${executionId}`,\n apiKey,\n );\n return resp.data ?? (resp as unknown as WorkflowExecution);\n },\n\n /** Pause a running execution. */\n async pauseExecution(executionId: string): Promise<{ data: { paused: boolean } }> {\n return request(baseUrl, `/api/v1/workflows/executions/${executionId}/pause`, apiKey, { method: \"POST\" });\n },\n\n /** Resume a paused execution. */\n async resumeExecution(executionId: string): Promise<{ data: { resumed: boolean } }> {\n return request(baseUrl, `/api/v1/workflows/executions/${executionId}/resume`, apiKey, { method: \"POST\" });\n },\n\n /** Stop an execution. */\n async stopExecution(executionId: string): Promise<{ data: { stopped: boolean } }> {\n return request(baseUrl, `/api/v1/workflows/executions/${executionId}/stop`, apiKey, { method: \"POST\" });\n },\n\n /** Get the trigger status (e.g. schedule / webhook wiring) for a workflow. */\n async getTriggerStatus(workflowId: string): Promise<{ data: { status: string; details: Record<string, unknown> } }> {\n return request(baseUrl, `/api/v1/workflows/${workflowId}/trigger-status`, apiKey);\n },\n\n /** Reset a workflow's trigger cursor / state. */\n async resetTrigger(workflowId: string): Promise<{ data: { reset: boolean } }> {\n return request(baseUrl, `/api/v1/workflows/${workflowId}/trigger-reset`, apiKey, { method: \"POST\" });\n },\n\n /** Catalog of available workflow node types with config + output schemas. */\n async nodeTypes(params: { type?: string } = {}): Promise<{ data: NodeTypeSchema[] }> {\n return request(baseUrl, \"/api/v1/workflows/node-types/schema\", apiKey, {\n query: params as Record<string, unknown>,\n });\n },\n\n /** Slack users available to workflow nodes. */\n async listSlackUsers(): Promise<{ data: Array<{ id: string; name: string; email: string | null }> }> {\n return request(baseUrl, \"/api/v1/workflows/integrations/slack/users\", apiKey);\n },\n\n /** Slack channels available to workflow nodes. */\n async listSlackChannels(): Promise<{ data: Array<{ id: string; name: string; is_private: boolean }> }> {\n return request(baseUrl, \"/api/v1/workflows/integrations/slack/channels\", apiKey);\n },\n\n /** Roam users available to workflow nodes. */\n async listRoamUsers(): Promise<{ data: Array<{ id: string; name: string; email: string | null }> }> {\n return request(baseUrl, \"/api/v1/workflows/integrations/roam/users\", apiKey);\n },\n\n /** Roam groups available to workflow nodes. */\n async listRoamGroups(): Promise<{ data: Array<{ id: string; name: string }> }> {\n return request(baseUrl, \"/api/v1/workflows/integrations/roam/groups\", apiKey);\n },\n\n /** MCP servers available to workflow nodes. */\n async listMcpServers(): Promise<{ data: Array<{ id: string; name: string; url: string }> }> {\n return request(baseUrl, \"/api/v1/workflows/mcp-servers\", apiKey);\n },\n\n /** Disposition options available to workflow nodes. */\n async listDispositions(): Promise<{ data: Array<{ id: string; label: string }> }> {\n return request(baseUrl, \"/api/v1/workflows/dispositions\", apiKey);\n },\n\n /** Form field definitions for a given form (used by form-trigger nodes). */\n async listFormFields(formId: string): Promise<{ data: Array<{ name: string; type: string; required: boolean }> }> {\n return request(baseUrl, `/api/v1/workflows/forms/${formId}/fields`, apiKey);\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport type SkillType = \"llm\" | \"api\";\n\nexport interface SkillInputField {\n name: string;\n type: string;\n required: boolean;\n description?: string;\n}\n\nexport interface Skill {\n id: string;\n title: string;\n description: string | null;\n type: SkillType;\n /** Only present on `type=llm`. */\n prompt?: string;\n /** Only present on `type=llm`. */\n model?: string;\n /** Only present on `type=api`. */\n method?: \"GET\" | \"POST\" | \"PATCH\" | \"PUT\" | \"DELETE\";\n /** Only present on `type=api`. */\n url?: string;\n /** Only present on `type=api`. */\n headers?: Record<string, string>;\n /** Only present on `type=api`. */\n body_template?: string;\n input_schema: { fields: SkillInputField[] };\n output_schema: Record<string, unknown> | null;\n created_at: string | null;\n updated_at: string | null;\n}\n\nexport interface SkillCreateLLMParams {\n title: string;\n description?: string;\n prompt: string;\n model: string;\n input_schema: { fields: SkillInputField[] };\n output_schema?: Record<string, unknown>;\n}\n\nexport interface SkillCreateAPIParams {\n title: string;\n description?: string;\n method: \"GET\" | \"POST\" | \"PATCH\" | \"PUT\" | \"DELETE\";\n url: string;\n headers?: Record<string, string>;\n body_template?: string;\n response_mapping?: Record<string, string>;\n input_schema: { fields: SkillInputField[] };\n}\n\nexport interface SkillUpdateLLMParams {\n title?: string;\n description?: string;\n prompt?: string;\n model?: string;\n input_schema?: { fields: SkillInputField[] };\n output_schema?: Record<string, unknown>;\n}\n\nexport interface SkillUpdateAPIParams {\n title?: string;\n description?: string;\n method?: \"GET\" | \"POST\" | \"PATCH\" | \"PUT\" | \"DELETE\";\n url?: string;\n headers?: Record<string, string>;\n body_template?: string;\n response_mapping?: Record<string, string>;\n input_schema?: { fields: SkillInputField[] };\n}\n\nexport interface SkillTemplate {\n id: string;\n title: string;\n description: string;\n type: SkillType;\n use_cases: string[];\n}\n\nexport interface SkillListParams {\n page?: number;\n limit?: number;\n type?: SkillType;\n search?: string;\n}\n\n/**\n * Skills API - LLM and API building blocks that workflows compose into runs.\n * Requires API key (server-side).\n *\n * Backed by:\n * GET /api/v1/skills\n * GET /api/v1/skills/{id}\n * GET /api/v1/skills/{id}/variables\n * GET /api/v1/skills/models\n * GET /api/v1/skills/templates\n * POST /api/v1/skills (both llm + api types via `type` field)\n * POST /api/v1/skills/from-template\n * POST /api/v1/skills/from-node\n * PUT /api/v1/skills/{id}\n * DELETE /api/v1/skills/{id}\n * POST /api/v1/skills/validate\n * POST /api/v1/skills/{id}/execute\n */\nexport const createSkillsClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n return {\n /** List skills. */\n async list(params: SkillListParams = {}): Promise<{ data: Skill[] }> {\n return request(baseUrl, \"/api/v1/skills\", apiKey, { query: params as Record<string, unknown> });\n },\n\n /** Get a skill by ID. */\n async get(skillId: string): Promise<Skill> {\n const resp = await request<{ data?: Skill }>(baseUrl, `/api/v1/skills/${skillId}`, apiKey);\n return resp.data ?? (resp as unknown as Skill);\n },\n\n /** Get the variables required by a skill (extracted from prompt or body template). */\n async getVariables(skillId: string): Promise<{ data: SkillInputField[] }> {\n return request(baseUrl, `/api/v1/skills/${skillId}/variables`, apiKey);\n },\n\n /** List available LLM models. */\n async listModels(): Promise<{ data: Array<{ id: string; label: string; provider: string }> }> {\n return request(baseUrl, \"/api/v1/skills/models\", apiKey);\n },\n\n /** List skill templates. */\n async listTemplates(params: { type?: SkillType } = {}): Promise<{ data: SkillTemplate[] }> {\n return request(baseUrl, \"/api/v1/skills/templates\", apiKey, { query: params as Record<string, unknown> });\n },\n\n /** Create an LLM skill (prompt + model + schemas). */\n async createLLM(params: SkillCreateLLMParams): Promise<Skill> {\n const resp = await request<{ data?: Skill }>(baseUrl, \"/api/v1/skills\", apiKey, {\n method: \"POST\",\n body: { ...params, type: \"llm\" },\n });\n return resp.data ?? (resp as unknown as Skill);\n },\n\n /** Create an API skill (HTTP request wrapper). */\n async createAPI(params: SkillCreateAPIParams): Promise<Skill> {\n const resp = await request<{ data?: Skill }>(baseUrl, \"/api/v1/skills\", apiKey, {\n method: \"POST\",\n body: { ...params, type: \"api\" },\n });\n return resp.data ?? (resp as unknown as Skill);\n },\n\n /** Create a skill from a built-in template. */\n async createFromTemplate(params: { title: string; template_id: string; overrides?: Record<string, unknown> }): Promise<Skill> {\n const resp = await request<{ data?: Skill }>(baseUrl, \"/api/v1/skills/from-template\", apiKey, {\n method: \"POST\",\n body: params,\n });\n return resp.data ?? (resp as unknown as Skill);\n },\n\n /** Lift a workflow node into a reusable skill. */\n async createFromNode(params: {\n title: string;\n description?: string;\n node_id: string;\n input_mapping?: Record<string, string>;\n output_mapping?: Record<string, string>;\n }): Promise<Skill> {\n const resp = await request<{ data?: Skill }>(baseUrl, \"/api/v1/skills/from-node\", apiKey, {\n method: \"POST\",\n body: params,\n });\n return resp.data ?? (resp as unknown as Skill);\n },\n\n /** Update an LLM skill. */\n async updateLLM(skillId: string, fields: SkillUpdateLLMParams): Promise<Skill> {\n const resp = await request<{ data?: Skill }>(baseUrl, `/api/v1/skills/${skillId}`, apiKey, {\n method: \"PUT\",\n body: { ...fields, type: \"llm\" },\n });\n return resp.data ?? (resp as unknown as Skill);\n },\n\n /** Update an API skill. */\n async updateAPI(skillId: string, fields: SkillUpdateAPIParams): Promise<Skill> {\n const resp = await request<{ data?: Skill }>(baseUrl, `/api/v1/skills/${skillId}`, apiKey, {\n method: \"PUT\",\n body: { ...fields, type: \"api\" },\n });\n return resp.data ?? (resp as unknown as Skill);\n },\n\n /** Delete a skill (irreversible if in-use workflows exist). */\n async delete(skillId: string): Promise<{ data: { deleted: boolean } }> {\n return request(baseUrl, `/api/v1/skills/${skillId}`, apiKey, { method: \"DELETE\" });\n },\n\n /** Validate a skill definition (without saving). */\n async validate(skill: Partial<Skill>): Promise<{ data: { valid: boolean; errors: string[] } }> {\n return request(baseUrl, \"/api/v1/skills/validate\", apiKey, { method: \"POST\", body: skill });\n },\n\n /** Execute a skill immediately with an input payload (test / preview). */\n async execute(skillId: string, inputPayload: Record<string, unknown>): Promise<{\n data: { output: Record<string, unknown>; latency_ms: number; tokens?: number; error: string | null };\n }> {\n return request(baseUrl, `/api/v1/skills/${skillId}/execute`, apiKey, { method: \"POST\", body: inputPayload });\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface IntentKeyword {\n id: string;\n keyword: string;\n domain: string | null;\n include_patterns: string[];\n exclude_patterns: string[];\n page_count: number;\n visitor_count: number;\n company_count: number;\n contact_count: number;\n created_at: string | null;\n}\n\nexport interface IntentPage {\n url: string;\n title: string | null;\n keyword: string | null;\n domain: string;\n visitor_count: number;\n last_seen: string | null;\n}\n\nexport interface IntentVisitor {\n visitor_id: string;\n company_domain: string | null;\n company_name: string | null;\n page_url: string;\n first_seen: string | null;\n last_seen: string | null;\n visit_count: number;\n}\n\nexport interface IntentCompany {\n domain: string;\n name: string | null;\n industry: string | null;\n employee_count: string | null;\n visit_count: number;\n last_seen: string | null;\n}\n\n/** One company row from `intent.urlCompanies()`. Richer than {@link IntentCompany},\n * which describes the keyword-companies surface. */\nexport interface IntentUrlCompany {\n company_id: number | null;\n name: string | null;\n domain: string | null;\n industry: string | null;\n employee_count: number | null;\n logo_url: string | null;\n audience: string;\n visit_count: number;\n contacts_seen: number;\n last_seen: string | null;\n}\n\n/** Response of `intent.urlCompanies()`. This endpoint returns a bare aggregate,\n * not the `{ data }` envelope the rest of the Developer API uses. */\nexport interface IntentUrlCompaniesResponse {\n url: string;\n total_companies: number;\n b2b_count: number;\n b2c_count: number;\n companies: IntentUrlCompany[];\n}\n\nexport interface IntentContact {\n contact_id: number | null;\n email: string | null;\n full_name: string | null;\n company: string | null;\n job_title: string | null;\n last_seen: string | null;\n}\n\nexport interface IntentStats {\n total_keywords: number;\n total_pages: number;\n total_visitors_30d: number;\n total_companies_30d: number;\n}\n\n/**\n * Intent + visitor tracking. Distinct from `g8.signals` (which is summary scoring) —\n * this module exposes keyword tracking, page-level visitor data, and account-level\n * intent signals from your own site (or from competitor URLs you've added).\n *\n * Requires API key (server-side).\n *\n * Backed by:\n * GET /api/v1/intent/stats\n * POST /api/v1/intent/keywords/list\n * POST /api/v1/intent/keywords/create-from-domain\n * DELETE /api/v1/intent/keywords/{keyword_id}\n * POST /api/v1/intent/keywords/{keyword_id}/companies\n * POST /api/v1/intent/keywords/{keyword_id}/contacts\n * POST /api/v1/intent/keywords/{keyword_id}/urls\n * POST /api/v1/intent/pages-by-domain\n * POST /api/v1/intent/pages/search\n * POST /api/v1/intent/pages/visitors\n * POST /api/v1/intent/pages/contacts\n * POST /api/v1/intent/pages/visitor-counts\n * POST /api/v1/intent/url-companies\n */\nexport const createIntentClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n const get = (path: string) => request<any>(baseUrl, `/api/v1${path}`, apiKey);\n const post = (path: string, body: Record<string, unknown> = {}) =>\n request<any>(baseUrl, `/api/v1${path}`, apiKey, { method: \"POST\", body });\n const del = (path: string) => request<any>(baseUrl, `/api/v1${path}`, apiKey, { method: \"DELETE\" });\n\n return {\n /** Org-level intent stats (totals over the last 30 days). */\n async stats(): Promise<{ data: IntentStats }> {\n return get(\"/intent/stats\");\n },\n\n /** List tracked keywords with optional pagination. */\n async listKeywords(params: { page?: number; limit?: number; search?: string } = {}): Promise<{ data: IntentKeyword[] }> {\n return post(\"/intent/keywords/list\", params);\n },\n\n /** Create a keyword group from a domain (auto-tracks all pages). */\n async createFromDomain(domain: string): Promise<{ data: IntentKeyword }> {\n return post(\"/intent/keywords/create-from-domain\", { domain });\n },\n\n /** Stop tracking a keyword (irreversible — historical data is retained). */\n async deleteKeyword(keywordId: string): Promise<{ data: { deleted: boolean } }> {\n return del(`/intent/keywords/${keywordId}`);\n },\n\n /** Companies showing interest in a tracked keyword. */\n async keywordCompanies(keywordId: string, params: { limit?: number; date_from?: string; date_to?: string } = {}): Promise<{ data: IntentCompany[] }> {\n return post(`/intent/keywords/${keywordId}/companies`, params);\n },\n\n /** Contacts showing interest in a tracked keyword. */\n async keywordContacts(keywordId: string, params: { limit?: number; date_from?: string; date_to?: string } = {}): Promise<{ data: IntentContact[] }> {\n return post(`/intent/keywords/${keywordId}/contacts`, params);\n },\n\n /** URLs associated with a keyword (pages visitors landed on while interested). */\n async keywordUrls(keywordId: string, params: { limit?: number } = {}): Promise<{ data: IntentPage[] }> {\n return post(`/intent/keywords/${keywordId}/urls`, params);\n },\n\n /** Pages tracked on a specific domain. */\n async pagesByDomain(domain: string, params: { limit?: number } = {}): Promise<{ data: IntentPage[] }> {\n return post(\"/intent/pages-by-domain\", { domain, ...params });\n },\n\n /** Search tracked pages by URL fragment or keyword. */\n async searchPages(query: string, params: { limit?: number } = {}): Promise<{ data: IntentPage[] }> {\n return post(\"/intent/pages/search\", { query, ...params });\n },\n\n /** Get visitor records for a specific page URL. */\n async pageVisitors(pageUrl: string, params: { limit?: number; date_from?: string; date_to?: string } = {}): Promise<{ data: IntentVisitor[] }> {\n return post(\"/intent/pages/visitors\", { url: pageUrl, ...params });\n },\n\n /** Get contacts who visited a specific page URL. */\n async pageContacts(pageUrl: string, params: { limit?: number } = {}): Promise<{ data: IntentContact[] }> {\n return post(\"/intent/pages/contacts\", { url: pageUrl, ...params });\n },\n\n /** Visitor count aggregates by page (pass an array of URLs). */\n async pageVisitorCounts(urls: string[]): Promise<{ data: Array<{ url: string; visitor_count: number }> }> {\n return post(\"/intent/pages/visitor-counts\", { urls });\n },\n\n /**\n * Find companies whose users visited a specific URL (intent search).\n *\n * Previously posted to the bare-host `/intent-search/url-companies`, bypassing\n * the shared `post()` helper. That route is JWT-only, so this method could\n * never actually work with an API key. It now goes through\n * `/api/v1/intent/url-companies` like the rest of the intent surface (g8 issue\n * #16536).\n *\n * Returns a bare aggregate, NOT the `{ data }` envelope — the previous\n * `{ data: IntentCompany[] }` signature never matched what the API sends.\n *\n * `date_from` / `date_to` are accepted for backwards compatibility but have\n * never been read by this endpoint; use `days` to set the lookback window.\n */\n async urlCompanies(\n url: string,\n params: { limit?: number; days?: number; date_from?: string; date_to?: string } = {},\n ): Promise<IntentUrlCompaniesResponse> {\n return post(\"/intent/url-companies\", { url, ...params });\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface GlobalContextDocument {\n id: string;\n category: string;\n title: string;\n content: string;\n metadata: Record<string, unknown> | null;\n created_at: string | null;\n updated_at: string | null;\n}\n\nexport interface Persona {\n id: string;\n name: string;\n description: string | null;\n status: string;\n pain_points: string[];\n goals: string[];\n channels: string[];\n created_at: string | null;\n}\n\nexport interface ICP {\n id: string;\n name: string;\n description: string | null;\n status: string;\n industry: string[] | null;\n employee_count_min: number | null;\n employee_count_max: number | null;\n revenue_min: number | null;\n revenue_max: number | null;\n countries: string[] | null;\n created_at: string | null;\n}\n\nexport interface IntelligenceData {\n id: string;\n source_type: string;\n url: string | null;\n title: string | null;\n content_summary: string | null;\n collected_at: string | null;\n}\n\nexport interface ResearchReport {\n id: string;\n category: \"buyer_psychology\" | \"competitive_teardown\" | \"gtm_channel\" | \"industry_analyst\" | \"review_sentiment\" | \"voice_of_customer\";\n title: string;\n summary: string | null;\n created_at: string | null;\n}\n\n/**\n * Studio context — org-level intelligence documents. The 44-doc engine that\n * powers campaign ideation: brand brief, value props, messaging house, ICPs,\n * personas, intelligence (scrapes + enrichment), and AI research reports.\n *\n * Requires API key (server-side).\n *\n * Backed by:\n * GET /api/v1/global-context/documents\n * GET /api/v1/icps POST /api/v1/icps PATCH /api/v1/icps/{id} POST /api/v1/icps/{id}/archive\n * GET /api/v1/personas POST /api/v1/personas PATCH /api/v1/personas/{id} POST /api/v1/personas/{id}/archive\n * GET /api/v1/intelligence-data\n * GET /api/v1/research-reports\n */\nexport const createStudioClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n const get = (path: string, params: object = {}) =>\n request<any>(baseUrl, `/api/v1${path}`, apiKey, { query: params as Record<string, unknown> });\n const post = (path: string, body: object = {}) =>\n request<any>(baseUrl, `/api/v1${path}`, apiKey, { method: \"POST\", body });\n const patch = (path: string, body: object = {}) =>\n request<any>(baseUrl, `/api/v1${path}`, apiKey, { method: \"PATCH\", body });\n\n return {\n /** Org-level Studio documents (brand_brief, value_props, messaging_house, etc.).\n * `include_content` defaults to true server-side, so each document includes\n * its `content` body unless you explicitly pass `include_content: false`. */\n async globalContext(\n params: { category?: string; limit?: number; include_content?: boolean } = {}\n ): Promise<{ data: GlobalContextDocument[] }> {\n return get(\"/global-context/documents\", params);\n },\n\n /** ICP definitions. */\n async icps(params: { status?: string; limit?: number } = {}): Promise<{ data: ICP[] }> {\n return get(\"/icps\", params);\n },\n\n /** Buyer persona definitions. */\n async personas(params: { status?: string; limit?: number } = {}): Promise<{ data: Persona[] }> {\n return get(\"/personas\", params);\n },\n\n /** Intelligence data (website scrapes, enrichment, competitor research). */\n async intelligenceData(params: { source_type?: string; limit?: number } = {}): Promise<{ data: IntelligenceData[] }> {\n return get(\"/intelligence-data\", params);\n },\n\n /** AI research reports (buyer psychology, competitive teardown, GTM channel, etc.). */\n async researchReports(params: { category?: string; limit?: number } = {}): Promise<{ data: ResearchReport[] }> {\n return get(\"/research-reports\", params);\n },\n\n /** Create an ICP manually (no AI scoring). Requires name + website_url. */\n async createIcp(body: {\n name: string;\n website_url: string;\n description?: string;\n firmographics?: Record<string, unknown>;\n tech_stack?: Record<string, unknown>;\n buying_signals?: unknown[];\n estimated_market_size?: number;\n }): Promise<{ data: ICP }> {\n return post(\"/icps\", body);\n },\n\n /** Update an ICP (partial - send only fields to change). */\n async updateIcp(\n icpId: string,\n body: {\n name?: string;\n description?: string;\n firmographics?: Record<string, unknown>;\n tech_stack?: Record<string, unknown>;\n buying_signals?: unknown[];\n estimated_market_size?: number;\n }\n ): Promise<{ data: ICP }> {\n return patch(`/icps/${icpId}`, body);\n },\n\n /** Archive an ICP (soft delete - sets status to \"archived\"). */\n async archiveIcp(icpId: string): Promise<{ data: ICP }> {\n return post(`/icps/${icpId}/archive`);\n },\n\n /** Create a buyer persona manually (no AI generation). Requires title + website_url. */\n async createPersona(body: {\n title: string;\n website_url: string;\n priority?: number;\n confidence?: string;\n why_target?: string;\n key_signals?: unknown[];\n expected_receptivity?: string;\n campaign_approach?: string;\n recommended_goal?: string;\n source?: string;\n }): Promise<{ data: Persona }> {\n return post(\"/personas\", body);\n },\n\n /** Update a persona (partial). Passing status \"archived\" is equivalent to archivePersona. */\n async updatePersona(\n personaId: string,\n body: {\n title?: string;\n priority?: number;\n confidence?: string;\n why_target?: string;\n key_signals?: unknown[];\n expected_receptivity?: string;\n campaign_approach?: string;\n recommended_goal?: string;\n status?: string;\n }\n ): Promise<{ data: Persona }> {\n return patch(`/personas/${personaId}`, body);\n },\n\n /** Archive a persona (soft delete - sets status to \"archived\"). */\n async archivePersona(personaId: string): Promise<{ data: Persona }> {\n return post(`/personas/${personaId}/archive`);\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface MeetingAttendee {\n name: string | null;\n email: string;\n role: \"host\" | \"prospect\" | \"guest\" | string;\n contact_id?: number | null;\n}\n\nexport interface MeetingTranscriptLine {\n speaker: string;\n text: string;\n timestamp: number;\n}\n\nexport interface MeetingAnalysis {\n sentiment: \"positive\" | \"neutral\" | \"negative\";\n summary: string;\n next_steps: string[];\n objections: string[];\n talk_listen_ratio: number | null;\n}\n\nexport interface MeetingSummary {\n id: string;\n status: \"scheduled\" | \"completed\" | \"cancelled\" | \"no_show\";\n event_type: string | null;\n scheduled_at: string;\n duration_minutes: number;\n attendees: MeetingAttendee[];\n contact_id: number | null;\n conferencing_provider: string | null;\n conferencing_url: string | null;\n recording_url: string | null;\n created_at: string;\n}\n\nexport interface MeetingDetail extends MeetingSummary {\n transcript: MeetingTranscriptLine[] | null;\n analysis: MeetingAnalysis | null;\n}\n\nexport interface MeetingListParams {\n status?: \"scheduled\" | \"completed\" | \"cancelled\" | \"no_show\";\n date_from?: string;\n date_to?: string;\n attendee_id?: string;\n event_type?: string;\n contact_id?: number;\n page?: number;\n limit?: number;\n}\n\n/**\n * Meetings API - read-only access to scheduled, completed, and cancelled meetings.\n * Booking happens via `g8.calendar.*` (write-key safe, browser only) — this surface\n * is for reading meeting records, transcripts, and AI analysis.\n *\n * Requires API key (server-side).\n *\n * Backed by:\n * GET /api/v1/inbox/meetings\n * GET /api/v1/inbox/meetings/{meeting_id}\n */\nexport const createMeetingsClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n /** List meetings with optional filters. Returns summary rows without transcript / analysis. */\n async list(params: MeetingListParams = {}): Promise<{ data: MeetingSummary[]; pagination?: { page: number; limit: number; total: number; has_next: boolean } }> {\n return request(baseUrl, \"/api/v1/inbox/meetings\", apiKey, { query: params as Record<string, unknown> });\n },\n\n /** Get full meeting detail including transcript + AI analysis (transcript available 1-5 min after meeting ends). */\n async get(meetingId: string): Promise<MeetingDetail> {\n const resp = await request<{ data?: MeetingDetail }>(baseUrl, `/api/v1/inbox/meetings/${meetingId}`, apiKey);\n return resp.data ?? (resp as unknown as MeetingDetail);\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\n/** Ad platform an audience can sync to. */\nexport type AudienceSyncPlatform = \"meta\" | \"linkedin\" | \"google\" | \"x\";\n/** Sync mode: full mirror, or only ever add members. */\nexport type AudienceSyncMode = \"mirror\" | \"append_only\";\n\nexport interface AudienceSync {\n id: number;\n audience_id: number;\n platform: string;\n platform_audience_name: string | null;\n mode: string;\n refresh_cadence_hours: number;\n is_active: boolean;\n status: string | null;\n last_sync_at: string | null;\n created_at: string | null;\n}\n\nexport interface AudienceSyncCreateParams {\n /** Audience list ID to sync. */\n audience_id: number;\n platform: AudienceSyncPlatform;\n platform_audience_name?: string;\n /** Default \"mirror\". */\n mode?: AudienceSyncMode;\n /** Refresh cadence in hours (0-720; default 24). */\n refresh_cadence_hours?: number;\n /** Platform-specific config (OAuth creds, ad-account ids, etc.). */\n platform_config?: Record<string, unknown>;\n /** Audience list IDs whose members should be suppressed from the sync. */\n suppression_list_ids?: number[];\n}\n\nexport interface AudienceSyncUpdateParams {\n mode?: AudienceSyncMode;\n refresh_cadence_hours?: number;\n is_active?: boolean;\n suppression_list_ids?: number[];\n}\n\nexport interface AudienceSyncRun {\n id: number;\n started_at: string | null;\n finished_at: string | null;\n status: string | null;\n members_added: number | null;\n members_removed: number | null;\n total_members: number | null;\n error_message: string | null;\n}\n\nexport interface AudienceSyncError {\n id: number;\n started_at: string | null;\n error_message: string | null;\n details: Record<string, unknown> | null;\n}\n\n/**\n * Audiences API — sync an audience list to ad platforms (Meta, LinkedIn,\n * Google, X). Requires API key (server-side). Built on the hardened HTTP core,\n * so every method throws a typed `G8Error` on failure and retries transient\n * 429/5xx/network errors.\n *\n * Backed by `/api/v1/audience-syncs`.\n */\nexport const createAudiencesClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n const base = \"/api/v1/audience-syncs\";\n\n return {\n /** List all audience syncs for the organization. */\n async list(): Promise<{ data: AudienceSync[] }> {\n return request(baseUrl, base, apiKey);\n },\n\n /** Create a new audience sync to an ad platform. */\n async create(params: AudienceSyncCreateParams): Promise<AudienceSync> {\n const resp = await request<{ data?: AudienceSync }>(baseUrl, base, apiKey, {\n method: \"POST\",\n body: params,\n });\n return resp.data ?? (resp as unknown as AudienceSync);\n },\n\n /** Get a single audience sync by ID. */\n async get(configId: number): Promise<AudienceSync> {\n const resp = await request<{ data?: AudienceSync }>(baseUrl, `${base}/${configId}`, apiKey);\n return resp.data ?? (resp as unknown as AudienceSync);\n },\n\n /** Update an audience sync (partial). */\n async update(configId: number, fields: AudienceSyncUpdateParams): Promise<AudienceSync> {\n const resp = await request<{ data?: AudienceSync }>(baseUrl, `${base}/${configId}`, apiKey, {\n method: \"PATCH\",\n body: fields,\n });\n return resp.data ?? (resp as unknown as AudienceSync);\n },\n\n /** Delete an audience sync. */\n async delete(configId: number): Promise<{ data: Record<string, unknown> }> {\n return request(baseUrl, `${base}/${configId}`, apiKey, { method: \"DELETE\" });\n },\n\n /** Trigger an immediate sync run for a config. */\n async trigger(configId: number): Promise<{ data: Record<string, unknown> }> {\n return request(baseUrl, `${base}/${configId}/trigger`, apiKey, { method: \"POST\" });\n },\n\n /** List recent sync runs for a config (most recent first). */\n async runs(configId: number): Promise<{ data: AudienceSyncRun[] }> {\n return request(baseUrl, `${base}/${configId}/runs`, apiKey);\n },\n\n /** List recent sync errors for a config. */\n async errors(configId: number): Promise<{ data: AudienceSyncError[] }> {\n return request(baseUrl, `${base}/${configId}/errors`, apiKey);\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\n/** Filter operators supported by open-data search. */\nexport type SearchOperator =\n | \"any_of\"\n | \"contains\"\n | \"all_of\"\n | \"none_of\"\n | \"is_empty\"\n | \"is_not_empty\"\n | \"between\"\n | \"exists\";\n\n/** A single search filter condition (named to avoid colliding with enrich's `SearchFilter`). */\nexport interface SearchCondition {\n /** Field to filter on, e.g. \"job_title\", \"country\", \"industry\". */\n field: string;\n operator: SearchOperator;\n /** Filter values (operator-dependent; e.g. `[\"VP\", \"Director\"]` for any_of). */\n value?: unknown[];\n}\n\nexport interface SearchParams {\n /** Filter conditions, combined with AND. */\n filters?: SearchCondition[];\n /** Page number, 1-indexed (1-100). */\n page?: number;\n /** Results per page (1-100, default 25). */\n limit?: number;\n}\n\nexport interface SearchSaveParams extends SearchParams {\n /** Title for the new list the matched records are saved into. */\n list_title: string;\n /** Max records to save (1-10,000, default 1,000). */\n max_results?: number;\n}\n\nexport interface SearchContactItem {\n first_name?: string | null;\n last_name?: string | null;\n middle_name?: string | null;\n work_email?: string | null;\n personal_emails?: string | null;\n direct_phone?: string | null;\n mobile_phone?: string | null;\n job_title?: string | null;\n job_department?: string | null;\n seniority_level?: string | null;\n role?: string | null;\n linkedin_url?: string | null;\n linkedin_headline?: string | null;\n city?: string | null;\n state?: string | null;\n country?: string | null;\n company_name?: string | null;\n company_domain?: string | null;\n company_industry?: string | null;\n company_employee_count?: string | null;\n company_country?: string | null;\n confidence_score?: number | null;\n}\n\nexport interface SearchCompanyItem {\n name?: string | null;\n domain?: string | null;\n website?: string | null;\n description?: string | null;\n industry?: string | null;\n industry_group?: string | null;\n employee_count?: string | null;\n revenue?: string | null;\n founded_year?: number | null;\n phone?: string | null;\n address?: string | null;\n city?: string | null;\n state?: string | null;\n country?: string | null;\n zip?: number | null;\n linkedin_url?: string | null;\n linkedin_followers?: string | null;\n facebook_url?: string | null;\n twitter_url?: string | null;\n crunchbase_url?: string | null;\n logo_url?: string | null;\n}\n\nexport interface SearchSaveResult {\n list_id: number;\n list_title: string;\n estimated_total: number;\n status: string;\n}\n\n/**\n * Search API — prospect the open-data graph for new contacts and companies by\n * filter, and optionally save the matches straight into a list. Requires API\n * key (server-side). Built on the hardened HTTP core, so every method throws a\n * typed `G8Error` on failure and retries transient errors.\n *\n * Backed by `/api/v1/search`.\n */\nexport const createSearchClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n const body = (p: SearchParams) => ({ filters: [], page: 1, limit: 25, ...p });\n\n return {\n /** Search open-data contacts by filter. */\n async contacts(params: SearchParams = {}): Promise<{ data: SearchContactItem[] }> {\n return request(baseUrl, \"/api/v1/search/contacts\", apiKey, { method: \"POST\", body: body(params) });\n },\n\n /** Search open-data companies by filter. */\n async companies(params: SearchParams = {}): Promise<{ data: SearchCompanyItem[] }> {\n return request(baseUrl, \"/api/v1/search/companies\", apiKey, { method: \"POST\", body: body(params) });\n },\n\n /** Search contacts and save the matches into a new list. */\n async saveContacts(params: SearchSaveParams): Promise<SearchSaveResult> {\n const resp = await request<{ data?: SearchSaveResult }>(baseUrl, \"/api/v1/search/contacts/save\", apiKey, {\n method: \"POST\",\n body: { filters: [], page: 1, limit: 25, max_results: 1000, ...params },\n });\n return resp.data ?? (resp as unknown as SearchSaveResult);\n },\n\n /** Search companies and save the matches into a new list. */\n async saveCompanies(params: SearchSaveParams): Promise<SearchSaveResult> {\n const resp = await request<{ data?: SearchSaveResult }>(baseUrl, \"/api/v1/search/companies/save\", apiKey, {\n method: \"POST\",\n body: { filters: [], page: 1, limit: 25, max_results: 1000, ...params },\n });\n return resp.data ?? (resp as unknown as SearchSaveResult);\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface AgencyInfo {\n agency_org_id: string;\n agency_org_name: string | null;\n is_agency: boolean;\n client_count: number;\n /** Request header to set when operating a client org (e.g. \"X-Target-Org-Id\"). */\n target_header: string;\n}\n\nexport interface AgencyClient {\n /** Pass this as the `X-Target-Org-Id` header to operate this client org. */\n org_id: string;\n}\n\n/**\n * Agency API — for agency-scoped API keys (minted with `is_agency: true`).\n * Discover the agency credential and the client orgs it may operate. To act on a\n * client org, set the `X-Target-Org-Id` header (see `target_header`) to that\n * client's `org_id` on your subsequent requests. Requires an agency API key\n * (server-side); a non-agency key gets a `403`.\n *\n * Built on the hardened HTTP core (typed `G8Error` + retries). Backed by\n * `/api/v1/agency`.\n */\nexport const createAgencyClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n /** Describe the agency credential: agency org + authorized client count. */\n async me(): Promise<AgencyInfo> {\n const resp = await request<{ data?: AgencyInfo }>(baseUrl, \"/api/v1/agency/me\", apiKey);\n return resp.data ?? (resp as unknown as AgencyInfo);\n },\n\n /** List the client orgs this agency key may target via `X-Target-Org-Id`. */\n async clients(): Promise<{ data: AgencyClient[] }> {\n return request(baseUrl, \"/api/v1/agency/clients\", apiKey);\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface MarketplaceProfile {\n id: string;\n email: string;\n first_name?: string | null;\n last_name?: string | null;\n marketplace_role?: string | null;\n country_code?: string | null;\n monthly_rate_usd?: string | null;\n per_meeting_rate_usd?: string | null;\n availability_status?: string | null;\n bio?: string | null;\n is_complete: boolean;\n}\n\nexport interface MarketplaceOffer {\n id: string;\n org_id: string;\n org_name?: string | null;\n sdr_id: string;\n status: string;\n monthly_rate_usd?: string | null;\n per_meeting_rate_usd?: string | null;\n created_at?: string | null;\n}\n\nexport interface MarketplaceHiring {\n id: string;\n org_id: string;\n sdr_id: string;\n status: string;\n sdr_name?: string | null;\n sdr_role?: string | null;\n sdr_email?: string | null;\n monthly_rate_usd?: string | null;\n per_meeting_rate_usd?: string | null;\n started_at?: string | null;\n}\n\n/**\n * Marketplace API — for SDR/AE talent on the graph8 marketplace. View your\n * profile, see pending hire offers and respond to them, and list your active\n * hirings. Requires a personal API key (server-side). Built on the hardened\n * HTTP core, so every method throws a typed `G8Error` on failure and retries\n * transient errors.\n *\n * Backed by `/api/v1/marketplace`.\n */\nexport const createMarketplaceClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n const base = \"/api/v1/marketplace\";\n\n return {\n /** Your own marketplace SDR profile. */\n async profile(): Promise<MarketplaceProfile> {\n const resp = await request<{ data?: MarketplaceProfile }>(baseUrl, `${base}/me/profile`, apiKey);\n return resp.data ?? (resp as unknown as MarketplaceProfile);\n },\n\n /** Pending hire offers you can accept or reject. */\n async offers(): Promise<{ offers: MarketplaceOffer[]; count: number }> {\n const resp = await request<{ data?: { offers: MarketplaceOffer[]; count: number } }>(\n baseUrl,\n `${base}/me/offers`,\n apiKey,\n );\n return resp.data ?? (resp as unknown as { offers: MarketplaceOffer[]; count: number });\n },\n\n /** Accept a pending hire offer (by hiring id). */\n async acceptOffer(hiringId: string): Promise<Record<string, unknown>> {\n const resp = await request<{ data?: Record<string, unknown> }>(\n baseUrl,\n `${base}/me/offers/${hiringId}/accept`,\n apiKey,\n { method: \"POST\" },\n );\n return resp.data ?? (resp as unknown as Record<string, unknown>);\n },\n\n /** Reject a pending hire offer (by hiring id). */\n async rejectOffer(hiringId: string): Promise<Record<string, unknown>> {\n const resp = await request<{ data?: Record<string, unknown> }>(\n baseUrl,\n `${base}/me/offers/${hiringId}/reject`,\n apiKey,\n { method: \"POST\" },\n );\n return resp.data ?? (resp as unknown as Record<string, unknown>);\n },\n\n /** Your active hiring contracts. */\n async hirings(): Promise<{ hirings: MarketplaceHiring[]; count: number }> {\n const resp = await request<{ data?: { hirings: MarketplaceHiring[]; count: number } }>(\n baseUrl,\n `${base}/me/hirings`,\n apiKey,\n );\n return resp.data ?? (resp as unknown as { hirings: MarketplaceHiring[]; count: number });\n },\n };\n};\n","import { request } from \"./http\";\n\nconst DEFAULT_API = \"https://be.graph8.com\";\n\nexport interface Snippet {\n write_key: string;\n tracking_host: string;\n domains: string[];\n /** Ready-to-use React component import for @graph8/nextjs. */\n react_snippet: string;\n /** Vanilla JS script tag for non-React apps. */\n script_tag: string;\n /** Full configuration object (gtm/config.json). */\n config: Record<string, unknown>;\n}\n\n/**\n * Snippet API — fetch your org's graph8 tracking snippet programmatically, to\n * embed the tracker into an app or a customer's site (the write key, a React\n * component, a vanilla `<script>` tag, allowed domains, and the full config).\n * Requires an API key (server-side). Built on the hardened HTTP core, so it\n * throws a typed `G8Error` on failure and retries transient errors.\n *\n * Backed by `GET /api/v1/snippet`.\n */\nexport const createSnippetClient = (apiKey: string, apiUrl?: string) => {\n const baseUrl = apiUrl || DEFAULT_API;\n\n return {\n /** Get your org's tracking snippet (write key + React/script-tag embeds + config). */\n async get(): Promise<Snippet> {\n const resp = await request<{ data?: Snippet }>(baseUrl, \"/api/v1/snippet\", apiKey);\n return resp.data ?? (resp as unknown as Snippet);\n },\n };\n};\n","/**\n * App-platform token exchange + refresh core (M6-9).\n *\n * The hosted-app clients never carry a permanent org API key. Instead they mint\n * SHORT-LIVED app tokens by EXCHANGING a longer-lived credential, and refresh\n * them on/near expiry:\n *\n * - browser apps exchange the caller's PropelAuth login at\n * `POST /api/v1/app-sessions/exchange` (identity is derived server-side from\n * the validated principal — the body only names the target app + scopes);\n * - hosted backends exchange service client-credentials at\n * `POST /api/v1/service-token`.\n *\n * Both endpoints answer with the reused `ApiResponse` envelope wrapping a\n * `TokenResponse` (see developer_api/app_platform/app_tokens/dtos.py). This\n * module owns:\n *\n * - `AppTokenResponse` — the minted-token payload shape (frozen wire contract);\n * - `createTokenManager` — caches a token, refreshes it on/near expiry, and\n * de-dupes concurrent refreshes into one in-flight exchange;\n * - `createAppRequester` — binds the managed token onto the hardened\n * `request()` core (typed `G8Error`, retries, idempotency, pagination),\n * attaching `Authorization: Bearer <app-token>` (plus any static header such\n * as a service client's `X-Target-Org-Id`) and refreshing once on a 401.\n *\n * The backend app-token API is INERT/unmounted today (behind a deny-by-default\n * flag), so these paths are exercised against mocked HTTP, never a live server.\n */\n\nimport { request, G8Error, type RequestOptions } from \"./http\";\n\nexport const DEFAULT_APP_API = \"https://be.graph8.com\";\n\n/** The minted app token + the non-secret metadata a client reads back.\n * Mirrors `TokenResponse` in developer_api/app_platform/app_tokens/dtos.py. */\nexport interface AppTokenResponse {\n /** The signed app JWT (attach as `Authorization: Bearer <access_token>`). */\n access_token: string;\n /** Always \"Bearer\". */\n token_type: string;\n /** Lifetime in seconds from issuance. */\n expires_in: number;\n /** The distinct-per-app, per-environment audience. */\n audience: string;\n /** App the token is bound to (dapp_-prefixed). */\n app_id: string;\n /** Signing key id (matches a JWKS entry). */\n kid: string;\n}\n\n/** Per-request options accepted by an {@link AppRequest}. A subset of the\n * hardened core's `RequestOptions` — auth (the app token) and the injected\n * fetch/sleep seams are bound by the requester, not passed per call. */\nexport interface AppRequestOptions {\n method?: string;\n body?: unknown;\n headers?: Record<string, string>;\n query?: Record<string, unknown>;\n /** Sent as `Idempotency-Key` for safe POST/PATCH retries. */\n idempotencyKey?: string;\n /** Max retry attempts on 429/5xx/network (default 2). */\n maxRetries?: number;\n /** Base backoff in ms (default 200). */\n retryBaseMs?: number;\n signal?: AbortSignal;\n}\n\n/** A token-bound request function: same ergonomics as the raw `request()` core\n * minus the credential, which the token manager supplies. */\nexport type AppRequest = <T = unknown>(path: string, opts?: AppRequestOptions) => Promise<T>;\n\n/** Caches + refreshes one app token. */\nexport interface TokenManager {\n /** Return a valid token, refreshing on/near expiry. `force` refreshes now. */\n getToken(force?: boolean): Promise<string>;\n /** Current token expiry as an epoch-ms timestamp (0 before the first mint). */\n expiresAt(): number;\n}\n\n/**\n * Build a token manager over a `refresh` exchange.\n *\n * Freshness rule: a cached token is reused until it is within `refreshSkewMs`\n * of expiry, then a refresh is triggered. Concurrent callers during a refresh\n * share ONE in-flight exchange (no stampede). `now` is injectable for tests.\n */\nexport function createTokenManager(args: {\n refresh: () => Promise<AppTokenResponse>;\n now?: () => number;\n refreshSkewMs?: number;\n}): TokenManager {\n const now = args.now ?? (() => Date.now());\n const skewMs = args.refreshSkewMs ?? 60_000;\n let token: string | null = null;\n let expiresAtMs = 0;\n let inflight: Promise<string> | null = null;\n\n const doRefresh = async (): Promise<string> => {\n const resp = await args.refresh();\n token = resp.access_token;\n expiresAtMs = now() + Math.max(0, resp.expires_in) * 1000;\n return token;\n };\n\n return {\n async getToken(force = false): Promise<string> {\n const stillFresh = !force && token !== null && now() < expiresAtMs - skewMs;\n if (stillFresh) return token as string;\n if (!inflight) {\n inflight = doRefresh().finally(() => {\n inflight = null;\n });\n }\n return inflight;\n },\n expiresAt(): number {\n return expiresAtMs;\n },\n };\n}\n\n/**\n * Bind a {@link TokenManager} onto the hardened `request()` core.\n *\n * Every call fetches the current token, attaches `Authorization: Bearer …`\n * (plus any `extraHeaders`, e.g. a service client's `X-Target-Org-Id`), and\n * reuses all of the core's typed errors / retries / idempotency. On a 401 —\n * a token revoked or gone stale mid-window — it force-refreshes ONCE and retries.\n */\nexport function createAppRequester(args: {\n baseUrl: string;\n tokens: TokenManager;\n extraHeaders?: Record<string, string>;\n fetchImpl?: typeof fetch;\n sleepImpl?: (ms: number) => Promise<void>;\n}): AppRequest {\n const { baseUrl, tokens, extraHeaders, fetchImpl, sleepImpl } = args;\n\n // Written as a `const` arrow ON PURPOSE: the SDK contract-extractor\n // (tests/contract/extract-routes.ts) recognises a local arrow that wraps\n // request() around its own `path` parameter as a *helper* and skips this\n // generic pass-through, so it never mis-reads it as a concrete dead route.\n const appRequest = async <T = unknown>(path: string, opts: AppRequestOptions = {}): Promise<T> => {\n const headers = { ...(extraHeaders ?? {}), ...(opts.headers ?? {}) };\n const reqOpts: RequestOptions = { ...opts, headers, fetchImpl, sleepImpl };\n const token = await tokens.getToken();\n try {\n return await request<T>(baseUrl, path, token, reqOpts);\n } catch (err) {\n if (err instanceof G8Error && err.status === 401) {\n const fresh = await tokens.getToken(true);\n return await request<T>(baseUrl, path, fresh, reqOpts);\n }\n throw err;\n }\n };\n\n return appRequest;\n}\n\n/**\n * Exchange the caller's PropelAuth token for a short-lived BROWSER app token.\n * `POST /api/v1/app-sessions/exchange` (body = target app + scopes only;\n * identity/role/tenant are derived server-side from the validated principal).\n */\nexport async function exchangeBrowserToken(args: {\n baseUrl: string;\n appId: string;\n scopes?: string[];\n getPropelAuthToken: () => string | Promise<string>;\n fetchImpl?: typeof fetch;\n sleepImpl?: (ms: number) => Promise<void>;\n}): Promise<AppTokenResponse> {\n const propelToken = await Promise.resolve(args.getPropelAuthToken());\n if (!propelToken) {\n throw new G8Error({\n message: \"getPropelAuthToken() returned no token — cannot exchange a browser app session\",\n status: 401,\n type: \"app_token_invalid\",\n code: \"app_token_invalid\",\n });\n }\n const resp = await request<{ data?: AppTokenResponse } & Partial<AppTokenResponse>>(\n args.baseUrl,\n \"/api/v1/app-sessions/exchange\",\n propelToken,\n {\n method: \"POST\",\n body: { app_id: args.appId, scopes: args.scopes ?? [] },\n fetchImpl: args.fetchImpl,\n sleepImpl: args.sleepImpl,\n },\n );\n return (resp.data ?? (resp as AppTokenResponse)) as AppTokenResponse;\n}\n\n/**\n * Exchange service client-credentials for a SERVICE app token.\n * `POST /api/v1/service-token` — credentials travel in the body; this endpoint\n * has no bearer auth, so no `Authorization` is sent on the exchange itself.\n */\nexport async function exchangeServiceToken(args: {\n baseUrl: string;\n clientId: string;\n clientSecret: string;\n scopes?: string[];\n ttlSeconds?: number;\n fetchImpl?: typeof fetch;\n sleepImpl?: (ms: number) => Promise<void>;\n}): Promise<AppTokenResponse> {\n const resp = await request<{ data?: AppTokenResponse } & Partial<AppTokenResponse>>(\n args.baseUrl,\n \"/api/v1/service-token\",\n \"\",\n {\n method: \"POST\",\n body: {\n client_id: args.clientId,\n client_secret: args.clientSecret,\n scopes: args.scopes ?? [],\n ttl_seconds: args.ttlSeconds ?? null,\n },\n fetchImpl: args.fetchImpl,\n sleepImpl: args.sleepImpl,\n },\n );\n return (resp.data ?? (resp as AppTokenResponse)) as AppTokenResponse;\n}\n","/**\n * Hosted app-platform clients (M6-9).\n *\n * Two entry points, both additive to `@graph8/sdk` and independent of the\n * `g8` tracking/API-key singleton:\n *\n * - `createGraph8AppClient` — BROWSER client. Exchanges the caller's\n * PropelAuth token for a short-lived app token, refreshes it on/near expiry,\n * and attaches `Authorization: Bearer <app-token>` to every call. It NEVER\n * accepts a permanent org API key for browser init (the whole reason the\n * token-exchange exists).\n * - `createGraph8ServiceClient` — BACKEND client. Exchanges service\n * client-credentials for a service app token and sends `X-Target-Org-Id`\n * for the single configured tenant on every call.\n *\n * Both expose a token-bound `request()` — the hardened `request()` core with the\n * minted app token attached — so callers reach Developer API routes with the\n * app token and reuse the core's typed `G8Error`, retries, idempotency, and\n * cursor pagination. The typed `apps` (control-plane) and `objects`\n * (custom-object CRUD) resource modules are DEFERRED until their backend routes\n * ship and freeze (M6-3 / M6-5); only the auth clients ship here.\n */\n\nimport {\n createTokenManager,\n createAppRequester,\n exchangeBrowserToken,\n exchangeServiceToken,\n DEFAULT_APP_API,\n type AppRequest,\n} from \"./appTokens\";\n\nexport type { AppTokenResponse, TokenManager, AppRequest, AppRequestOptions } from \"./appTokens\";\n\n/** Config for {@link createGraph8AppClient} (browser). No API key is accepted. */\nexport interface Graph8AppClientConfig {\n /** Target app id (dapp_-prefixed). */\n appId: string;\n /** The client org this browser session acts in (informational; the server\n * derives the authoritative tenant from the validated PropelAuth principal). */\n tenantOrgId: string;\n /** Returns the caller's current PropelAuth token (sync or async). */\n getPropelAuthToken: () => string | Promise<string>;\n /** Scopes to embed in the minted app token. */\n scopes?: string[];\n /** Backend API URL (default https://be.graph8.com). */\n apiUrl?: string;\n /** Refresh this many ms before expiry (default 60_000). */\n refreshSkewMs?: number;\n /** Injected for tests; defaults to global fetch. */\n fetchImpl?: typeof fetch;\n /** Injected for tests; defaults to a real setTimeout sleep. */\n sleepImpl?: (ms: number) => Promise<void>;\n /** Injected for tests; defaults to Date.now. */\n now?: () => number;\n}\n\n/** A browser app-platform client. */\nexport interface Graph8AppClient {\n readonly appId: string;\n readonly tenantOrgId: string;\n /** Token-bound requester: mints/refreshes the app token and attaches\n * `Authorization: Bearer <app-token>` to every Developer API call. */\n readonly request: AppRequest;\n /** Return the current app token, refreshing on/near expiry (`force` refreshes now). */\n getAppToken(force?: boolean): Promise<string>;\n}\n\n/**\n * Create a BROWSER app-platform client backed by PropelAuth->app-token exchange.\n * Refuses an org API key / write key — those must never reach a browser.\n */\nexport function createGraph8AppClient(config: Graph8AppClientConfig): Graph8AppClient {\n if (typeof config?.getPropelAuthToken !== \"function\") {\n throw new Error(\n \"createGraph8AppClient requires getPropelAuthToken() — a function returning the caller's PropelAuth token\",\n );\n }\n const asRecord = config as unknown as Record<string, unknown>;\n if (\"apiKey\" in asRecord || \"writeKey\" in asRecord) {\n throw new Error(\n \"createGraph8AppClient does not accept an org API key or write key in the browser — pass getPropelAuthToken() instead\",\n );\n }\n\n const baseUrl = config.apiUrl || DEFAULT_APP_API;\n const tokens = createTokenManager({\n now: config.now,\n refreshSkewMs: config.refreshSkewMs,\n refresh: () =>\n exchangeBrowserToken({\n baseUrl,\n appId: config.appId,\n scopes: config.scopes,\n getPropelAuthToken: config.getPropelAuthToken,\n fetchImpl: config.fetchImpl,\n sleepImpl: config.sleepImpl,\n }),\n });\n const req = createAppRequester({ baseUrl, tokens, fetchImpl: config.fetchImpl, sleepImpl: config.sleepImpl });\n\n return {\n appId: config.appId,\n tenantOrgId: config.tenantOrgId,\n request: req,\n getAppToken: (force = false) => tokens.getToken(force),\n };\n}\n\n/** Config for {@link createGraph8ServiceClient} (backend / server-to-server). */\nexport interface Graph8ServiceClientConfig {\n /** Service credential client id. */\n clientId: string;\n /** Service credential client secret (checked against the stored hash). */\n clientSecret: string;\n /** The single consented client org this backend acts for (sent as X-Target-Org-Id). */\n tenantOrgId: string;\n /** Scopes to embed in the minted service token. */\n scopes?: string[];\n /** Requested token TTL; the server clamps it into its service band. */\n ttlSeconds?: number;\n /** Backend API URL (default https://be.graph8.com). */\n apiUrl?: string;\n /** Refresh this many ms before expiry (default 60_000). */\n refreshSkewMs?: number;\n /** Injected for tests; defaults to global fetch. */\n fetchImpl?: typeof fetch;\n /** Injected for tests; defaults to a real setTimeout sleep. */\n sleepImpl?: (ms: number) => Promise<void>;\n /** Injected for tests; defaults to Date.now. */\n now?: () => number;\n}\n\n/** A backend service app-platform client. */\nexport interface Graph8ServiceClient {\n readonly tenantOrgId: string;\n /** Token-bound requester: mints/refreshes the service token, attaches it, and\n * sends `X-Target-Org-Id` for the one consented tenant on every call. */\n readonly request: AppRequest;\n /** Return the current service token, refreshing on/near expiry (`force` refreshes now). */\n getServiceToken(force?: boolean): Promise<string>;\n}\n\n/**\n * Create a BACKEND service client backed by client-credentials->service-token\n * exchange. Every request carries `X-Target-Org-Id: <tenantOrgId>` for the one\n * configured consented tenant.\n */\nexport function createGraph8ServiceClient(config: Graph8ServiceClientConfig): Graph8ServiceClient {\n if (!config?.clientId || !config?.clientSecret) {\n throw new Error(\"createGraph8ServiceClient requires clientId and clientSecret\");\n }\n if (!config?.tenantOrgId) {\n throw new Error(\n \"createGraph8ServiceClient requires tenantOrgId — the single consented client org this backend acts for\",\n );\n }\n\n const baseUrl = config.apiUrl || DEFAULT_APP_API;\n const tokens = createTokenManager({\n now: config.now,\n refreshSkewMs: config.refreshSkewMs,\n refresh: () =>\n exchangeServiceToken({\n baseUrl,\n clientId: config.clientId,\n clientSecret: config.clientSecret,\n scopes: config.scopes,\n ttlSeconds: config.ttlSeconds,\n fetchImpl: config.fetchImpl,\n sleepImpl: config.sleepImpl,\n }),\n });\n const req = createAppRequester({\n baseUrl,\n tokens,\n extraHeaders: { \"X-Target-Org-Id\": config.tenantOrgId },\n fetchImpl: config.fetchImpl,\n sleepImpl: config.sleepImpl,\n });\n\n return {\n tenantOrgId: config.tenantOrgId,\n request: req,\n getServiceToken: (force = false) => tokens.getToken(force),\n };\n}\n"],"mappings":"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","names":["DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","request","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","DEFAULT_API","module"]}