@graph8/sdk 0.277.0 → 0.294.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.
Files changed (52) hide show
  1. package/README.md +2 -2
  2. package/dist/index.d.mts +604 -14
  3. package/dist/index.d.ts +604 -14
  4. package/dist/index.js +6 -4
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +6 -4
  7. package/dist/index.mjs.map +1 -1
  8. package/dist/react.d.mts +597 -14
  9. package/dist/react.d.ts +597 -14
  10. package/dist/react.js +5 -3
  11. package/dist/react.js.map +1 -1
  12. package/dist/react.mjs +5 -3
  13. package/dist/react.mjs.map +1 -1
  14. package/dist/types/enrich.d.ts +2 -2
  15. package/dist/types/react.d.ts +1 -3
  16. package/dist/types/version.d.ts +2 -2
  17. package/docs/agents.md +6 -6
  18. package/docs/api/README.md +3 -3
  19. package/docs/api/analytics.d.ts +26 -1
  20. package/docs/api/analytics.md +2 -0
  21. package/docs/api/assertUpsert.d.ts +2 -0
  22. package/docs/api/assertUpsert.md +1 -0
  23. package/docs/api/audienceSyncs.d.ts +8 -0
  24. package/docs/api/audienceSyncs.md +16 -0
  25. package/docs/api/by-path.md +2 -0
  26. package/docs/api/companies.d.ts +7 -1
  27. package/docs/api/companies.md +4 -1
  28. package/docs/api/contacts.d.ts +8 -2
  29. package/docs/api/contacts.md +5 -2
  30. package/docs/api/enrichment.d.ts +76 -2
  31. package/docs/api/enrichment.md +76 -1
  32. package/docs/api/fields.d.ts +24 -3
  33. package/docs/api/fields.md +15 -4
  34. package/docs/api/install.d.ts +26 -0
  35. package/docs/api/install.md +18 -0
  36. package/docs/api/intent.d.ts +26 -1
  37. package/docs/api/intent.md +23 -0
  38. package/docs/api/lists.d.ts +9 -0
  39. package/docs/api/lists.md +17 -2
  40. package/docs/api/marketingIntelligence.d.ts +40 -0
  41. package/docs/api/marketingIntelligence.md +20 -0
  42. package/docs/api/snippet.d.ts +66 -2
  43. package/docs/api/snippet.md +73 -4
  44. package/docs/api/suppressions.d.ts +4 -0
  45. package/docs/api/suppressions.md +2 -0
  46. package/docs/api/visitors.d.ts +26 -0
  47. package/docs/api/visitors.md +12 -0
  48. package/docs/api/webAnalytics.d.ts +288 -0
  49. package/docs/api/webAnalytics.md +144 -0
  50. package/docs/api/workflows.md +6 -0
  51. package/docs/sdk-clients.md +2 -2
  52. package/package.json +1 -1
@@ -34,7 +34,7 @@ export interface SearchResults {
34
34
  export declare const createEnrichClient: (apiKey: string, apiUrl?: string) => {
35
35
  /**
36
36
  * Look up a person by email, LinkedIn, or name + company. Costs 2 credits per
37
- * lookup, charged even when no match is found (`found: false`).
37
+ * lookup, charged only when a match is found (`found: false` costs nothing).
38
38
  */
39
39
  person(params: {
40
40
  email?: string;
@@ -45,7 +45,7 @@ export declare const createEnrichClient: (apiKey: string, apiUrl?: string) => {
45
45
  }): Promise<PersonEnrichment>;
46
46
  /**
47
47
  * Look up a company by domain or name. Costs 2 credits per lookup, charged
48
- * even when no match is found (`found: false`).
48
+ * only when a match is found (`found: false` costs nothing).
49
49
  */
50
50
  company(params: {
51
51
  domain?: string;
@@ -128,9 +128,7 @@ export declare const useG8: () => {
128
128
  company_domain?: string;
129
129
  }): Promise<import("./enrich").PersonEnrichment>;
130
130
  company(params: {
131
- domain
132
- /** Progressive form helpers (read lazily, so useG8() never throws on its own) */
133
- ? /** Progressive form helpers (read lazily, so useG8() never throws on its own) */: string;
131
+ domain?: string;
134
132
  name?: string;
135
133
  }): Promise<import("./enrich").CompanyEnrichment>;
136
134
  verifyEmail(email: string): Promise<import("./enrich").EmailVerification>;
@@ -5,6 +5,6 @@
5
5
  * both together (the version-parity gate already pins package.json, the
6
6
  * lockfile and the CHANGELOG to each other).
7
7
  */
8
- export declare const SDK_VERSION = "0.277.0";
8
+ export declare const SDK_VERSION = "0.294.0";
9
9
  /** Value of the `X-G8-Client` header every SDK request carries (I-13). */
10
- export declare const SDK_CLIENT_ID = "js-sdk/0.277.0";
10
+ export declare const SDK_CLIENT_ID = "js-sdk/0.294.0";
package/docs/agents.md CHANGED
@@ -9,7 +9,7 @@ grows every week and your training data is older than this file.
9
9
 
10
10
  graph8 is a B2B go-to-market platform: CRM (contacts, companies, deals, custom objects), a 700M-person
11
11
  data index with enrichment, intent signals, sequences and campaigns across email/SMS/LinkedIn/voice, a unified
12
- inbox, meetings, workflows and more. This SDK reaches all of it: **3253 Developer API
12
+ inbox, meetings, workflows and more. This SDK reaches all of it: **3255 Developer API
13
13
  operations** in 141 areas.
14
14
 
15
15
  ## 1. The short version
@@ -92,7 +92,7 @@ g8.init({ apiKey: process.env.G8_API_KEY! });
92
92
  | | Hand-written clients | `g8.api` (generated) |
93
93
  |---|---|---|
94
94
  | Example | `g8.contacts.upsert({ list_id, work_email })` | `g8.api.assertUpsert.assertContact({ body: { list_id, work_email } })` |
95
- | Coverage | 289 methods, reaching 258 operations | all 3253 operations |
95
+ | Coverage | 289 methods, reaching 258 operations | all 3255 operations |
96
96
  | Returns | the unwrapped value (usually `data`) | the full envelope; read `result.data` |
97
97
  | Arguments | positional / friendly names | `{ path?, query?, headers?, body? }`, exactly as the contract names them |
98
98
  | Reference | [sdk-clients.md](sdk-clients.md) | [api/README.md](api/README.md) |
@@ -141,7 +141,7 @@ Hand-written modules:
141
141
  | [`g8.webhooks`](sdk-clients.md#g8-webhooks) | 1 | API key |
142
142
  | [`g8.workflows`](sdk-clients.md#g8-workflows) | 21 | API key |
143
143
 
144
- `g8.api` namespaces (operation counts): `accounts` (16), `adCampaigns` (19), `adCreatives` (40), `agency` (24), `agent` (8), `agents` (65), `aiInbox` (79), `analytics` (51), `apiKeys` (6), `appDeployments` (7), `appDomains` (5), `appLogs` (2), `appObjects` (10), `appPages` (16), `appPlatformTokens` (3), `appointments` (92), `apps` (22), `assertUpsert` (6), `audienceSyncs` (8), `brandKit` (30), `bugReports` (7), `calendarInvites` (3), `campaignWorkflows` (5), `campaigns` (29), `channels` (5), `cliCampaigns` (6), `cliPipelines` (12), `clickHouse` (2), `companies` (50), `companyIntelligence` (14), `contacts` (70), `content` (41), `contentGridSkills` (6), `copilot` (39), `crmSyncs` (6), `customerSuccess` (19), `deals` (48), `deliverability` (17), `desk` (71), `dialer` (46), `dialerAnalytics` (6), `documents` (35), `duplicates` (7), `eSign` (1), `eSignature` (7), `enrichment` (114), `events` (1), `fields` (8), `forms` (35), `gtmCampaigns` (22), `gtmContext` (51), `gtmDeliverability` (1), `gtmKnowledgeBase` (2), `gtmLaunchHelpers` (2), `gtmLibrary` (20), `health` (1), `imageLibrary` (14), `imports` (3), `inbox` (22), `install` (6), `installedApps` (4), `integrationStatus` (6), `integrations` (83), `intent` (16), `knowledge` (21), `knowledgeBase` (2), `landingPages` (59), `leads` (4), `library` (9), `linkedIn` (5), `linkedInOutreach` (20), `lists` (36), `logs` (1), `mailboxes` (38), `marketingIntelligence` (10), `marketplace` (142), `mcp` (1), `me` (1), `meetings` (17), `newsletter` (61), `notes` (19), `notifications` (2), `objects` (18), `openSearch` (4), `ops` (5), `orgGovernance` (17), `orgVideos` (6), `organizationSettings` (22), `phoneNumbers` (16), `profile` (14), `programmaticPages` (15), `providerDomains` (34), `providerUsage` (5), `public` (1), `publicCopilot` (1), `publicSignals` (1), `publicVisitors` (2), `quotes` (56), `radar` (42), `radarReports` (8), `recordRoles` (4), `repos` (4), `research` (13), `revenue` (14), `revenueAnalytics` (6), `revenueLifecycle` (5), `revenueRecords` (15), `roles` (21), `salesCoach` (37), `sandbox` (9), `scans` (2), `scheduleBuy` (9), `scraping` (10), `sdrAnalytics` (5), `search` (19), `seo` (11), `sequenceLifecycle` (8), `sequences` (71), `settings` (18), `signals` (56), `signatures` (24), `skills` (12), `snippet` (2), `socialPublishing` (22), `studioDocuments` (9), `support` (1), `suppressions` (6), `surveys` (21), `tasks` (25), `teamMembers` (21), `teams` (35), `usage` (9), `visitors` (26), `voice` (187), `webAnalytics` (38), `webChat` (46), `webhooks` (8), `websiteEditor` (12), `work` (59), `workbench` (107), `workflows` (59).
144
+ `g8.api` namespaces (operation counts): `accounts` (16), `adCampaigns` (19), `adCreatives` (40), `agency` (24), `agent` (8), `agents` (65), `aiInbox` (79), `analytics` (51), `apiKeys` (6), `appDeployments` (7), `appDomains` (5), `appLogs` (2), `appObjects` (10), `appPages` (16), `appPlatformTokens` (3), `appointments` (92), `apps` (22), `assertUpsert` (6), `audienceSyncs` (8), `brandKit` (30), `bugReports` (7), `calendarInvites` (3), `campaignWorkflows` (5), `campaigns` (29), `channels` (5), `cliCampaigns` (6), `cliPipelines` (12), `clickHouse` (2), `companies` (50), `companyIntelligence` (14), `contacts` (70), `content` (41), `contentGridSkills` (6), `copilot` (39), `crmSyncs` (6), `customerSuccess` (19), `deals` (48), `deliverability` (17), `desk` (71), `dialer` (46), `dialerAnalytics` (6), `documents` (35), `duplicates` (7), `eSign` (1), `eSignature` (7), `enrichment` (115), `events` (1), `fields` (8), `forms` (35), `gtmCampaigns` (22), `gtmContext` (51), `gtmDeliverability` (1), `gtmKnowledgeBase` (2), `gtmLaunchHelpers` (2), `gtmLibrary` (20), `health` (1), `imageLibrary` (14), `imports` (3), `inbox` (22), `install` (6), `installedApps` (4), `integrationStatus` (6), `integrations` (83), `intent` (16), `knowledge` (21), `knowledgeBase` (2), `landingPages` (59), `leads` (4), `library` (9), `linkedIn` (5), `linkedInOutreach` (20), `lists` (36), `logs` (1), `mailboxes` (38), `marketingIntelligence` (10), `marketplace` (142), `mcp` (1), `me` (1), `meetings` (17), `newsletter` (61), `notes` (19), `notifications` (2), `objects` (18), `openSearch` (4), `ops` (5), `orgGovernance` (17), `orgVideos` (6), `organizationSettings` (22), `phoneNumbers` (16), `profile` (14), `programmaticPages` (15), `providerDomains` (34), `providerUsage` (5), `public` (1), `publicCopilot` (1), `publicSignals` (1), `publicVisitors` (2), `quotes` (56), `radar` (42), `radarReports` (8), `recordRoles` (4), `repos` (4), `research` (13), `revenue` (14), `revenueAnalytics` (6), `revenueLifecycle` (5), `revenueRecords` (15), `roles` (21), `salesCoach` (37), `sandbox` (9), `scans` (2), `scheduleBuy` (9), `scraping` (10), `sdrAnalytics` (5), `search` (19), `seo` (11), `sequenceLifecycle` (8), `sequences` (71), `settings` (18), `signals` (56), `signatures` (24), `skills` (12), `snippet` (3), `socialPublishing` (22), `studioDocuments` (9), `support` (1), `suppressions` (6), `surveys` (21), `tasks` (25), `teamMembers` (21), `teams` (35), `usage` (9), `visitors` (26), `voice` (187), `webAnalytics` (38), `webChat` (46), `webhooks` (8), `websiteEditor` (12), `work` (59), `workbench` (107), `workflows` (59).
145
145
 
146
146
  ## 5. How to find the right call
147
147
 
@@ -196,7 +196,7 @@ Every operation carries a tier:
196
196
 
197
197
  | Tier | What it means | Count |
198
198
  |---|---|---|
199
- | `read` | reads data; safe to call and to retry | 1508 |
199
+ | `read` | reads data; safe to call and to retry | 1510 |
200
200
  | `write` | creates or changes records; the SDK sends an Idempotency-Key and retries transient failures | 811 |
201
201
  | `destructive` | deletes or archives data; confirm with a human before calling | 300 |
202
202
  | `billable` | spends credits; the SDK never blindly retries it; confirm cost before bulk use | 383 |
@@ -220,7 +220,7 @@ What you must do:
220
220
  - Get a human's confirmation before calling an `external`, `billable` or `destructive` operation, and say
221
221
  what it will do ("this sends the email to 240 contacts").
222
222
  - Keep bulk billable jobs small until the user confirms the cost (a person or company lookup is 2 credits,
223
- charged even when no match is found).
223
+ charged only when a match is found).
224
224
 
225
225
  ## 8. Errors
226
226
 
@@ -376,7 +376,7 @@ for (const person of results.contacts) {
376
376
 
377
377
  ### Enrich one person or company
378
378
 
379
- You have an email, LinkedIn URL, name + company, or a domain and need the full profile. Each lookup costs 2 credits, charged even when no match is found, and an email verify costs 1 (tier `billable`), so the SDK will not blindly retry them on a 5xx.
379
+ You have an email, LinkedIn URL, name + company, or a domain and need the full profile. Each lookup costs 2 credits, charged only when a match is found, and an email verify costs 1 (tier `billable`), so the SDK will not blindly retry them on a 5xx.
380
380
 
381
381
  ```ts
382
382
  import { g8 } from "@graph8/sdk";
@@ -2,7 +2,7 @@
2
2
 
3
3
  # `g8.api`: every Developer API operation
4
4
 
5
- `g8.api` is generated from the published OpenAPI contract, so it covers all 3253 operations across 141 namespaces (1508 read, 811 write, 300 destructive, 383 billable, 251 external).
5
+ `g8.api` is generated from the published OpenAPI contract, so it covers all 3255 operations across 141 namespaces (1510 read, 811 write, 300 destructive, 383 billable, 251 external).
6
6
 
7
7
  - Found a REST path in a curl example or the web docs? Look it up in [by-path.md](by-path.md).
8
8
  - Prefer a hand-written method where one exists: [../sdk-clients.md](../sdk-clients.md).
@@ -67,7 +67,7 @@
67
67
  | `g8.api.duplicates` | Duplicates | 7 | none | [duplicates.md](duplicates.md) | [duplicates.d.ts](duplicates.d.ts) |
68
68
  | `g8.api.eSign` | E-Sign | 1 | none | [eSign.md](eSign.md) | [eSign.d.ts](eSign.d.ts) |
69
69
  | `g8.api.eSignature` | E-Signature | 7 | none | [eSignature.md](eSignature.md) | [eSignature.d.ts](eSignature.d.ts) |
70
- | `g8.api.enrichment` | Enrichment | 114 | 3 | [enrichment.md](enrichment.md) | [enrichment.d.ts](enrichment.d.ts) |
70
+ | `g8.api.enrichment` | Enrichment | 115 | 3 | [enrichment.md](enrichment.md) | [enrichment.d.ts](enrichment.d.ts) |
71
71
  | `g8.api.events` | Events | 1 | none | [events.md](events.md) | [events.d.ts](events.d.ts) |
72
72
  | `g8.api.fields` | Fields | 8 | 5 | [fields.md](fields.md) | [fields.d.ts](fields.d.ts) |
73
73
  | `g8.api.forms` | Forms | 35 | none | [forms.md](forms.md) | [forms.d.ts](forms.d.ts) |
@@ -144,7 +144,7 @@
144
144
  | `g8.api.signals` | Signals | 56 | none | [signals.md](signals.md) | [signals.d.ts](signals.d.ts) |
145
145
  | `g8.api.signatures` | Signatures | 24 | none | [signatures.md](signatures.md) | [signatures.d.ts](signatures.d.ts) |
146
146
  | `g8.api.skills` | Skills | 12 | 12 | [skills.md](skills.md) | [skills.d.ts](skills.d.ts) |
147
- | `g8.api.snippet` | Snippet | 2 | 1 | [snippet.md](snippet.md) | [snippet.d.ts](snippet.d.ts) |
147
+ | `g8.api.snippet` | Snippet | 3 | 1 | [snippet.md](snippet.md) | [snippet.d.ts](snippet.d.ts) |
148
148
  | `g8.api.socialPublishing` | Social Publishing | 22 | none | [socialPublishing.md](socialPublishing.md) | [socialPublishing.d.ts](socialPublishing.d.ts) |
149
149
  | `g8.api.studioDocuments` | Studio Documents | 9 | none | [studioDocuments.md](studioDocuments.md) | [studioDocuments.d.ts](studioDocuments.d.ts) |
150
150
  | `g8.api.support` | Support | 1 | none | [support.md](support.md) | [support.d.ts](support.d.ts) |
@@ -949,7 +949,7 @@ export interface AnalyticsOperations {
949
949
  };
950
950
  }
951
951
 
952
- // 19 schemas referenced above (the same as G8Contract.<Name>).
952
+ // 20 schemas referenced above (the same as G8Contract.<Name>).
953
953
 
954
954
  export interface ApiResponse_CampaignVisitorsReport_ {
955
955
  data: CampaignVisitorsReport;
@@ -990,6 +990,8 @@ export interface CampaignVisitorContact {
990
990
  export interface CampaignVisitorLink {
991
991
  /** events ÷ emails_with_link × 100, clamped [0,100]; null when no denominator */
992
992
  ctr_percent?: number | null;
993
+ /** Why `ctr_percent` is null: 'no_matching_send' = no email sent in the window contained this exact link (e.g. the click came from an email sent before the window, or from a link shared outside a sequence); 'send_lookup_unavailable' = the send counts could not be loaded this time. Null whenever `ctr_percent` has a value. */
994
+ ctr_unavailable_reason?: string | null;
993
995
  /** Sends whose template embedded this URL (CTR denominator) */
994
996
  emails_with_link?: number | null;
995
997
  /** Human click events on this URL (scanner-filtered) */
@@ -1028,6 +1030,27 @@ export interface CampaignVisitorStep {
1028
1030
  unique_contacts: number;
1029
1031
  }
1030
1032
 
1033
+ /**
1034
+ * One summary counter with a human label and a plain-language definition.
1035
+ *
1036
+ * Added for VI-04: the raw ``summary`` keys are internal names
1037
+ * (``jitsu_wired``, ``encrypted_pageviews``) and mix whole-site volume with
1038
+ * campaign clicks. ``summary.metrics`` lists the SAME values under friendly
1039
+ * labels; the original keys are unchanged.
1040
+ */
1041
+ export interface CampaignVisitorsMetric {
1042
+ /** What is counted, in plain language */
1043
+ definition: string;
1044
+ /** The `summary` field this entry describes (same value) */
1045
+ key: string;
1046
+ /** Human-readable metric name */
1047
+ label: string;
1048
+ /** 'site' = all tracked page views (context only), 'campaign' = email-link clicks, 'diagnostic' = tracking-health detail, 'table' = row counts behind the tables */
1049
+ scope: string;
1050
+ /** Same value as `summary.<key>` */
1051
+ value: number | boolean;
1052
+ }
1053
+
1031
1054
  /** Composite Campaign Visitors report for a single org (analytics report a23). */
1032
1055
  export interface CampaignVisitorsReport {
1033
1056
  link_engagement: Array<CampaignVisitorLink>;
@@ -1055,6 +1078,8 @@ export interface CampaignVisitorsSummary {
1055
1078
  jitsu_wired: boolean;
1056
1079
  /** Total distinct clicked (host, path) before the ceiling */
1057
1080
  link_rows: number;
1081
+ /** The counters above with friendly labels, plain-language definitions and a scope, in display order. Same values as the individual keys; use this to present the summary. */
1082
+ metrics?: Array<CampaignVisitorsMetric>;
1058
1083
  /** Parseable campaign-attributed click events */
1059
1084
  pageview_events: number;
1060
1085
  /** Diagnostic: campaign clicks with a plaintext hash */
@@ -1963,6 +1963,7 @@ One row of the Email Link Engagement table (a clicked destination URL).
1963
1963
  | `path` | `string` | yes | Clicked destination path |
1964
1964
  | `unique_contacts` | `integer` | yes | Distinct contacts who clicked this URL |
1965
1965
  | `ctr_percent` | `number \| null` | no | events ÷ emails_with_link × 100, clamped [0,100]; null when no denominator |
1966
+ | `ctr_unavailable_reason` | `string \| null` | no | Why `ctr_percent` is null: 'no_matching_send' = no email sent in the window contained this exact link (e.g. the click came from an email sent before the window, or from a link shared outside a sequence); 'send_lookup_unavailable' = the send counts could not be loaded this time. Null whenever `ctr_percent` has a value. |
1966
1967
  | `emails_with_link` | `integer \| null` | no | Sends whose template embedded this URL (CTR denominator) |
1967
1968
  | `last_seen` | `string \| null` | no | Latest click timestamp (ISO) |
1968
1969
  | `step_ids` | `Array<string>` | no | Sequencer steps whose links drove these clicks |
@@ -2005,6 +2006,7 @@ KPI tiles for the org's campaign-click activity in the window. All click-derived
2005
2006
  | `unique_campaign_steps` | `integer` | yes | Distinct sequencer steps that drew clicks |
2006
2007
  | `unique_contacts` | `integer` | yes | Distinct contacts who clicked a campaign link |
2007
2008
  | `unparseable_pageviews` | `integer` | yes | Campaign clicks whose tracking hash could not be decoded |
2009
+ | `metrics` | `Array<CampaignVisitorsMetric>` | no | The counters above with friendly labels, plain-language definitions and a scope, in display order. Same values as the individual keys; use this to present the summary. |
2008
2010
 
2009
2011
  <a id="type-campaignvisitorswindow"></a>
2010
2012
 
@@ -193,6 +193,8 @@ export interface CompanyAssertRequest {
193
193
  export interface CompanyAssertResponse {
194
194
  /** 'created' or 'updated' */
195
195
  action: string;
196
+ /** ID of the company that was created or updated (use it with GET/PATCH /companies/{company_id}). Null when the saved record cannot be identified unambiguously, e.g. when the domain already matched several companies in the list. */
197
+ company_id?: number | null;
196
198
  count?: number;
197
199
  custom_fields?: CustomFieldWriteReport;
198
200
  /** Advisory notes about values saved as sent that look wrong (e.g. an unrecognised country). Empty when the input is clean. */
@@ -128,6 +128,7 @@ Other envelope fields: `pagination` (PaginationMeta).
128
128
  | Field | Type | Required | Description |
129
129
  |---|---|---|---|
130
130
  | `action` | `string` | yes | 'created' or 'updated' |
131
+ | `company_id` | `integer \| null` | no | ID of the company that was created or updated (use it with GET/PATCH /companies/{company_id}). Null when the saved record cannot be identified unambiguously, e.g. when the domain already matched several companies in the list. |
131
132
  | `count` | `integer` | no | default `1` |
132
133
  | `custom_fields` | `CustomFieldWriteReport` | no | |
133
134
  | `warnings` | `Array<string>` | no | Advisory notes about values saved as sent that look wrong (e.g. an unrecognised country). Empty when the input is clean. |
@@ -198,11 +198,19 @@ export interface AudienceSyncConfigResponse {
198
198
  id: number;
199
199
  is_active?: boolean;
200
200
  last_sync_at?: string | null;
201
+ /** Error of the last failed run, if any. */
202
+ last_sync_error?: string | null;
201
203
  mode?: string;
204
+ /** When the worker will next try this sync. */
205
+ next_sync_at?: string | null;
202
206
  platform: string;
203
207
  platform_audience_name?: string | null;
204
208
  refresh_cadence_hours?: number;
205
209
  status?: string | null;
210
+ /** Human-readable explanation of status_reason. */
211
+ status_detail?: string | null;
212
+ /** Why the sync is not delivering, or null when healthy: keyword_only (no list, keyword-only Google Search; never scheduled), list_missing (every list it uploads from was deleted), lists_partially_missing, paused, failing (last run failed), failing_never_synced (has never succeeded). */
213
+ status_reason?: string | null;
206
214
  }
207
215
 
208
216
  export interface AudienceSyncCreateRequest {
@@ -79,10 +79,14 @@ Other envelope fields: `pagination` (PaginationMeta).
79
79
  | `created_at` | `string \| null` | no | |
80
80
  | `is_active` | `boolean` | no | default `true` |
81
81
  | `last_sync_at` | `string \| null` | no | |
82
+ | `last_sync_error` | `string \| null` | no | Error of the last failed run, if any. |
82
83
  | `mode` | `string` | no | default `"mirror"` |
84
+ | `next_sync_at` | `string \| null` | no | When the worker will next try this sync. |
83
85
  | `platform_audience_name` | `string \| null` | no | |
84
86
  | `refresh_cadence_hours` | `integer` | no | default `24` |
85
87
  | `status` | `string \| null` | no | |
88
+ | `status_detail` | `string \| null` | no | Human-readable explanation of status_reason. |
89
+ | `status_reason` | `string \| null` | no | Why the sync is not delivering, or null when healthy: keyword_only (no list, keyword-only Google Search; never scheduled), list_missing (every list it uploads from was deleted), lists_partially_missing, paused, failing (last run failed), failing_never_synced (has never succeeded). |
86
90
 
87
91
  **Example**
88
92
 
@@ -160,10 +164,14 @@ Other envelope fields: `pagination` (PaginationMeta).
160
164
  | `created_at` | `string \| null` | no | |
161
165
  | `is_active` | `boolean` | no | default `true` |
162
166
  | `last_sync_at` | `string \| null` | no | |
167
+ | `last_sync_error` | `string \| null` | no | Error of the last failed run, if any. |
163
168
  | `mode` | `string` | no | default `"mirror"` |
169
+ | `next_sync_at` | `string \| null` | no | When the worker will next try this sync. |
164
170
  | `platform_audience_name` | `string \| null` | no | |
165
171
  | `refresh_cadence_hours` | `integer` | no | default `24` |
166
172
  | `status` | `string \| null` | no | |
173
+ | `status_detail` | `string \| null` | no | Human-readable explanation of status_reason. |
174
+ | `status_reason` | `string \| null` | no | Why the sync is not delivering, or null when healthy: keyword_only (no list, keyword-only Google Search; never scheduled), list_missing (every list it uploads from was deleted), lists_partially_missing, paused, failing (last run failed), failing_never_synced (has never succeeded). |
167
175
 
168
176
  **Example**
169
177
 
@@ -203,10 +211,14 @@ Other envelope fields: `pagination` (PaginationMeta).
203
211
  | `created_at` | `string \| null` | no | |
204
212
  | `is_active` | `boolean` | no | default `true` |
205
213
  | `last_sync_at` | `string \| null` | no | |
214
+ | `last_sync_error` | `string \| null` | no | Error of the last failed run, if any. |
206
215
  | `mode` | `string` | no | default `"mirror"` |
216
+ | `next_sync_at` | `string \| null` | no | When the worker will next try this sync. |
207
217
  | `platform_audience_name` | `string \| null` | no | |
208
218
  | `refresh_cadence_hours` | `integer` | no | default `24` |
209
219
  | `status` | `string \| null` | no | |
220
+ | `status_detail` | `string \| null` | no | Human-readable explanation of status_reason. |
221
+ | `status_reason` | `string \| null` | no | Why the sync is not delivering, or null when healthy: keyword_only (no list, keyword-only Google Search; never scheduled), list_missing (every list it uploads from was deleted), lists_partially_missing, paused, failing (last run failed), failing_never_synced (has never succeeded). |
210
222
 
211
223
  **Example**
212
224
 
@@ -382,10 +394,14 @@ Other envelope fields: `pagination` (PaginationMeta).
382
394
  | `created_at` | `string \| null` | no | |
383
395
  | `is_active` | `boolean` | no | default `true` |
384
396
  | `last_sync_at` | `string \| null` | no | |
397
+ | `last_sync_error` | `string \| null` | no | Error of the last failed run, if any. |
385
398
  | `mode` | `string` | no | default `"mirror"` |
399
+ | `next_sync_at` | `string \| null` | no | When the worker will next try this sync. |
386
400
  | `platform_audience_name` | `string \| null` | no | |
387
401
  | `refresh_cadence_hours` | `integer` | no | default `24` |
388
402
  | `status` | `string \| null` | no | |
403
+ | `status_detail` | `string \| null` | no | Human-readable explanation of status_reason. |
404
+ | `status_reason` | `string \| null` | no | Why the sync is not delivering, or null when healthy: keyword_only (no list, keyword-only Google Search; never scheduled), list_missing (every list it uploads from was deleted), lists_partially_missing, paused, failing (last run failed), failing_never_synced (has never succeeded). |
389
405
 
390
406
  **Example**
391
407
 
@@ -1183,6 +1183,7 @@ Every Developer API route, sorted by path, with the SDK call that reaches it. Th
1183
1183
  | POST | `/api/v1/enrichment/lookup/company` | [`g8.api.enrichment.lookupCompany`](enrichment.md#op-lookupcompany) or `g8.enrich.company()` | billable |
1184
1184
  | POST | `/api/v1/enrichment/lookup/person` | [`g8.api.enrichment.lookupPerson`](enrichment.md#op-lookupperson) or `g8.enrich.person()` | billable |
1185
1185
  | POST | `/api/v1/enrichment/person` | [`g8.api.enrichment.lookupPersonAlias`](enrichment.md#op-lookuppersonalias) | billable |
1186
+ | POST | `/api/v1/enrichment/preflight` | [`g8.api.enrichment.preflightEnrichment`](enrichment.md#op-preflightenrichment) | read |
1186
1187
  | GET | `/api/v1/enrichment/providers` | [`g8.api.enrichment.listEnrichmentProviders`](enrichment.md#op-listenrichmentproviders) | read |
1187
1188
  | GET | `/api/v1/enrichment/record-sync/{entity_type}/fields` | [`g8.api.enrichment.listRecordSyncFields`](enrichment.md#op-listrecordsyncfields) | read |
1188
1189
  | GET | `/api/v1/enrichment/record-sync/{entity_type}/history` | [`g8.api.enrichment.listRecordSyncHistory`](enrichment.md#op-listrecordsynchistory) | read |
@@ -2653,6 +2654,7 @@ Every Developer API route, sorted by path, with the SDK call that reaches it. Th
2653
2654
  | POST | `/api/v1/social/posts/{post_id}/publish` | [`g8.api.socialPublishing.publishSocialPost`](socialPublishing.md#op-publishsocialpost) | external |
2654
2655
  | POST | `/api/v1/social/posts/{post_id}/schedule` | [`g8.api.socialPublishing.scheduleSocialPost`](socialPublishing.md#op-schedulesocialpost) | external |
2655
2656
  | GET | `/api/v1/social/providers` | [`g8.api.socialPublishing.listSocialProviders`](socialPublishing.md#op-listsocialproviders) | read |
2657
+ | GET | `/api/v1/streams` | [`g8.api.snippet.listOrgStreams`](snippet.md#op-listorgstreams) | read |
2656
2658
  | POST | `/api/v1/stripe/api-key` | [`g8.api.quotes.connectStripeApiKey`](quotes.md#op-connectstripeapikey) | external |
2657
2659
  | GET | `/api/v1/stripe/companies/{company_id}/invoices` | [`g8.api.quotes.getCompanyStripeInvoices`](quotes.md#op-getcompanystripeinvoices) | read |
2658
2660
  | GET | `/api/v1/stripe/companies/{company_id}/revenue-summary` | [`g8.api.quotes.getCompanyRevenueSummary`](quotes.md#op-getcompanyrevenuesummary) | read |
@@ -1320,7 +1320,7 @@ export type CompanyWorkflowType = "company_intelligence" | "stakeholder_mapping"
1320
1320
  export interface CreateCompanyColumnRequest {
1321
1321
  /** Email of the user creating the column (for audit). */
1322
1322
  created_by: string;
1323
- /** Column data type. Currently only 'text' is supported. */
1323
+ /** Column data type: 'text', 'int' (whole number), 'numeric' (decimal) or 'boolean' (yes/no). Other values 422. */
1324
1324
  data_type?: ColumnType;
1325
1325
  /** STRONGLY RECOMMENDED. Optional waterfall enrichment block. When supplied, a companion `enrichment_configs` row is created alongside the column so the UI 'Enrich' button works immediately. Without it the column exists but isn't enrichable until a pipeline is configured via POST /enrichment/waterfall/configs. */
1326
1326
  enrichment?: CreateFieldEnrichmentInput | null;
@@ -1339,15 +1339,21 @@ export interface CreateCompanyColumnRequest {
1339
1339
  * that is populated when the request included an `enrichment` block.
1340
1340
  */
1341
1341
  export interface CreateCompanyColumnResponse {
1342
+ /** True when a column with this name already existed in this scope, so the existing column was returned. A different `data_type` is rejected with 409. */
1343
+ already_existed?: boolean;
1342
1344
  data_type?: ColumnType;
1343
1345
  /** Populated when the request included an `enrichment` block. Use `enrichment.column_id` for subsequent POST /enrichment/waterfall/enrich calls. */
1344
1346
  enrichment?: CreateFieldEnrichmentResponse | null;
1347
+ /** When `already_existed` is true: the existing column's data type. */
1348
+ existing_data_type?: string | null;
1345
1349
  id?: number | null;
1346
1350
  /** Computed: True when object_id is null/empty (global column). */
1347
1351
  is_global?: boolean;
1348
1352
  name?: string | null;
1349
1353
  object_id?: string | null;
1350
1354
  title: string;
1355
+ /** Non-blocking notices (e.g. a same-named org-wide column also shows on this list). */
1356
+ warnings?: Array<string>;
1351
1357
  }
1352
1358
 
1353
1359
  /**
@@ -339,7 +339,7 @@ Body fields (`CreateCompanyColumnRequest`):
339
339
  |---|---|---|---|
340
340
  | `body.created_by` | `string (email)` | yes | Email of the user creating the column (for audit). |
341
341
  | `body.title` | `string` | yes | Column title shown in the UI (e.g. 'Tier') |
342
- | `body.data_type` | `ColumnType` | no | Column data type. Currently only 'text' is supported. default `"text"` |
342
+ | `body.data_type` | `ColumnType` | no | Column data type: 'text', 'int' (whole number), 'numeric' (decimal) or 'boolean' (yes/no). Other values 422. default `"text"` |
343
343
  | `body.enrichment` | `CreateFieldEnrichmentInput \| null` | no | STRONGLY RECOMMENDED. Optional waterfall enrichment block. When supplied, a companion `enrichment_configs` row is created alongside the column so the UI 'Enrich' button works immediately. Without it the column exists but isn't enrichable until a pipeline is configured via POST /enrichment/waterfall/configs. |
344
344
  | `body.list_id` | `integer \| null` | no | If set, the column is list-scoped (only visible on that list). Required when `enrichment` is supplied — waterfall pipelines are list-scoped. |
345
345
 
@@ -352,12 +352,15 @@ Other envelope fields: `pagination` (PaginationMeta).
352
352
  | Field | Type | Required | Description |
353
353
  |---|---|---|---|
354
354
  | `title` | `string` | yes | |
355
+ | `already_existed` | `boolean` | no | True when a column with this name already existed in this scope, so the existing column was returned. A different `data_type` is rejected with 409. default `false` |
355
356
  | `data_type` | `ColumnType` | no | default `"text"` |
356
357
  | `enrichment` | `CreateFieldEnrichmentResponse \| null` | no | Populated when the request included an `enrichment` block. Use `enrichment.column_id` for subsequent POST /enrichment/waterfall/enrich calls. |
358
+ | `existing_data_type` | `string \| null` | no | When `already_existed` is true: the existing column's data type. |
357
359
  | `id` | `integer \| null` | no | |
358
360
  | `is_global` | `boolean` | no | Computed: True when object_id is null/empty (global column). default `false` |
359
361
  | `name` | `string \| null` | no | |
360
362
  | `object_id` | `string \| null` | no | |
363
+ | `warnings` | `Array<string>` | no | Non-blocking notices (e.g. a same-named org-wide column also shows on this list). |
361
364
 
362
365
  **Example**
363
366
 
@@ -852,7 +852,7 @@ export interface ContactsOperations {
852
852
  name?: string | null;
853
853
  /** Filter by job title (partial match) */
854
854
  job_title?: string | null;
855
- /** Filter by seniority level (exact match, case-insensitive). Spellings of the same level match each other: VP = Vice President; SVP/EVP = SVP = EVP = Senior/Executive Vice President; C-Suite = C-Level = CXO. Pass several levels comma-separated to match any of them, e.g. `VP,SVP/EVP`. */
855
+ /** Filter by seniority level (exact match, case-insensitive). Spellings of the same level match each other: VP = Vice President; SVP/EVP = SVP = EVP = Senior/Executive Vice President; C-Suite = C-Level = CXO. `VP` (or `Vice President`) means VP and above, so it also returns the SVP/EVP band; `SVP/EVP` alone returns only that band. Pass several levels comma-separated to match any of them, e.g. `Director,VP`. */
856
856
  seniority_level?: string | null;
857
857
  /** Filter by company name (partial match) */
858
858
  company_name?: string | null;
@@ -1716,7 +1716,7 @@ export interface ContactUpdateRequest {
1716
1716
  export interface CreateContactColumnRequest {
1717
1717
  /** Email of the user creating the column (for audit). */
1718
1718
  created_by: string;
1719
- /** Column data type. Currently only 'text' is supported. */
1719
+ /** Column data type: 'text', 'int' (whole number), 'numeric' (decimal) or 'boolean' (yes/no). Other values 422. */
1720
1720
  data_type?: ColumnType;
1721
1721
  /** STRONGLY RECOMMENDED. Optional waterfall enrichment block. When supplied, a companion `enrichment_configs` row is created alongside the column so the UI 'Enrich' button works immediately. Without it the column exists but isn't enrichable until a pipeline is configured via POST /enrichment/waterfall/configs. */
1722
1722
  enrichment?: CreateFieldEnrichmentInput | null;
@@ -1735,15 +1735,21 @@ export interface CreateContactColumnRequest {
1735
1735
  * that is populated when the request included an `enrichment` block.
1736
1736
  */
1737
1737
  export interface CreateContactColumnResponse {
1738
+ /** True when a column with this name already existed in this scope, so the existing column was returned. A different `data_type` is rejected with 409. */
1739
+ already_existed?: boolean;
1738
1740
  data_type?: ColumnType;
1739
1741
  /** Populated when the request included an `enrichment` block. Use `enrichment.column_id` for subsequent POST /enrichment/waterfall/enrich calls. */
1740
1742
  enrichment?: CreateFieldEnrichmentResponse | null;
1743
+ /** When `already_existed` is true: the existing column's data type. */
1744
+ existing_data_type?: string | null;
1741
1745
  id?: number | null;
1742
1746
  /** Computed: True when object_id is null/empty (global column). */
1743
1747
  is_global?: boolean;
1744
1748
  name?: string | null;
1745
1749
  object_id?: string | null;
1746
1750
  title: string;
1751
+ /** Non-blocking notices (e.g. a same-named org-wide column also shows on this list). */
1752
+ warnings?: Array<string>;
1747
1753
  }
1748
1754
 
1749
1755
  /**
@@ -385,7 +385,7 @@ Body fields (`CreateContactColumnRequest`):
385
385
  |---|---|---|---|
386
386
  | `body.created_by` | `string (email)` | yes | Email of the user creating the column (for audit). |
387
387
  | `body.title` | `string` | yes | Column title shown in the UI (e.g. 'Lead Score') |
388
- | `body.data_type` | `ColumnType` | no | Column data type. Currently only 'text' is supported. default `"text"` |
388
+ | `body.data_type` | `ColumnType` | no | Column data type: 'text', 'int' (whole number), 'numeric' (decimal) or 'boolean' (yes/no). Other values 422. default `"text"` |
389
389
  | `body.enrichment` | `CreateFieldEnrichmentInput \| null` | no | STRONGLY RECOMMENDED. Optional waterfall enrichment block. When supplied, a companion `enrichment_configs` row is created alongside the column so the UI 'Enrich' button works immediately. Without it the column exists but isn't enrichable until a pipeline is configured via POST /enrichment/waterfall/configs. |
390
390
  | `body.list_id` | `integer \| null` | no | If set, the column is list-scoped (only visible on that list). Required when `enrichment` is supplied — waterfall pipelines are list-scoped. |
391
391
 
@@ -398,12 +398,15 @@ Other envelope fields: `pagination` (PaginationMeta).
398
398
  | Field | Type | Required | Description |
399
399
  |---|---|---|---|
400
400
  | `title` | `string` | yes | |
401
+ | `already_existed` | `boolean` | no | True when a column with this name already existed in this scope, so the existing column was returned. A different `data_type` is rejected with 409. default `false` |
401
402
  | `data_type` | `ColumnType` | no | default `"text"` |
402
403
  | `enrichment` | `CreateFieldEnrichmentResponse \| null` | no | Populated when the request included an `enrichment` block. Use `enrichment.column_id` for subsequent POST /enrichment/waterfall/enrich calls. |
404
+ | `existing_data_type` | `string \| null` | no | When `already_existed` is true: the existing column's data type. |
403
405
  | `id` | `integer \| null` | no | |
404
406
  | `is_global` | `boolean` | no | Computed: True when object_id is null/empty (global column). default `false` |
405
407
  | `name` | `string \| null` | no | |
406
408
  | `object_id` | `string \| null` | no | |
409
+ | `warnings` | `Array<string>` | no | Non-blocking notices (e.g. a same-named org-wide column also shows on this list). |
407
410
 
408
411
  **Example**
409
412
 
@@ -1884,7 +1887,7 @@ List contacts with optional filtering and pagination.
1884
1887
  | `query.list_id` | `integer \| null` | no | Filter by list ID |
1885
1888
  | `query.name` | `string \| null` | no | Filter by first or last name (partial match) |
1886
1889
  | `query.job_title` | `string \| null` | no | Filter by job title (partial match) |
1887
- | `query.seniority_level` | `string \| null` | no | Filter by seniority level (exact match, case-insensitive). Spellings of the same level match each other: VP = Vice President; SVP/EVP = SVP = EVP = Senior/Executive Vice President; C-Suite = C-Level = CXO. Pass several levels comma-separated to match any of them, e.g. `VP,SVP/EVP`. |
1890
+ | `query.seniority_level` | `string \| null` | no | Filter by seniority level (exact match, case-insensitive). Spellings of the same level match each other: VP = Vice President; SVP/EVP = SVP = EVP = Senior/Executive Vice President; C-Suite = C-Level = CXO. `VP` (or `Vice President`) means VP and above, so it also returns the SVP/EVP band; `SVP/EVP` alone returns only that band. Pass several levels comma-separated to match any of them, e.g. `Direc… |
1888
1891
  | `query.company_name` | `string \| null` | no | Filter by company name (partial match) |
1889
1892
  | `query.company_id` | `integer \| null` | no | Filter by company ID |
1890
1893
  | `query.country` | `string \| null` | no | Filter by country (exact match, case-insensitive; surrounding spaces ignored). Common aliases match each other: United States = US = USA; United Kingdom = UK. |
@@ -1,7 +1,7 @@
1
1
  /* Generated by sdk/js/scripts/generate-agent-docs.ts from developer-contract/openapi.json. Regenerated on every build. */
2
2
 
3
3
  /**
4
- * Types for `g8.api.enrichment` (Enrichment): 114 operations.
4
+ * Types for `g8.api.enrichment` (Enrichment): 115 operations.
5
5
  *
6
6
  * For reading. In code, import from the package instead:
7
7
  * import type { ApiInput, ApiOutput, G8Contract } from "@graph8/sdk";
@@ -92,6 +92,7 @@ export interface EnrichmentMembers {
92
92
  lookupPerson: "lookup_person_enrichment_lookup_person_post";
93
93
  lookupPersonAlias: "lookup_person_alias_enrichment_person_post";
94
94
  pauseEnrichmentJob: "pause_enrichment_job_enrichment_jobs__job_id__pause_post";
95
+ preflightEnrichment: "preflight_enrichment_enrichment_preflight_post";
95
96
  previewFindPeople: "preview_find_people_enrichment_find_people__config_id__preview_post";
96
97
  previewFormula: "preview_formula_enrichment_ai_formula_preview_post";
97
98
  previewRecordSync: "preview_record_sync_enrichment_record_sync__entity_type__preview_post";
@@ -1473,6 +1474,20 @@ export interface EnrichmentOperations {
1473
1474
  };
1474
1475
  output: ApiResponse_EnrichmentPayload_;
1475
1476
  };
1477
+ /**
1478
+ * POST /enrichment/preflight
1479
+ * Preflight Enrichment
1480
+ * tier: read · scope: enrichment:read
1481
+ */
1482
+ preflight_enrichment_enrichment_preflight_post: {
1483
+ input: {
1484
+ headers?: {
1485
+ "X-Target-Org-Id"?: string | null;
1486
+ };
1487
+ body: EnrichmentPreflightRequest;
1488
+ };
1489
+ output: ApiResponse_EnrichmentPreflightResponse_;
1490
+ };
1476
1491
  /**
1477
1492
  * POST /enrichment/find-people/{config_id}/preview
1478
1493
  * Preview Find People
@@ -2066,7 +2081,7 @@ export interface EnrichmentOperations {
2066
2081
  };
2067
2082
  }
2068
2083
 
2069
- // 102 schemas referenced above (the same as G8Contract.<Name>).
2084
+ // 105 schemas referenced above (the same as G8Contract.<Name>).
2070
2085
 
2071
2086
  /**
2072
2087
  * Run a saved AI group over a list. CHARGES per record.
@@ -2256,6 +2271,11 @@ export interface ApiResponse_EnrichmentPayload_ {
2256
2271
  pagination?: PaginationMeta | null;
2257
2272
  }
2258
2273
 
2274
+ export interface ApiResponse_EnrichmentPreflightResponse_ {
2275
+ data: EnrichmentPreflightResponse;
2276
+ pagination?: PaginationMeta | null;
2277
+ }
2278
+
2259
2279
  export interface ApiResponse_LookupResponse_ {
2260
2280
  data: LookupResponse;
2261
2281
  pagination?: PaginationMeta | null;
@@ -2565,6 +2585,60 @@ export interface EnrichmentListPayload {
2565
2585
  */
2566
2586
  export type EnrichmentPayload = Record<string, unknown>;
2567
2587
 
2588
+ /**
2589
+ * Check an enrichment request before running it. Free: nothing is written,
2590
+ * queued or charged.
2591
+ *
2592
+ * Takes the same inputs as the MCP `g8_enrich_contacts` tool so a preview can
2593
+ * say up front whether the run would work.
2594
+ */
2595
+ export interface EnrichmentPreflightRequest {
2596
+ /** group_id of a saved AI enrichment config (required for 'ai-research') */
2597
+ ai_group_id?: string | null;
2598
+ /** Contact IDs to enrich (duplicates are counted once) */
2599
+ contact_ids: Array<number>;
2600
+ /** Add ZeroBounce email verification */
2601
+ email_verification?: boolean;
2602
+ /** List ID the contacts belong to */
2603
+ list_id: number;
2604
+ /** 'auto' (AI when ai_group_id is set, else waterfall), 'waterfall' or 'ai-research' */
2605
+ mode?: string;
2606
+ /** Ordered provider names for a waterfall run. Omit to use the list's saved pipeline; an empty list also uses the saved pipeline (a warning says so). */
2607
+ providers?: Array<string> | null;
2608
+ /** Column to enrich: a canonical CONTACT_*\/COMPANY_* column or a synonym like 'phone' or 'email' */
2609
+ target_field?: string | null;
2610
+ }
2611
+
2612
+ /**
2613
+ * What a run with these inputs would do. Errors (unknown list, mode, field or
2614
+ * provider, impossible contact IDs) come back as 400/404 instead.
2615
+ */
2616
+ export interface EnrichmentPreflightResponse {
2617
+ /** Number of unique contacts (the count credits are estimated on) */
2618
+ contact_count: number;
2619
+ /** Contact IDs that will be enriched: duplicates removed, order kept */
2620
+ contact_ids: Array<number>;
2621
+ /** How many repeated contact IDs were dropped */
2622
+ duplicates_removed?: number;
2623
+ list_id: number;
2624
+ list_name?: string | null;
2625
+ /** Providers whose step would be skipped because the org has no key for them */
2626
+ missing_credentials?: Array<string>;
2627
+ /** Resolved mode: 'waterfall' or 'ai-research' */
2628
+ mode: string;
2629
+ /** Contact IDs that are not on the list; the run skips them and does not charge for them */
2630
+ not_in_list?: Array<number>;
2631
+ /** Providers that would run, in order (empty = the saved pipeline) */
2632
+ providers?: Array<string>;
2633
+ providers_detail?: Array<ProviderStepDetail>;
2634
+ /** Canonical column for target_field, when given */
2635
+ target_field?: string | null;
2636
+ /** True when the list's saved pipeline (or the graph8 default) would run */
2637
+ uses_saved_pipeline?: boolean;
2638
+ /** Plain-language advisories for the user */
2639
+ warnings?: Array<string>;
2640
+ }
2641
+
2568
2642
  /** Price every visible qualification template against one list. Charges nothing. */
2569
2643
  export interface EstimateAllRequest {
2570
2644
  list_id: number;