@elevasis/core 0.15.1 → 0.17.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 (72) hide show
  1. package/dist/index.d.ts +1662 -23
  2. package/dist/index.js +171 -24
  3. package/dist/knowledge/index.d.ts +1340 -0
  4. package/dist/knowledge/index.js +138 -0
  5. package/dist/organization-model/index.d.ts +1662 -23
  6. package/dist/organization-model/index.js +171 -24
  7. package/dist/test-utils/index.d.ts +711 -10
  8. package/dist/test-utils/index.js +159 -16
  9. package/package.json +7 -3
  10. package/src/__tests__/publish.test.ts +14 -13
  11. package/src/__tests__/template-core-compatibility.test.ts +4 -4
  12. package/src/_gen/__tests__/__snapshots__/contracts.md.snap +1265 -1154
  13. package/src/auth/multi-tenancy/index.ts +3 -0
  14. package/src/auth/multi-tenancy/theme-presets.ts +45 -0
  15. package/src/auth/multi-tenancy/types.ts +57 -83
  16. package/src/auth/multi-tenancy/users/api-schemas.ts +165 -194
  17. package/src/business/acquisition/activity-events.ts +1 -1
  18. package/src/business/acquisition/api-schemas.ts +1196 -1177
  19. package/src/business/acquisition/crm-state-actions.test.ts +139 -139
  20. package/src/business/acquisition/types.ts +381 -390
  21. package/src/business/crm/api-schemas.ts +40 -0
  22. package/src/business/crm/index.ts +1 -0
  23. package/src/business/deals/api-schemas.ts +79 -0
  24. package/src/business/deals/index.ts +1 -0
  25. package/src/business/projects/types.ts +124 -88
  26. package/src/execution/core/runner-types.ts +61 -80
  27. package/src/execution/engine/tools/integration/server/adapters/gmail/gmail-tools.ts +105 -104
  28. package/src/execution/engine/tools/integration/server/adapters/instantly/instantly-tools.ts +1474 -1473
  29. package/src/execution/engine/tools/integration/server/adapters/millionverifier/millionverifier-tools.ts +103 -102
  30. package/src/execution/engine/tools/integration/server/adapters/signature-api/signature-api-tools.ts +182 -179
  31. package/src/execution/engine/tools/integration/server/adapters/stripe/stripe-tools.ts +310 -309
  32. package/src/execution/engine/tools/integration/tool.ts +255 -253
  33. package/src/execution/engine/tools/lead-service-types.ts +895 -894
  34. package/src/execution/engine/tools/messages.ts +43 -0
  35. package/src/execution/engine/tools/platform/acquisition/types.ts +2 -1
  36. package/src/execution/engine/tools/platform/email/types.ts +97 -96
  37. package/src/execution/engine/tools/types.ts +234 -233
  38. package/src/execution/engine/workflow/types.ts +195 -193
  39. package/src/execution/external/api-schemas.ts +40 -0
  40. package/src/execution/external/index.ts +1 -0
  41. package/src/knowledge/README.md +32 -0
  42. package/src/knowledge/__tests__/queries.test.ts +504 -0
  43. package/src/knowledge/format.ts +99 -0
  44. package/src/knowledge/index.ts +5 -0
  45. package/src/knowledge/published.ts +5 -0
  46. package/src/knowledge/queries.ts +256 -0
  47. package/src/organization-model/__tests__/defaults.test.ts +172 -172
  48. package/src/organization-model/__tests__/foundation.test.ts +7 -7
  49. package/src/organization-model/__tests__/icons.test.ts +27 -0
  50. package/src/organization-model/__tests__/knowledge.test.ts +214 -0
  51. package/src/organization-model/contracts.ts +17 -15
  52. package/src/organization-model/defaults.ts +74 -19
  53. package/src/organization-model/domains/knowledge.ts +53 -0
  54. package/src/organization-model/domains/navigation.ts +416 -399
  55. package/src/organization-model/domains/shared.ts +6 -5
  56. package/src/organization-model/foundation.ts +10 -6
  57. package/src/organization-model/graph/build.ts +209 -182
  58. package/src/organization-model/graph/schema.ts +37 -34
  59. package/src/organization-model/graph/types.ts +47 -31
  60. package/src/organization-model/icons.ts +81 -0
  61. package/src/organization-model/index.ts +8 -3
  62. package/src/organization-model/organization-model.mdx +1 -1
  63. package/src/organization-model/published.ts +103 -86
  64. package/src/organization-model/schema.ts +90 -85
  65. package/src/organization-model/types.ts +40 -33
  66. package/src/platform/index.ts +23 -27
  67. package/src/platform/registry/index.ts +0 -4
  68. package/src/platform/registry/resource-registry.ts +0 -77
  69. package/src/platform/registry/serialized-types.ts +148 -219
  70. package/src/platform/registry/stats-types.ts +60 -60
  71. package/src/reference/_generated/contracts.md +1265 -1154
  72. package/src/platform/registry/__tests__/resource-registry.list-executable.test.ts +0 -393
@@ -1,190 +1,190 @@
1
- import { z } from 'zod'
2
- import { UuidSchema, NonEmptyStringSchema } from '../../platform/utils/validation'
3
- import { LEAD_GEN_STAGE_CATALOG } from '../../organization-model/domains/sales'
4
- import { isProspectingBuildTemplateId } from './build-templates'
5
- export { CrmPriorityBucketKeySchema, CrmPriorityBucketOverrideSchema, CrmPriorityOverrideSchema } from './crm-priority'
6
- export type { CrmPriorityBucketOverride, CrmPriorityOverride, ResolvedCrmPriorityRuleConfig } from './crm-priority'
7
-
8
- /**
9
- * Deal Management API Schemas
10
- *
11
- * Request/response validation for /api/deals surface.
12
- * Used by both the API route handlers and the frontend hooks.
13
- *
14
- * Table mapping:
15
- * acq_deals -> DealSchemas (list/detail)
16
- * acq_deal_notes -> DealSchemas (note shapes)
17
- * acq_deal_tasks -> DealSchemas (task shapes)
18
- */
19
-
20
- // ---------------------------------------------------------------------------
21
- // Enum literals (must match DB CHECK constraints exactly)
22
- // ---------------------------------------------------------------------------
23
-
24
- export const DealStageSchema = z.enum(['interested', 'proposal', 'closing', 'closed_won', 'closed_lost', 'nurturing'])
25
-
26
- export const AcqDealTaskKindSchema = z.enum(['call', 'email', 'meeting', 'other'])
27
-
28
- // ---------------------------------------------------------------------------
29
- // Params
30
- // ---------------------------------------------------------------------------
31
-
32
- export const DealIdParamsSchema = z.object({
33
- dealId: UuidSchema
34
- })
35
-
36
- export const DealTaskIdParamsSchema = z.object({
37
- dealId: UuidSchema,
38
- taskId: UuidSchema
39
- })
40
-
41
- // ---------------------------------------------------------------------------
42
- // Query schemas (coerce strings from query params)
43
- // ---------------------------------------------------------------------------
44
-
45
- export const ListDealsQuerySchema = z
46
- .object({
47
- stage: DealStageSchema.optional(),
48
- search: z.string().optional(),
49
- limit: z.coerce.number().int().positive().default(50),
50
- offset: z.coerce.number().int().min(0).default(0)
51
- })
52
- .strict()
53
-
54
- export const DealLookupQuerySchema = z
55
- .object({
56
- search: z.string().trim().min(1).max(200).optional(),
57
- limit: z.coerce.number().int().min(1).max(25).default(10)
58
- })
59
- .strict()
60
-
61
- export const ListDealTasksDueQuerySchema = z
62
- .object({
63
- window: z.enum(['overdue', 'today', 'today_and_overdue', 'upcoming']).optional(),
64
- assigneeUserId: UuidSchema.optional()
65
- })
66
- .strict()
67
-
68
- // ---------------------------------------------------------------------------
69
- // Request body schemas (all use .strict() — rejects unknown fields)
70
- // ---------------------------------------------------------------------------
71
-
72
- export const CreateDealNoteRequestSchema = z
73
- .object({
74
- body: z.string().trim().min(1).max(10000)
75
- })
76
- .strict()
77
-
78
- export const CreateDealTaskRequestSchema = z
79
- .object({
80
- title: z.string().trim().min(1).max(255),
81
- description: z.string().nullable().optional(),
82
- kind: AcqDealTaskKindSchema.optional(),
83
- dueAt: z.string().datetime().nullable().optional(),
84
- assigneeUserId: UuidSchema.nullable().optional()
85
- })
86
- .strict()
87
-
88
- export const TransitionItemRequestSchema = z
89
- .object({
90
- pipelineKey: z.string().min(1),
91
- stageKey: z.string().min(1),
92
- stateKey: z.string().nullable().optional(),
93
- reason: z.string().optional(),
94
- expectedUpdatedAt: z.string().datetime().optional()
95
- })
96
- .strict()
97
-
98
- export const TransitionDealStateRequestSchema = z
99
- .object({
100
- stateKey: z.string().min(1),
101
- reason: z.string().optional(),
102
- expectedUpdatedAt: z.string().datetime().optional()
103
- })
104
- .strict()
105
-
106
- export const ExecuteActionParamsSchema = z
107
- .object({
108
- dealId: UuidSchema,
109
- actionKey: NonEmptyStringSchema
110
- })
111
- .strict()
112
-
113
- export const ExecuteActionRequestSchema = z
114
- .object({
115
- payload: z.record(z.string(), z.unknown()).optional()
116
- })
117
- .strict()
118
-
119
- // ---------------------------------------------------------------------------
120
- // Response schemas (no .strict() — allows forward-compatible additions)
121
- // ---------------------------------------------------------------------------
122
-
123
- /**
124
- * Contact summary nested inside DealListItem / DealDetailResponse.
125
- * Matches the joined shape returned by useDeals / useDealDetail Supabase queries.
126
- */
127
- export const DealContactSummarySchema = z.object({
128
- id: z.string(),
129
- first_name: z.string().nullable(),
130
- last_name: z.string().nullable(),
131
- email: z.string(),
132
- title: z.string().nullable(),
133
- headline: z.string().nullable(),
134
- linkedin_url: z.string().nullable(),
135
- pipeline_status: z.record(z.string(), z.unknown()).nullable(),
136
- enrichment_data: z.record(z.string(), z.unknown()).nullable(),
137
- company: z
138
- .object({
139
- id: z.string(),
140
- name: z.string(),
141
- domain: z.string().nullable(),
142
- website: z.string().nullable(),
143
- linkedin_url: z.string().nullable(),
144
- segment: z.string().nullable(),
145
- category: z.string().nullable(),
146
- num_employees: z.number().nullable()
147
- })
148
- .nullable()
149
- })
150
-
151
- export const DealPrioritySchema = z.object({
152
- bucketKey: z.enum(['needs_response', 'follow_up_due', 'waiting', 'stale', 'closed_low']),
153
- rank: z.number().int(),
154
- label: z.string(),
155
- color: z.string(),
156
- reason: z.string(),
157
- latestActivityAt: z.string().nullable(),
158
- nextActionAt: z.string().nullable()
159
- })
160
-
161
- /**
162
- * Deal list item with joined contact (and company via contact).
163
- * Matches DealListItem from @repo/core types.
164
- */
165
- export const DealListItemSchema = z.object({
166
- // acq_deals columns
167
- id: z.string(),
168
- organization_id: z.string(),
169
- contact_id: z.string().nullable(),
170
- contact_email: z.string(),
171
- pipeline_key: z.string(),
172
- stage_key: z.string().nullable(),
173
- state_key: z.string().nullable(),
174
- activity_log: z.unknown(),
175
- discovery_data: z.unknown().nullable(),
176
- discovery_submitted_at: z.string().nullable(),
177
- discovery_submitted_by: z.string().nullable(),
178
- proposal_data: z.unknown().nullable(),
179
- proposal_sent_at: z.string().nullable(),
180
- proposal_pdf_url: z.string().nullable(),
181
- signature_envelope_id: z.string().nullable(),
182
- source_list_id: z.string().nullable(),
183
- source_type: z.string().nullable(),
184
- initial_fee: z.number().nullable(),
185
- monthly_fee: z.number().nullable(),
186
- closed_lost_at: z.string().nullable(),
187
- closed_lost_reason: z.string().nullable(),
1
+ import { z } from 'zod'
2
+ import { UuidSchema, NonEmptyStringSchema } from '../../platform/utils/validation'
3
+ import { LEAD_GEN_STAGE_CATALOG } from '../../organization-model/domains/sales'
4
+ import { isProspectingBuildTemplateId } from './build-templates'
5
+ export { CrmPriorityBucketKeySchema, CrmPriorityBucketOverrideSchema, CrmPriorityOverrideSchema } from './crm-priority'
6
+ export type { CrmPriorityBucketOverride, CrmPriorityOverride, ResolvedCrmPriorityRuleConfig } from './crm-priority'
7
+
8
+ /**
9
+ * Deal Management API Schemas
10
+ *
11
+ * Request/response validation for /api/deals surface.
12
+ * Used by both the API route handlers and the frontend hooks.
13
+ *
14
+ * Table mapping:
15
+ * acq_deals -> DealSchemas (list/detail)
16
+ * acq_deal_notes -> DealSchemas (note shapes)
17
+ * acq_deal_tasks -> DealSchemas (task shapes)
18
+ */
19
+
20
+ // ---------------------------------------------------------------------------
21
+ // Enum literals (must match DB CHECK constraints exactly)
22
+ // ---------------------------------------------------------------------------
23
+
24
+ export const DealStageSchema = z.enum(['interested', 'proposal', 'closing', 'closed_won', 'closed_lost', 'nurturing'])
25
+
26
+ export const AcqDealTaskKindSchema = z.enum(['call', 'email', 'meeting', 'other'])
27
+
28
+ // ---------------------------------------------------------------------------
29
+ // Params
30
+ // ---------------------------------------------------------------------------
31
+
32
+ export const DealIdParamsSchema = z.object({
33
+ dealId: UuidSchema
34
+ })
35
+
36
+ export const DealTaskIdParamsSchema = z.object({
37
+ dealId: UuidSchema,
38
+ taskId: UuidSchema
39
+ })
40
+
41
+ // ---------------------------------------------------------------------------
42
+ // Query schemas (coerce strings from query params)
43
+ // ---------------------------------------------------------------------------
44
+
45
+ export const ListDealsQuerySchema = z
46
+ .object({
47
+ stage: DealStageSchema.optional(),
48
+ search: z.string().optional(),
49
+ limit: z.coerce.number().int().positive().default(50),
50
+ offset: z.coerce.number().int().min(0).default(0)
51
+ })
52
+ .strict()
53
+
54
+ export const DealLookupQuerySchema = z
55
+ .object({
56
+ search: z.string().trim().min(1).max(200).optional(),
57
+ limit: z.coerce.number().int().min(1).max(25).default(10)
58
+ })
59
+ .strict()
60
+
61
+ export const ListDealTasksDueQuerySchema = z
62
+ .object({
63
+ window: z.enum(['overdue', 'today', 'today_and_overdue', 'upcoming']).optional(),
64
+ assigneeUserId: UuidSchema.optional()
65
+ })
66
+ .strict()
67
+
68
+ // ---------------------------------------------------------------------------
69
+ // Request body schemas (all use .strict() — rejects unknown fields)
70
+ // ---------------------------------------------------------------------------
71
+
72
+ export const CreateDealNoteRequestSchema = z
73
+ .object({
74
+ body: z.string().trim().min(1).max(10000)
75
+ })
76
+ .strict()
77
+
78
+ export const CreateDealTaskRequestSchema = z
79
+ .object({
80
+ title: z.string().trim().min(1).max(255),
81
+ description: z.string().nullable().optional(),
82
+ kind: AcqDealTaskKindSchema.optional(),
83
+ dueAt: z.string().datetime().nullable().optional(),
84
+ assigneeUserId: UuidSchema.nullable().optional()
85
+ })
86
+ .strict()
87
+
88
+ export const TransitionItemRequestSchema = z
89
+ .object({
90
+ pipelineKey: z.string().min(1),
91
+ stageKey: z.string().min(1),
92
+ stateKey: z.string().nullable().optional(),
93
+ reason: z.string().optional(),
94
+ expectedUpdatedAt: z.string().datetime().optional()
95
+ })
96
+ .strict()
97
+
98
+ export const TransitionDealStateRequestSchema = z
99
+ .object({
100
+ stateKey: z.string().min(1),
101
+ reason: z.string().optional(),
102
+ expectedUpdatedAt: z.string().datetime().optional()
103
+ })
104
+ .strict()
105
+
106
+ export const ExecuteActionParamsSchema = z
107
+ .object({
108
+ dealId: UuidSchema,
109
+ actionKey: NonEmptyStringSchema
110
+ })
111
+ .strict()
112
+
113
+ export const ExecuteActionRequestSchema = z
114
+ .object({
115
+ payload: z.record(z.string(), z.unknown()).optional()
116
+ })
117
+ .strict()
118
+
119
+ // ---------------------------------------------------------------------------
120
+ // Response schemas (no .strict() — allows forward-compatible additions)
121
+ // ---------------------------------------------------------------------------
122
+
123
+ /**
124
+ * Contact summary nested inside DealListItem / DealDetailResponse.
125
+ * Matches the joined shape returned by useDeals / useDealDetail Supabase queries.
126
+ */
127
+ export const DealContactSummarySchema = z.object({
128
+ id: z.string(),
129
+ first_name: z.string().nullable(),
130
+ last_name: z.string().nullable(),
131
+ email: z.string(),
132
+ title: z.string().nullable(),
133
+ headline: z.string().nullable(),
134
+ linkedin_url: z.string().nullable(),
135
+ pipeline_status: z.record(z.string(), z.unknown()).nullable(),
136
+ enrichment_data: z.record(z.string(), z.unknown()).nullable(),
137
+ company: z
138
+ .object({
139
+ id: z.string(),
140
+ name: z.string(),
141
+ domain: z.string().nullable(),
142
+ website: z.string().nullable(),
143
+ linkedin_url: z.string().nullable(),
144
+ segment: z.string().nullable(),
145
+ category: z.string().nullable(),
146
+ num_employees: z.number().nullable()
147
+ })
148
+ .nullable()
149
+ })
150
+
151
+ export const DealPrioritySchema = z.object({
152
+ bucketKey: z.enum(['needs_response', 'follow_up_due', 'waiting', 'stale', 'closed_low']),
153
+ rank: z.number().int(),
154
+ label: z.string(),
155
+ color: z.string(),
156
+ reason: z.string(),
157
+ latestActivityAt: z.string().nullable(),
158
+ nextActionAt: z.string().nullable()
159
+ })
160
+
161
+ /**
162
+ * Deal list item with joined contact (and company via contact).
163
+ * Matches DealListItem from @repo/core types.
164
+ */
165
+ export const DealListItemSchema = z.object({
166
+ // acq_deals columns
167
+ id: z.string(),
168
+ organization_id: z.string(),
169
+ contact_id: z.string().nullable(),
170
+ contact_email: z.string(),
171
+ pipeline_key: z.string(),
172
+ stage_key: z.string().nullable(),
173
+ state_key: z.string().nullable(),
174
+ activity_log: z.unknown(),
175
+ discovery_data: z.unknown().nullable(),
176
+ discovery_submitted_at: z.string().nullable(),
177
+ discovery_submitted_by: z.string().nullable(),
178
+ proposal_data: z.unknown().nullable(),
179
+ proposal_sent_at: z.string().nullable(),
180
+ proposal_pdf_url: z.string().nullable(),
181
+ signature_envelope_id: z.string().nullable(),
182
+ source_list_id: z.string().nullable(),
183
+ source_type: z.string().nullable(),
184
+ initial_fee: z.number().nullable(),
185
+ monthly_fee: z.number().nullable(),
186
+ closed_lost_at: z.string().nullable(),
187
+ closed_lost_reason: z.string().nullable(),
188
188
  created_at: z.string(),
189
189
  updated_at: z.string(),
190
190
  priority: DealPrioritySchema,
@@ -193,248 +193,248 @@ export const DealListItemSchema = z.object({
193
193
  // joined relation
194
194
  contact: DealContactSummarySchema.nullable()
195
195
  })
196
-
197
- export const DealListResponseSchema = z.object({
198
- data: z.array(DealListItemSchema),
199
- total: z.number().int(),
200
- limit: z.number().int(),
201
- offset: z.number().int()
202
- })
203
-
204
- export const DealStageSummarySchema = z.object({
205
- stage: z.string(),
206
- count: z.number().int(),
207
- totalValue: z.number(),
208
- oldestUpdatedAt: z.string().nullable(),
209
- newestUpdatedAt: z.string().nullable()
210
- })
211
-
212
- export const StaleDealSummarySchema = z.object({
213
- id: z.string(),
214
- contactEmail: z.string(),
215
- stageKey: z.string(),
216
- updatedAt: z.string(),
217
- daysStale: z.number().int()
218
- })
219
-
220
- export const DealSummaryResponseSchema = z.object({
221
- totalDeals: z.number().int(),
222
- openDeals: z.number().int(),
223
- wonDeals: z.number().int(),
224
- lostDeals: z.number().int(),
225
- winRate: z.number(),
226
- avgDealSize: z.number(),
227
- totalPipelineValue: z.number(),
228
- stageSummary: z.array(DealStageSummarySchema),
229
- staleDeals: z.array(StaleDealSummarySchema)
230
- })
231
-
232
- export const DealLookupItemSchema = z.object({
233
- id: z.string(),
234
- contactEmail: z.string(),
235
- stageKey: z.string().nullable(),
236
- updatedAt: z.string(),
237
- contactName: z.string().nullable(),
238
- companyName: z.string().nullable(),
239
- displayLabel: z.string()
240
- })
241
-
242
- export const DealLookupResponseSchema = z.array(DealLookupItemSchema)
243
-
244
- export const ConversationMessageSchema = z.object({
245
- id: z.string(),
246
- direction: z.enum(['inbound', 'outbound']),
247
- fromEmail: z.string(),
248
- toEmail: z.string(),
249
- subject: z.string().nullable(),
250
- body: z.string(),
251
- sentAt: z.string().nullable()
252
- })
253
-
254
- export const DealConversationSchema = z.object({
255
- messages: z.array(ConversationMessageSchema)
256
- })
257
-
258
- /**
259
- * Deal detail shape — currently the same as a list item (full joined record).
260
- * Additive fields keep existing DealListItem callers compatible.
261
- */
262
- export const DealDetailResponseSchema = DealListItemSchema.extend({
263
- conversation: DealConversationSchema
264
- })
265
-
266
- /**
267
- * Single acq_deal_notes row (camelCase API representation).
268
- */
269
- export const DealNoteResponseSchema = z.object({
270
- id: z.string(),
271
- dealId: z.string(),
272
- organizationId: z.string(),
273
- authorUserId: z.string().nullable(),
274
- body: z.string(),
275
- createdAt: z.string(),
276
- updatedAt: z.string()
277
- })
278
-
279
- export const DealNoteListResponseSchema = z.array(DealNoteResponseSchema)
280
-
281
- /**
282
- * Single acq_deal_tasks row (camelCase API representation).
283
- * Matches AcqDealTask domain type from types.ts.
284
- */
285
- export const DealTaskResponseSchema = z.object({
286
- id: z.string(),
287
- organizationId: z.string(),
288
- dealId: z.string(),
289
- title: z.string(),
290
- description: z.string().nullable(),
291
- kind: AcqDealTaskKindSchema,
292
- dueAt: z.string().nullable(),
293
- assigneeUserId: z.string().nullable(),
294
- completedAt: z.string().nullable(),
295
- completedByUserId: z.string().nullable(),
296
- createdAt: z.string(),
297
- updatedAt: z.string(),
298
- createdByUserId: z.string().nullable()
299
- })
300
-
301
- export const DealTaskListResponseSchema = z.array(DealTaskResponseSchema)
302
-
303
- // ---------------------------------------------------------------------------
304
- // Bundled export
305
- // ---------------------------------------------------------------------------
306
-
307
- export const DealSchemas = {
308
- // Params
309
- DealIdParams: DealIdParamsSchema,
310
- DealTaskIdParams: DealTaskIdParamsSchema,
311
-
312
- // Queries
313
- ListDealsQuery: ListDealsQuerySchema,
314
- DealLookupQuery: DealLookupQuerySchema,
315
- ListDealTasksDueQuery: ListDealTasksDueQuerySchema,
316
-
317
- // Request bodies
318
- CreateDealNoteRequest: CreateDealNoteRequestSchema,
319
- CreateDealTaskRequest: CreateDealTaskRequestSchema,
320
- TransitionItemRequest: TransitionItemRequestSchema,
321
- TransitionDealStateRequest: TransitionDealStateRequestSchema,
322
- ExecuteActionParams: ExecuteActionParamsSchema,
323
- ExecuteActionRequest: ExecuteActionRequestSchema,
324
-
325
- // Responses
326
- DealPriority: DealPrioritySchema,
327
- DealListResponse: DealListResponseSchema,
328
- DealSummaryResponse: DealSummaryResponseSchema,
329
- DealLookupResponse: DealLookupResponseSchema,
330
- ConversationMessage: ConversationMessageSchema,
331
- DealDetailResponse: DealDetailResponseSchema,
332
- DealNoteResponse: DealNoteResponseSchema,
333
- DealNoteListResponse: DealNoteListResponseSchema,
334
- DealTaskResponse: DealTaskResponseSchema,
335
- DealTaskListResponse: DealTaskListResponseSchema
336
- }
337
-
338
- // ---------------------------------------------------------------------------
339
- // Inferred types
340
- // ---------------------------------------------------------------------------
341
-
342
- export type DealStage = z.infer<typeof DealStageSchema>
343
- export type AcqDealTaskKind = z.infer<typeof AcqDealTaskKindSchema>
344
- export type DealIdParams = z.infer<typeof DealIdParamsSchema>
345
- export type DealTaskIdParams = z.infer<typeof DealTaskIdParamsSchema>
346
- export type ListDealsQuery = z.infer<typeof ListDealsQuerySchema>
347
- export type DealLookupQuery = z.infer<typeof DealLookupQuerySchema>
348
- export type ListDealTasksDueQuery = z.infer<typeof ListDealTasksDueQuerySchema>
349
- export type CreateDealNoteRequest = z.infer<typeof CreateDealNoteRequestSchema>
350
- export type CreateDealTaskRequest = z.infer<typeof CreateDealTaskRequestSchema>
351
- export type TransitionItemRequest = z.infer<typeof TransitionItemRequestSchema>
352
- export type TransitionDealStateRequest = z.infer<typeof TransitionDealStateRequestSchema>
353
- export type ExecuteActionParams = z.infer<typeof ExecuteActionParamsSchema>
354
- export type ExecuteActionRequest = z.infer<typeof ExecuteActionRequestSchema>
355
- export type DealPriorityResponse = z.infer<typeof DealPrioritySchema>
356
- export type DealListResponse = z.infer<typeof DealListResponseSchema>
357
- export type DealSummaryResponse = z.infer<typeof DealSummaryResponseSchema>
358
- export type DealLookupItem = z.infer<typeof DealLookupItemSchema>
359
- export type DealLookupResponse = z.infer<typeof DealLookupResponseSchema>
360
- export type ConversationMessage = z.infer<typeof ConversationMessageSchema>
361
- export type DealDetailResponse = z.infer<typeof DealDetailResponseSchema>
362
- export type DealNoteResponse = z.infer<typeof DealNoteResponseSchema>
363
- export type DealNoteListResponse = z.infer<typeof DealNoteListResponseSchema>
364
- export type DealTaskResponse = z.infer<typeof DealTaskResponseSchema>
365
- export type DealTaskListResponse = z.infer<typeof DealTaskListResponseSchema>
366
-
367
- // ---------------------------------------------------------------------------
368
- // List Management API Schemas
369
- //
370
- // Request/response validation for /api/acquisition/lists surface.
371
- // Used by both the API route handlers and the frontend hooks.
372
- //
373
- // Table mapping:
374
- // acq_lists -> AcqListSchemas (list/detail/progress)
375
- // acq_list_companies -> AcqListSchemas (add/remove company membership)
376
- // acq_list_contacts -> AcqListSchemas (add/remove contact membership)
377
- // acq_list_executions -> AcqListSchemas (execution history)
378
- // ---------------------------------------------------------------------------
379
-
380
- // ---------------------------------------------------------------------------
381
- // Primitives — list status enum + jsonb config schemas
382
- // ---------------------------------------------------------------------------
383
-
384
- /**
385
- * Lifecycle status enum for `acq_lists.status` (mirrors DB CHECK constraint
386
- * from migration 20260428000003_lead_gen_acq_lists_status_and_config.sql).
387
- */
388
- export const ListStatusSchema = z.enum(['draft', 'enriching', 'launched', 'closing', 'archived'])
389
-
390
- /**
391
- * Scraping criteria stored in `acq_lists.scraping_config` jsonb.
392
- * Edited via the UI; consumed by lgn-01 prospecting workflows (Apify input shape,
393
- * geography, vertical, size). All fields are optional — empty config is valid.
394
- */
395
- export const ScrapingConfigSchema = z.object({
396
- vertical: z.string().trim().max(255).optional(),
397
- geography: z.string().trim().max(500).optional(),
398
- size: z.string().trim().max(255).optional(),
399
- apifyInput: z.record(z.string(), z.unknown()).optional()
400
- })
401
-
402
- /**
403
- * ICP / qualification rubric stored in `acq_lists.icp` jsonb.
404
- * Replaces the legacy `config.qualification` blob. Consumed by the
405
- * company-qualification workflow.
406
- */
407
- export const IcpRubricSchema = z.object({
408
- qualificationRubricKey: z.string().trim().max(255).nullish(),
409
- targetDescription: z.string().optional(),
410
- minReviewCount: z.number().int().min(0).optional(),
411
- minRating: z.number().min(0).max(5).optional(),
412
- excludeFranchises: z.boolean().optional(),
413
- customRules: z.string().optional()
414
- })
415
-
416
- /**
417
- * One stage entry in a list's `pipeline_config.stages[]`. The `key` is
418
- * validated against `LEAD_GEN_STAGE_CATALOG` so list pipeline definitions
419
- * stay aligned with the org-os semantic layer.
420
- */
421
- export const PipelineStageSchema = z.object({
422
- key: z.string().refine((value) => Object.prototype.hasOwnProperty.call(LEAD_GEN_STAGE_CATALOG, value), {
423
- message: 'pipeline stage key must match LEAD_GEN_STAGE_CATALOG'
424
- }),
425
- label: z.string().optional(),
426
- enabled: z.boolean().optional(),
427
- order: z.number().int().optional()
428
- })
429
-
430
- /**
431
- * Pipeline presentation contract stored in `acq_lists.pipeline_config` jsonb.
432
- * `stages[].key` validates against the catalog; the rest is presentation only.
433
- */
434
- export const PipelineConfigSchema = z.object({
435
- stages: z.array(PipelineStageSchema).optional()
436
- })
437
-
196
+
197
+ export const DealListResponseSchema = z.object({
198
+ data: z.array(DealListItemSchema),
199
+ total: z.number().int(),
200
+ limit: z.number().int(),
201
+ offset: z.number().int()
202
+ })
203
+
204
+ export const DealStageSummarySchema = z.object({
205
+ stage: z.string(),
206
+ count: z.number().int(),
207
+ totalValue: z.number(),
208
+ oldestUpdatedAt: z.string().nullable(),
209
+ newestUpdatedAt: z.string().nullable()
210
+ })
211
+
212
+ export const StaleDealSummarySchema = z.object({
213
+ id: z.string(),
214
+ contactEmail: z.string(),
215
+ stageKey: z.string(),
216
+ updatedAt: z.string(),
217
+ daysStale: z.number().int()
218
+ })
219
+
220
+ export const DealSummaryResponseSchema = z.object({
221
+ totalDeals: z.number().int(),
222
+ openDeals: z.number().int(),
223
+ wonDeals: z.number().int(),
224
+ lostDeals: z.number().int(),
225
+ winRate: z.number(),
226
+ avgDealSize: z.number(),
227
+ totalPipelineValue: z.number(),
228
+ stageSummary: z.array(DealStageSummarySchema),
229
+ staleDeals: z.array(StaleDealSummarySchema)
230
+ })
231
+
232
+ export const DealLookupItemSchema = z.object({
233
+ id: z.string(),
234
+ contactEmail: z.string(),
235
+ stageKey: z.string().nullable(),
236
+ updatedAt: z.string(),
237
+ contactName: z.string().nullable(),
238
+ companyName: z.string().nullable(),
239
+ displayLabel: z.string()
240
+ })
241
+
242
+ export const DealLookupResponseSchema = z.array(DealLookupItemSchema)
243
+
244
+ export const ConversationMessageSchema = z.object({
245
+ id: z.string(),
246
+ direction: z.enum(['inbound', 'outbound']),
247
+ fromEmail: z.string(),
248
+ toEmail: z.string(),
249
+ subject: z.string().nullable(),
250
+ body: z.string(),
251
+ sentAt: z.string().nullable()
252
+ })
253
+
254
+ export const DealConversationSchema = z.object({
255
+ messages: z.array(ConversationMessageSchema)
256
+ })
257
+
258
+ /**
259
+ * Deal detail shape — currently the same as a list item (full joined record).
260
+ * Additive fields keep existing DealListItem callers compatible.
261
+ */
262
+ export const DealDetailResponseSchema = DealListItemSchema.extend({
263
+ conversation: DealConversationSchema
264
+ })
265
+
266
+ /**
267
+ * Single acq_deal_notes row (camelCase API representation).
268
+ */
269
+ export const DealNoteResponseSchema = z.object({
270
+ id: z.string(),
271
+ dealId: z.string(),
272
+ organizationId: z.string(),
273
+ authorUserId: z.string().nullable(),
274
+ body: z.string(),
275
+ createdAt: z.string(),
276
+ updatedAt: z.string()
277
+ })
278
+
279
+ export const DealNoteListResponseSchema = z.array(DealNoteResponseSchema)
280
+
281
+ /**
282
+ * Single acq_deal_tasks row (camelCase API representation).
283
+ * Matches AcqDealTask domain type from types.ts.
284
+ */
285
+ export const DealTaskResponseSchema = z.object({
286
+ id: z.string(),
287
+ organizationId: z.string(),
288
+ dealId: z.string(),
289
+ title: z.string(),
290
+ description: z.string().nullable(),
291
+ kind: AcqDealTaskKindSchema,
292
+ dueAt: z.string().nullable(),
293
+ assigneeUserId: z.string().nullable(),
294
+ completedAt: z.string().nullable(),
295
+ completedByUserId: z.string().nullable(),
296
+ createdAt: z.string(),
297
+ updatedAt: z.string(),
298
+ createdByUserId: z.string().nullable()
299
+ })
300
+
301
+ export const DealTaskListResponseSchema = z.array(DealTaskResponseSchema)
302
+
303
+ // ---------------------------------------------------------------------------
304
+ // Bundled export
305
+ // ---------------------------------------------------------------------------
306
+
307
+ export const DealSchemas = {
308
+ // Params
309
+ DealIdParams: DealIdParamsSchema,
310
+ DealTaskIdParams: DealTaskIdParamsSchema,
311
+
312
+ // Queries
313
+ ListDealsQuery: ListDealsQuerySchema,
314
+ DealLookupQuery: DealLookupQuerySchema,
315
+ ListDealTasksDueQuery: ListDealTasksDueQuerySchema,
316
+
317
+ // Request bodies
318
+ CreateDealNoteRequest: CreateDealNoteRequestSchema,
319
+ CreateDealTaskRequest: CreateDealTaskRequestSchema,
320
+ TransitionItemRequest: TransitionItemRequestSchema,
321
+ TransitionDealStateRequest: TransitionDealStateRequestSchema,
322
+ ExecuteActionParams: ExecuteActionParamsSchema,
323
+ ExecuteActionRequest: ExecuteActionRequestSchema,
324
+
325
+ // Responses
326
+ DealPriority: DealPrioritySchema,
327
+ DealListResponse: DealListResponseSchema,
328
+ DealSummaryResponse: DealSummaryResponseSchema,
329
+ DealLookupResponse: DealLookupResponseSchema,
330
+ ConversationMessage: ConversationMessageSchema,
331
+ DealDetailResponse: DealDetailResponseSchema,
332
+ DealNoteResponse: DealNoteResponseSchema,
333
+ DealNoteListResponse: DealNoteListResponseSchema,
334
+ DealTaskResponse: DealTaskResponseSchema,
335
+ DealTaskListResponse: DealTaskListResponseSchema
336
+ }
337
+
338
+ // ---------------------------------------------------------------------------
339
+ // Inferred types
340
+ // ---------------------------------------------------------------------------
341
+
342
+ export type DealStage = z.infer<typeof DealStageSchema>
343
+ export type AcqDealTaskKind = z.infer<typeof AcqDealTaskKindSchema>
344
+ export type DealIdParams = z.infer<typeof DealIdParamsSchema>
345
+ export type DealTaskIdParams = z.infer<typeof DealTaskIdParamsSchema>
346
+ export type ListDealsQuery = z.infer<typeof ListDealsQuerySchema>
347
+ export type DealLookupQuery = z.infer<typeof DealLookupQuerySchema>
348
+ export type ListDealTasksDueQuery = z.infer<typeof ListDealTasksDueQuerySchema>
349
+ export type CreateDealNoteRequest = z.infer<typeof CreateDealNoteRequestSchema>
350
+ export type CreateDealTaskRequest = z.infer<typeof CreateDealTaskRequestSchema>
351
+ export type TransitionItemRequest = z.infer<typeof TransitionItemRequestSchema>
352
+ export type TransitionDealStateRequest = z.infer<typeof TransitionDealStateRequestSchema>
353
+ export type ExecuteActionParams = z.infer<typeof ExecuteActionParamsSchema>
354
+ export type ExecuteActionRequest = z.infer<typeof ExecuteActionRequestSchema>
355
+ export type DealPriorityResponse = z.infer<typeof DealPrioritySchema>
356
+ export type DealListResponse = z.infer<typeof DealListResponseSchema>
357
+ export type DealSummaryResponse = z.infer<typeof DealSummaryResponseSchema>
358
+ export type DealLookupItem = z.infer<typeof DealLookupItemSchema>
359
+ export type DealLookupResponse = z.infer<typeof DealLookupResponseSchema>
360
+ export type ConversationMessage = z.infer<typeof ConversationMessageSchema>
361
+ export type DealDetailResponse = z.infer<typeof DealDetailResponseSchema>
362
+ export type DealNoteResponse = z.infer<typeof DealNoteResponseSchema>
363
+ export type DealNoteListResponse = z.infer<typeof DealNoteListResponseSchema>
364
+ export type DealTaskResponse = z.infer<typeof DealTaskResponseSchema>
365
+ export type DealTaskListResponse = z.infer<typeof DealTaskListResponseSchema>
366
+
367
+ // ---------------------------------------------------------------------------
368
+ // List Management API Schemas
369
+ //
370
+ // Request/response validation for /api/acquisition/lists surface.
371
+ // Used by both the API route handlers and the frontend hooks.
372
+ //
373
+ // Table mapping:
374
+ // acq_lists -> AcqListSchemas (list/detail/progress)
375
+ // acq_list_companies -> AcqListSchemas (add/remove company membership)
376
+ // acq_list_contacts -> AcqListSchemas (add/remove contact membership)
377
+ // acq_list_executions -> AcqListSchemas (execution history)
378
+ // ---------------------------------------------------------------------------
379
+
380
+ // ---------------------------------------------------------------------------
381
+ // Primitives — list status enum + jsonb config schemas
382
+ // ---------------------------------------------------------------------------
383
+
384
+ /**
385
+ * Lifecycle status enum for `acq_lists.status` (mirrors DB CHECK constraint
386
+ * from migration 20260428000003_lead_gen_acq_lists_status_and_config.sql).
387
+ */
388
+ export const ListStatusSchema = z.enum(['draft', 'enriching', 'launched', 'closing', 'archived'])
389
+
390
+ /**
391
+ * Scraping criteria stored in `acq_lists.scraping_config` jsonb.
392
+ * Edited via the UI; consumed by lgn-01 prospecting workflows (Apify input shape,
393
+ * geography, vertical, size). All fields are optional — empty config is valid.
394
+ */
395
+ export const ScrapingConfigSchema = z.object({
396
+ vertical: z.string().trim().max(255).optional(),
397
+ geography: z.string().trim().max(500).optional(),
398
+ size: z.string().trim().max(255).optional(),
399
+ apifyInput: z.record(z.string(), z.unknown()).optional()
400
+ })
401
+
402
+ /**
403
+ * ICP / qualification rubric stored in `acq_lists.icp` jsonb.
404
+ * Replaces the legacy `config.qualification` blob. Consumed by the
405
+ * company-qualification workflow.
406
+ */
407
+ export const IcpRubricSchema = z.object({
408
+ qualificationRubricKey: z.string().trim().max(255).nullish(),
409
+ targetDescription: z.string().optional(),
410
+ minReviewCount: z.number().int().min(0).optional(),
411
+ minRating: z.number().min(0).max(5).optional(),
412
+ excludeFranchises: z.boolean().optional(),
413
+ customRules: z.string().optional()
414
+ })
415
+
416
+ /**
417
+ * One stage entry in a list's `pipeline_config.stages[]`. The `key` is
418
+ * validated against `LEAD_GEN_STAGE_CATALOG` so list pipeline definitions
419
+ * stay aligned with the org-os semantic layer.
420
+ */
421
+ export const PipelineStageSchema = z.object({
422
+ key: z.string().refine((value) => Object.prototype.hasOwnProperty.call(LEAD_GEN_STAGE_CATALOG, value), {
423
+ message: 'pipeline stage key must match LEAD_GEN_STAGE_CATALOG'
424
+ }),
425
+ label: z.string().optional(),
426
+ enabled: z.boolean().optional(),
427
+ order: z.number().int().optional()
428
+ })
429
+
430
+ /**
431
+ * Pipeline presentation contract stored in `acq_lists.pipeline_config` jsonb.
432
+ * `stages[].key` validates against the catalog; the rest is presentation only.
433
+ */
434
+ export const PipelineConfigSchema = z.object({
435
+ stages: z.array(PipelineStageSchema).optional()
436
+ })
437
+
438
438
  export const BuildPlanSnapshotStepSchema = z
439
439
  .object({
440
440
  id: z.string().trim().min(1).max(100),
@@ -448,751 +448,770 @@ export const BuildPlanSnapshotStepSchema = z
448
448
  capabilityKey: z.string().trim().min(1).max(100),
449
449
  defaultBatchSize: z.number().int().positive(),
450
450
  maxBatchSize: z.number().int().positive()
451
- })
452
- .refine((step) => step.defaultBatchSize <= step.maxBatchSize, {
453
- message: 'defaultBatchSize must be less than or equal to maxBatchSize',
454
- path: ['defaultBatchSize']
455
- })
456
-
457
- export const BuildPlanSnapshotSchema = z.object({
458
- templateId: z.string().trim().min(1).max(100),
459
- templateLabel: z.string().trim().min(1).max(120),
460
- steps: z.array(BuildPlanSnapshotStepSchema).min(1)
461
- })
462
-
463
- export const AcqListMetadataSchema = z
464
- .object({
465
- buildPlanSnapshot: BuildPlanSnapshotSchema.optional()
466
- })
467
- .catchall(z.unknown())
468
-
469
- export const ProspectingBuildTemplateIdSchema = z
470
- .string()
471
- .trim()
472
- .min(1)
473
- .max(100)
474
- .refine(isProspectingBuildTemplateId, {
475
- message: 'buildTemplateId must match a known prospecting build template'
476
- })
477
-
478
- // ---------------------------------------------------------------------------
479
- // List telemetry / progress schemas
480
- // ---------------------------------------------------------------------------
481
-
482
- export const ListStageCountsSchema = z.object({
483
- // Attempted counts by canonical lead-gen stage. The detailed status
484
- // distribution lives on ListProgress; telemetry keeps the overview payload small.
485
- stageCounts: z.object({
486
- populated: z.number().int(),
487
- extracted: z.number().int(),
488
- qualified: z.number().int(),
489
- discovered: z.number().int(),
490
- verified: z.number().int(),
491
- personalized: z.number().int(),
492
- uploaded: z.number().int()
493
- }),
494
- deliverability: z.object({
495
- valid: z.number().int(),
496
- risky: z.number().int(),
497
- invalid: z.number().int(),
498
- unknown: z.number().int(),
499
- bounced: z.number().int()
500
- })
501
- })
502
-
503
- export const ListTelemetrySchema = z.object({
504
- listId: UuidSchema,
505
- totalCompanies: z.number().int(),
506
- totalContacts: z.number().int(),
507
- stageCounts: ListStageCountsSchema.shape.stageCounts,
508
- deliverability: ListStageCountsSchema.shape.deliverability,
509
- activeWorkflows: z.array(z.string()).optional()
510
- })
511
-
512
- // ---------------------------------------------------------------------------
513
- // Params
514
- // ---------------------------------------------------------------------------
515
-
516
- export const ListIdParamsSchema = z.object({
517
- listId: UuidSchema
518
- })
519
-
520
- // ---------------------------------------------------------------------------
521
- // Request body schemas (all use .strict() — rejects unknown fields)
522
- // ---------------------------------------------------------------------------
523
-
524
- export const CreateListRequestSchema = z
525
- .object({
526
- name: z.string().trim().min(1).max(255),
527
- description: z.string().trim().nullable().optional(),
528
- status: ListStatusSchema.optional(),
529
- buildTemplateId: ProspectingBuildTemplateIdSchema.optional(),
530
- scrapingConfig: ScrapingConfigSchema.optional(),
531
- icp: IcpRubricSchema.optional(),
532
- pipelineConfig: PipelineConfigSchema.optional()
533
- })
534
- .strict()
535
-
536
- export const UpdateListRequestSchema = z
537
- .object({
538
- name: z.string().trim().min(1).max(255).optional(),
539
- description: z.string().trim().nullable().optional(),
540
- batchIds: z.array(z.string()).optional(),
541
- buildTemplateId: ProspectingBuildTemplateIdSchema.optional(),
542
- confirmBuildTemplateChange: z.literal(true).optional()
543
- })
544
- .strict()
545
- .refine(
546
- (data) =>
547
- data.name !== undefined ||
548
- data.description !== undefined ||
549
- data.batchIds !== undefined ||
550
- data.buildTemplateId !== undefined,
551
- {
552
- message: 'At least one field (name, description, batchIds, or buildTemplateId) must be provided'
553
- }
554
- )
555
- .refine((data) => data.buildTemplateId === undefined || data.confirmBuildTemplateChange === true, {
556
- message: 'confirmBuildTemplateChange must be true when changing buildTemplateId',
557
- path: ['confirmBuildTemplateChange']
558
- })
559
-
560
- /**
561
- * Status-only PATCH body for `/acquisition/lists/:listId/status`.
562
- * Replaces the previous `transitionList` flow.
563
- */
564
- export const UpdateListStatusRequestSchema = z
565
- .object({
566
- status: ListStatusSchema
567
- })
568
- .strict()
569
-
570
- /**
571
- * Partial patch for the three jsonb config columns. UI sends only the edited
572
- * subtree; server writes the field as-is (no deep merge — each column is
573
- * replaced atomically when present in the patch).
574
- */
575
- export const UpdateListConfigRequestSchema = z
576
- .object({
577
- scrapingConfig: ScrapingConfigSchema.partial().optional(),
578
- icp: IcpRubricSchema.partial().optional(),
579
- pipelineConfig: PipelineConfigSchema.partial().optional()
580
- })
581
- .strict()
582
- .refine((data) => data.scrapingConfig !== undefined || data.icp !== undefined || data.pipelineConfig !== undefined, {
583
- message: 'At least one of scrapingConfig, icp, or pipelineConfig must be provided'
584
- })
585
-
586
- export const AddCompaniesToListRequestSchema = z
587
- .object({
588
- companyIds: z.array(UuidSchema).min(1).max(1000)
589
- })
590
- .strict()
591
-
592
- export const RemoveCompaniesFromListRequestSchema = z
593
- .object({
594
- companyIds: z.array(UuidSchema).min(1).max(1000)
595
- })
596
- .strict()
597
-
598
- export const AddContactsToListRequestSchema = z
599
- .object({
600
- contactIds: z.array(UuidSchema).min(1).max(1000)
601
- })
602
- .strict()
603
-
604
- export const RecordListExecutionRequestSchema = z
605
- .object({
606
- executionId: UuidSchema,
607
- configSnapshot: z.record(z.string(), z.unknown()).optional()
608
- })
609
- .strict()
610
-
611
- // ---------------------------------------------------------------------------
612
- // Response schemas (no .strict() allows forward-compatible additions)
613
- // ---------------------------------------------------------------------------
614
-
615
- /**
616
- * Single list as returned by /api/acquisition/lists/:id etc.
617
- * Camel-cased domain shape matching AcqList in types.ts.
618
- */
619
- export const AcqListResponseSchema = z.object({
620
- id: z.string(),
621
- organizationId: z.string(),
622
- name: z.string(),
623
- description: z.string().nullable(),
624
- batchIds: z.array(z.string()),
625
- instantlyCampaignId: z.string().nullable(),
626
- /** Lifecycle status (draft | enriching | launched | closing | archived). */
627
- status: ListStatusSchema,
628
- metadata: AcqListMetadataSchema,
629
- launchedAt: z.string().nullable(),
630
- completedAt: z.string().nullable(),
631
- createdAt: z.string(),
632
- /** Scraping criteria stored as jsonb on the row. */
633
- scrapingConfig: ScrapingConfigSchema,
634
- /** ICP / qualification rubric stored as jsonb on the row. */
635
- icp: IcpRubricSchema,
636
- /** Pipeline presentation contract stored as jsonb on the row. */
637
- pipelineConfig: PipelineConfigSchema
638
- })
639
-
640
- export const AcqListListResponseSchema = z.array(AcqListResponseSchema)
641
-
642
- export const ListTelemetryResponseSchema = ListTelemetrySchema
643
-
644
- export const ListTelemetryListResponseSchema = z.array(ListTelemetrySchema)
645
-
646
- /**
647
- * Terminal row-level status for one lead-gen processing stage.
648
- * Missing key still means not attempted; legacy boolean `true` is normalized
649
- * to `success` by the API reader during rollout.
650
- */
651
- export const ProcessingStageStatusSchema = z.enum(['success', 'no_result', 'skipped', 'error'])
652
-
653
- /**
654
- * Per-stage progress aggregate for a single pipeline stage.
655
- * `attempted` counts terminal statuses, including success, no-result, skipped,
656
- * error, and tolerant-reader `other` values.
657
- * `total` = total member/company count for the list.
658
- */
659
- export const ListStageProgressSchema = z.object({
660
- total: z.number().int().min(0),
661
- attempted: z.number().int().min(0),
662
- success: z.number().int().min(0),
663
- noResult: z.number().int().min(0),
664
- skipped: z.number().int().min(0),
665
- error: z.number().int().min(0),
666
- other: z.number().int().min(0),
667
- notAttempted: z.number().int().min(0)
668
- })
669
-
670
- /**
671
- * Progress response for GET /acquisition/lists/:listId/progress.
672
- * Aggregated on-demand from processing_state status values.
673
- * Stage keys are discovered from observed processing_state keys.
674
- */
675
- export const ListProgressResponseSchema = z.object({
676
- totalMembers: z.number().int().min(0),
677
- totalCompanies: z.number().int().min(0),
678
- byCompanyStage: z.record(z.string(), ListStageProgressSchema),
679
- byContactStage: z.record(z.string(), ListStageProgressSchema)
680
- })
681
-
682
- /**
683
- * Row from acq_list_executions joined with the execution summary,
684
- * shaped for the /lists/:id/executions response.
685
- */
686
- export const ListExecutionSummarySchema = z.object({
687
- executionId: z.string(),
688
- resourceId: z.string(),
689
- status: z.string(),
690
- createdAt: z.string(),
691
- completedAt: z.string().nullable(),
692
- durationMs: z.number().int().nullable()
693
- })
694
-
695
- export const ListExecutionsResponseSchema = z.array(ListExecutionSummarySchema)
696
-
697
- // ---------------------------------------------------------------------------
698
- // Company / Contact API Schemas
699
- // ---------------------------------------------------------------------------
700
-
701
- const QueryBooleanSchema = z.preprocess((value) => {
702
- if (value === 'true' || value === '1' || value === true) return true
703
- if (value === 'false' || value === '0' || value === false) return false
704
- return value
705
- }, z.boolean())
706
-
707
- export const AcqCompanyStatusSchema = z.enum(['active', 'invalid'])
708
- export const AcqContactStatusSchema = z.enum(['active', 'invalid'])
709
- export const AcqEmailValidSchema = z.enum(['VALID', 'INVALID', 'RISKY', 'UNKNOWN'])
710
-
711
- export const CompanyIdParamsSchema = z.object({
712
- companyId: UuidSchema
713
- })
714
-
715
- export const ContactIdParamsSchema = z.object({
716
- contactId: UuidSchema
717
- })
718
-
719
- export const ListCompaniesQuerySchema = z
720
- .object({
721
- search: z.string().trim().min(1).max(200).optional(),
722
- listId: UuidSchema.optional(),
723
- domain: z.string().trim().min(1).max(255).optional(),
724
- website: z.string().trim().min(1).max(2048).optional(),
725
- segment: z.string().trim().min(1).max(255).optional(),
726
- category: z.string().trim().min(1).max(255).optional(),
727
- batchId: z.string().trim().min(1).max(255).optional(),
728
- status: AcqCompanyStatusSchema.optional(),
729
- includeAll: QueryBooleanSchema.optional(),
730
- limit: z.coerce.number().int().min(1).max(5000).default(50),
731
- offset: z.coerce.number().int().min(0).default(0)
732
- })
733
- .strict()
734
-
735
- export const ListContactsQuerySchema = z
736
- .object({
737
- search: z.string().trim().min(1).max(200).optional(),
738
- listId: UuidSchema.optional(),
739
- openingLineIsNull: QueryBooleanSchema.optional(),
740
- batchId: z.string().trim().min(1).max(255).optional(),
741
- contactStatus: AcqContactStatusSchema.optional(),
742
- limit: z.coerce.number().int().min(1).max(5000).default(5000),
743
- offset: z.coerce.number().int().min(0).default(0)
744
- })
745
- .strict()
746
-
747
- export const CreateCompanyRequestSchema = z
748
- .object({
749
- name: z.string().trim().min(1).max(255),
750
- domain: z.string().trim().min(1).max(255).optional(),
751
- linkedinUrl: z.string().trim().url().optional(),
752
- website: z.string().trim().url().optional(),
753
- numEmployees: z.number().int().min(0).optional(),
754
- foundedYear: z.number().int().optional(),
755
- locationCity: z.string().trim().min(1).max(255).optional(),
756
- locationState: z.string().trim().min(1).max(255).optional(),
757
- category: z.string().trim().min(1).max(255).optional(),
758
- source: z.string().trim().min(1).max(255).optional(),
759
- batchId: z.string().trim().min(1).max(255).optional(),
760
- verticalResearch: z.string().trim().min(1).max(5000).optional()
761
- })
762
- .strict()
763
-
764
- export const UpdateCompanyRequestSchema = z
765
- .object({
766
- name: z.string().trim().min(1).max(255).optional(),
767
- domain: z.string().trim().min(1).max(255).optional(),
768
- linkedinUrl: z.string().trim().url().optional(),
769
- website: z.string().trim().url().optional(),
770
- numEmployees: z.number().int().min(0).optional(),
771
- foundedYear: z.number().int().optional(),
772
- locationCity: z.string().trim().min(1).max(255).optional(),
773
- locationState: z.string().trim().min(1).max(255).optional(),
774
- category: z.string().trim().min(1).max(255).optional(),
775
- segment: z.string().trim().min(1).max(255).optional(),
776
- pipelineStatus: z.record(z.string(), z.unknown()).optional(),
777
- enrichmentData: z.record(z.string(), z.unknown()).optional(),
778
- source: z.string().trim().min(1).max(255).optional(),
779
- batchId: z.string().trim().min(1).max(255).optional(),
780
- status: AcqCompanyStatusSchema.optional(),
781
- verticalResearch: z.string().trim().min(1).max(5000).nullable().optional()
782
- })
783
- .strict()
784
- .refine(
785
- (data) =>
786
- data.name !== undefined ||
787
- data.domain !== undefined ||
788
- data.linkedinUrl !== undefined ||
789
- data.website !== undefined ||
790
- data.numEmployees !== undefined ||
791
- data.foundedYear !== undefined ||
792
- data.locationCity !== undefined ||
793
- data.locationState !== undefined ||
794
- data.category !== undefined ||
795
- data.segment !== undefined ||
796
- data.pipelineStatus !== undefined ||
797
- data.enrichmentData !== undefined ||
798
- data.source !== undefined ||
799
- data.batchId !== undefined ||
800
- data.status !== undefined ||
801
- data.verticalResearch !== undefined,
802
- {
803
- message: 'At least one field must be provided'
804
- }
805
- )
806
-
807
- export const CreateContactRequestSchema = z
808
- .object({
809
- email: z.string().trim().email(),
810
- companyId: UuidSchema.optional(),
811
- firstName: z.string().trim().min(1).max(255).optional(),
812
- lastName: z.string().trim().min(1).max(255).optional(),
813
- linkedinUrl: z.string().trim().url().optional(),
814
- title: z.string().trim().min(1).max(255).optional(),
815
- source: z.string().trim().min(1).max(255).optional(),
816
- sourceId: z.string().trim().min(1).max(255).optional(),
817
- batchId: z.string().trim().min(1).max(255).optional()
818
- })
819
- .strict()
820
-
821
- export const UpdateContactRequestSchema = z
822
- .object({
823
- companyId: UuidSchema.optional(),
824
- emailValid: AcqEmailValidSchema.optional(),
825
- firstName: z.string().trim().min(1).max(255).optional(),
826
- lastName: z.string().trim().min(1).max(255).optional(),
827
- linkedinUrl: z.string().trim().url().optional(),
828
- title: z.string().trim().min(1).max(255).optional(),
829
- headline: z.string().trim().min(1).max(5000).optional(),
830
- filterReason: z.string().trim().min(1).max(5000).optional(),
831
- openingLine: z.string().trim().min(1).max(5000).optional(),
832
- pipelineStatus: z.record(z.string(), z.unknown()).optional(),
833
- enrichmentData: z.record(z.string(), z.unknown()).optional(),
834
- status: AcqContactStatusSchema.optional()
835
- })
836
- .strict()
837
- .refine(
838
- (data) =>
839
- data.companyId !== undefined ||
840
- data.emailValid !== undefined ||
841
- data.firstName !== undefined ||
842
- data.lastName !== undefined ||
843
- data.linkedinUrl !== undefined ||
844
- data.title !== undefined ||
845
- data.headline !== undefined ||
846
- data.filterReason !== undefined ||
847
- data.openingLine !== undefined ||
848
- data.pipelineStatus !== undefined ||
849
- data.enrichmentData !== undefined ||
850
- data.status !== undefined,
851
- {
852
- message: 'At least one field must be provided'
853
- }
854
- )
855
-
856
- export const AcqCompanyResponseSchema = z.object({
857
- id: z.string(),
858
- organizationId: z.string(),
859
- name: z.string(),
860
- domain: z.string().nullable(),
861
- linkedinUrl: z.string().nullable(),
862
- website: z.string().nullable(),
863
- numEmployees: z.number().nullable(),
864
- foundedYear: z.number().nullable(),
865
- locationCity: z.string().nullable(),
866
- locationState: z.string().nullable(),
867
- category: z.string().nullable(),
868
- categoryPain: z.string().nullable(),
869
- segment: z.string().nullable(),
870
- pipelineStatus: z.record(z.string(), z.unknown()).nullable(),
871
- enrichmentData: z.record(z.string(), z.unknown()).nullable(),
872
- source: z.string().nullable(),
873
- batchId: z.string().nullable(),
874
- status: AcqCompanyStatusSchema,
875
- contactCount: z.number().int().min(0),
876
- verticalResearch: z.string().nullable(),
877
- createdAt: z.string(),
878
- updatedAt: z.string()
879
- })
880
-
881
- export const AcqCompanyListResponseSchema = z.object({
882
- data: z.array(AcqCompanyResponseSchema),
883
- total: z.number().int(),
884
- limit: z.number().int(),
885
- offset: z.number().int()
886
- })
887
-
888
- export const AcqCompanyFacetsResponseSchema = z.object({
889
- segments: z.array(z.string()),
890
- categories: z.array(z.string()),
891
- statuses: z.array(AcqCompanyStatusSchema)
892
- })
893
-
894
- export const AcqContactCompanySummarySchema = z.object({
895
- id: z.string(),
896
- name: z.string(),
897
- domain: z.string().nullable(),
898
- website: z.string().nullable(),
899
- linkedinUrl: z.string().nullable(),
900
- segment: z.string().nullable(),
901
- category: z.string().nullable(),
902
- status: AcqCompanyStatusSchema
903
- })
904
-
905
- export const AcqContactResponseSchema = z.object({
906
- id: z.string(),
907
- organizationId: z.string(),
908
- companyId: z.string().nullable(),
909
- email: z.string(),
910
- emailValid: AcqEmailValidSchema.nullable(),
911
- firstName: z.string().nullable(),
912
- lastName: z.string().nullable(),
913
- linkedinUrl: z.string().nullable(),
914
- title: z.string().nullable(),
915
- headline: z.string().nullable(),
916
- filterReason: z.string().nullable(),
917
- openingLine: z.string().nullable(),
918
- source: z.string().nullable(),
919
- sourceId: z.string().nullable(),
920
- pipelineStatus: z.record(z.string(), z.unknown()).nullable(),
921
- enrichmentData: z.record(z.string(), z.unknown()).nullable(),
922
- attioPersonId: z.string().nullable(),
923
- batchId: z.string().nullable(),
924
- status: AcqContactStatusSchema,
925
- company: AcqContactCompanySummarySchema.nullable().optional(),
926
- createdAt: z.string(),
927
- updatedAt: z.string()
928
- })
929
-
930
- export const AcqContactListResponseSchema = z.object({
931
- data: z.array(AcqContactResponseSchema),
932
- total: z.number().int(),
933
- limit: z.number().int(),
934
- offset: z.number().int()
935
- })
936
-
937
- // ---------------------------------------------------------------------------
938
- // Track A: Artifacts API Schemas
939
- // ---------------------------------------------------------------------------
940
-
941
- export const AcqArtifactOwnerKindSchema = z.enum(['company', 'contact', 'deal', 'list', 'list_member'])
942
-
943
- export const ListArtifactsQuerySchema = z
944
- .object({
945
- ownerKind: AcqArtifactOwnerKindSchema,
946
- ownerId: UuidSchema
947
- })
948
- .strict()
949
-
950
- export const CreateArtifactRequestSchema = z
951
- .object({
952
- ownerKind: AcqArtifactOwnerKindSchema,
953
- ownerId: UuidSchema,
954
- kind: z.string().trim().min(1).max(255),
955
- content: z.record(z.string(), z.unknown()),
956
- sourceExecutionId: UuidSchema.optional()
957
- })
958
- .strict()
959
-
960
- export const AcqArtifactResponseSchema = z.object({
961
- id: z.string(),
962
- organizationId: z.string(),
963
- ownerKind: z.string(),
964
- ownerId: z.string(),
965
- kind: z.string(),
966
- content: z.record(z.string(), z.unknown()),
967
- sourceExecutionId: z.string().nullable(),
968
- createdBy: z.string().nullable(),
969
- createdAt: z.string(),
970
- version: z.number().int()
971
- })
972
-
973
- export const AcqArtifactListResponseSchema = z.object({
974
- artifacts: z.array(AcqArtifactResponseSchema)
975
- })
976
-
977
- // ---------------------------------------------------------------------------
978
- // Track B: List Members API Schemas
979
- // ---------------------------------------------------------------------------
980
-
981
- export const ListMembersQuerySchema = z
982
- .object({
983
- limit: z.coerce.number().int().min(1).max(500).default(50),
984
- offset: z.coerce.number().int().min(0).default(0)
985
- })
986
- .strict()
987
-
988
- export const MemberIdParamsSchema = z.object({
989
- memberId: UuidSchema
990
- })
991
-
992
- export const AcqListMemberContactSummarySchema = z.object({
993
- id: z.string(),
994
- email: z.string(),
995
- firstName: z.string().nullable(),
996
- lastName: z.string().nullable(),
997
- title: z.string().nullable(),
998
- linkedinUrl: z.string().nullable(),
999
- companyId: z.string().nullable()
1000
- })
1001
-
1002
- export const AcqListMemberResponseSchema = z.object({
1003
- id: z.string(),
1004
- listId: z.string(),
1005
- contactId: z.string(),
1006
- pipelineKey: z.string(),
1007
- stageKey: z.string(),
1008
- stateKey: z.string(),
1009
- activityLog: z.unknown(),
1010
- addedAt: z.string(),
1011
- addedBy: z.string().nullable(),
1012
- sourceExecutionId: z.string().nullable(),
1013
- contact: AcqListMemberContactSummarySchema.nullable()
1014
- })
1015
-
1016
- export const AcqListMembersResponseSchema = z.object({
1017
- members: z.array(AcqListMemberResponseSchema)
1018
- })
1019
-
1020
- // ---------------------------------------------------------------------------
1021
- // Track B: List Companies API Schemas
1022
- // ---------------------------------------------------------------------------
1023
-
1024
- export const ListCompanyIdParamsSchema = z.object({
1025
- listCompanyId: UuidSchema
1026
- })
1027
-
1028
- export const AcqListCompanyResponseSchema = z.object({
1029
- id: z.string(),
1030
- listId: z.string(),
1031
- companyId: z.string(),
1032
- pipelineKey: z.string(),
1033
- stageKey: z.string(),
1034
- stateKey: z.string(),
1035
- activityLog: z.unknown(),
1036
- addedAt: z.string(),
1037
- addedBy: z.string().nullable(),
1038
- sourceExecutionId: z.string().nullable()
1039
- })
1040
-
1041
- // ---------------------------------------------------------------------------
1042
- // Track B: Transition Request (shared by list, list-member, list-company)
1043
- // TransitionItemRequestSchema already exists above (for deals) — reuse it.
1044
- // ---------------------------------------------------------------------------
1045
-
1046
- export const AcqCompanySchemas = {
1047
- CompanyIdParams: CompanyIdParamsSchema,
1048
- ListCompaniesQuery: ListCompaniesQuerySchema,
1049
- CreateCompanyRequest: CreateCompanyRequestSchema,
1050
- UpdateCompanyRequest: UpdateCompanyRequestSchema,
1051
- AcqCompanyResponse: AcqCompanyResponseSchema,
1052
- AcqCompanyListResponse: AcqCompanyListResponseSchema,
1053
- AcqCompanyFacetsResponse: AcqCompanyFacetsResponseSchema
1054
- }
1055
-
1056
- export const AcqContactSchemas = {
1057
- ContactIdParams: ContactIdParamsSchema,
1058
- ListContactsQuery: ListContactsQuerySchema,
1059
- CreateContactRequest: CreateContactRequestSchema,
1060
- UpdateContactRequest: UpdateContactRequestSchema,
1061
- AcqContactResponse: AcqContactResponseSchema,
1062
- AcqContactListResponse: AcqContactListResponseSchema
1063
- }
1064
-
1065
- export type AcqCompanyStatus = z.infer<typeof AcqCompanyStatusSchema>
1066
- export type AcqContactStatus = z.infer<typeof AcqContactStatusSchema>
1067
- export type AcqEmailValid = z.infer<typeof AcqEmailValidSchema>
1068
- export type CompanyIdParams = z.infer<typeof CompanyIdParamsSchema>
1069
- export type ContactIdParams = z.infer<typeof ContactIdParamsSchema>
1070
- export type ListCompaniesQuery = z.infer<typeof ListCompaniesQuerySchema>
1071
- export type ListContactsQuery = z.infer<typeof ListContactsQuerySchema>
1072
- export type CreateCompanyRequest = z.infer<typeof CreateCompanyRequestSchema>
1073
- export type UpdateCompanyRequest = z.infer<typeof UpdateCompanyRequestSchema>
1074
- export type CreateContactRequest = z.infer<typeof CreateContactRequestSchema>
1075
- export type UpdateContactRequest = z.infer<typeof UpdateContactRequestSchema>
1076
- export type AcqCompanyResponse = z.infer<typeof AcqCompanyResponseSchema>
1077
- export type AcqCompanyListResponse = z.infer<typeof AcqCompanyListResponseSchema>
1078
- export type AcqCompanyFacetsResponse = z.infer<typeof AcqCompanyFacetsResponseSchema>
1079
- export type AcqContactCompanySummary = z.infer<typeof AcqContactCompanySummarySchema>
1080
- export type AcqContactResponse = z.infer<typeof AcqContactResponseSchema>
1081
- export type AcqContactListResponse = z.infer<typeof AcqContactListResponseSchema>
1082
-
1083
- // ---------------------------------------------------------------------------
1084
- // Bundled export
1085
- // ---------------------------------------------------------------------------
1086
-
1087
- export const AcqListSchemas = {
1088
- // Params
1089
- ListIdParams: ListIdParamsSchema,
1090
-
1091
- // Primitives (for UI / tests)
1092
- ListStatus: ListStatusSchema,
1093
- ScrapingConfig: ScrapingConfigSchema,
1094
- IcpRubric: IcpRubricSchema,
1095
- PipelineConfig: PipelineConfigSchema,
1096
- PipelineStage: PipelineStageSchema,
1097
- BuildPlanSnapshot: BuildPlanSnapshotSchema,
1098
- BuildPlanSnapshotStep: BuildPlanSnapshotStepSchema,
1099
- AcqListMetadata: AcqListMetadataSchema,
1100
- ProcessingStageStatus: ProcessingStageStatusSchema,
1101
- ListStageCounts: ListStageCountsSchema,
1102
- ListTelemetry: ListTelemetrySchema,
1103
-
1104
- // Requests
1105
- CreateListRequest: CreateListRequestSchema,
1106
- UpdateListRequest: UpdateListRequestSchema,
1107
- UpdateListStatusRequest: UpdateListStatusRequestSchema,
1108
- UpdateListConfigRequest: UpdateListConfigRequestSchema,
1109
- AddCompaniesToListRequest: AddCompaniesToListRequestSchema,
1110
- RemoveCompaniesFromListRequest: RemoveCompaniesFromListRequestSchema,
1111
- AddContactsToListRequest: AddContactsToListRequestSchema,
1112
- RecordListExecutionRequest: RecordListExecutionRequestSchema,
1113
-
1114
- // Responses
1115
- AcqListResponse: AcqListResponseSchema,
1116
- AcqListListResponse: AcqListListResponseSchema,
1117
- ListTelemetryResponse: ListTelemetryResponseSchema,
1118
- ListTelemetryListResponse: ListTelemetryListResponseSchema,
1119
- ListExecutionsResponse: ListExecutionsResponseSchema,
1120
- ListProgressResponse: ListProgressResponseSchema
1121
- }
1122
-
1123
- // ---------------------------------------------------------------------------
1124
- // Track A/B bundled schemas
1125
- // ---------------------------------------------------------------------------
1126
-
1127
- export const AcqSubstrateSchemas = {
1128
- // Artifacts
1129
- ListArtifactsQuery: ListArtifactsQuerySchema,
1130
- CreateArtifactRequest: CreateArtifactRequestSchema,
1131
- AcqArtifactResponse: AcqArtifactResponseSchema,
1132
- AcqArtifactListResponse: AcqArtifactListResponseSchema,
1133
-
1134
- // List members
1135
- ListMembersQuery: ListMembersQuerySchema,
1136
- MemberIdParams: MemberIdParamsSchema,
1137
- AcqListMemberResponse: AcqListMemberResponseSchema,
1138
- AcqListMembersResponse: AcqListMembersResponseSchema,
1139
-
1140
- // List companies
1141
- ListCompanyIdParams: ListCompanyIdParamsSchema,
1142
- AcqListCompanyResponse: AcqListCompanyResponseSchema,
1143
-
1144
- // Transition (shared with deals — TransitionItemRequestSchema)
1145
- TransitionItemRequest: TransitionItemRequestSchema
1146
- }
1147
-
1148
- // ---------------------------------------------------------------------------
1149
- // Inferred types
1150
- // ---------------------------------------------------------------------------
1151
-
1152
- // ---------------------------------------------------------------------------
1153
- // Inferred types — Track A/B substrate
1154
- // ---------------------------------------------------------------------------
1155
-
1156
- export type AcqArtifactOwnerKind = z.infer<typeof AcqArtifactOwnerKindSchema>
1157
- export type ListArtifactsQuery = z.infer<typeof ListArtifactsQuerySchema>
1158
- export type CreateArtifactRequest = z.infer<typeof CreateArtifactRequestSchema>
1159
- export type AcqArtifactResponse = z.infer<typeof AcqArtifactResponseSchema>
1160
- export type AcqArtifactListResponse = z.infer<typeof AcqArtifactListResponseSchema>
1161
- export type ListMembersQuery = z.infer<typeof ListMembersQuerySchema>
1162
- export type MemberIdParams = z.infer<typeof MemberIdParamsSchema>
1163
- export type AcqListMemberContactSummary = z.infer<typeof AcqListMemberContactSummarySchema>
1164
- export type AcqListMemberResponse = z.infer<typeof AcqListMemberResponseSchema>
1165
- export type AcqListMembersResponse = z.infer<typeof AcqListMembersResponseSchema>
1166
- export type ListCompanyIdParams = z.infer<typeof ListCompanyIdParamsSchema>
1167
- export type AcqListCompanyResponse = z.infer<typeof AcqListCompanyResponseSchema>
1168
-
1169
- // ---------------------------------------------------------------------------
1170
-
1171
- export type ListStatus = z.infer<typeof ListStatusSchema>
1172
- export type ScrapingConfig = z.infer<typeof ScrapingConfigSchema>
1173
- export type IcpRubric = z.infer<typeof IcpRubricSchema>
1174
- export type PipelineStage = z.infer<typeof PipelineStageSchema>
1175
- export type PipelineConfig = z.infer<typeof PipelineConfigSchema>
1176
- export type BuildPlanSnapshotStep = z.infer<typeof BuildPlanSnapshotStepSchema>
1177
- export type BuildPlanSnapshot = z.infer<typeof BuildPlanSnapshotSchema>
1178
- export type AcqListMetadata = z.infer<typeof AcqListMetadataSchema>
1179
- export type ProcessingStageStatus = z.infer<typeof ProcessingStageStatusSchema>
1180
- export type ListStageCountsInput = z.infer<typeof ListStageCountsSchema>['stageCounts']
1181
- export type ListTelemetryInput = z.infer<typeof ListTelemetrySchema>
1182
- export type ListIdParams = z.infer<typeof ListIdParamsSchema>
1183
- export type CreateListRequest = z.infer<typeof CreateListRequestSchema>
1184
- export type UpdateListRequest = z.infer<typeof UpdateListRequestSchema>
1185
- export type UpdateListStatusRequest = z.infer<typeof UpdateListStatusRequestSchema>
1186
- export type UpdateListConfigRequest = z.infer<typeof UpdateListConfigRequestSchema>
1187
- export type AddCompaniesToListRequest = z.infer<typeof AddCompaniesToListRequestSchema>
1188
- export type RemoveCompaniesFromListRequest = z.infer<typeof RemoveCompaniesFromListRequestSchema>
1189
- export type AddContactsToListRequest = z.infer<typeof AddContactsToListRequestSchema>
1190
- export type RecordListExecutionRequest = z.infer<typeof RecordListExecutionRequestSchema>
1191
- export type AcqListResponse = z.infer<typeof AcqListResponseSchema>
1192
- export type AcqListListResponse = z.infer<typeof AcqListListResponseSchema>
1193
- export type ListTelemetryResponse = z.infer<typeof ListTelemetryResponseSchema>
1194
- export type ListTelemetryListResponse = z.infer<typeof ListTelemetryListResponseSchema>
1195
- export type ListExecutionSummaryInput = z.infer<typeof ListExecutionSummarySchema>
1196
- export type ListExecutionsResponse = z.infer<typeof ListExecutionsResponseSchema>
1197
- export type ListStageProgress = z.infer<typeof ListStageProgressSchema>
1198
- export type ListProgress = z.infer<typeof ListProgressResponseSchema>
451
+ })
452
+ .refine((step) => step.defaultBatchSize <= step.maxBatchSize, {
453
+ message: 'defaultBatchSize must be less than or equal to maxBatchSize',
454
+ path: ['defaultBatchSize']
455
+ })
456
+
457
+ export const BuildPlanSnapshotSchema = z.object({
458
+ templateId: z.string().trim().min(1).max(100),
459
+ templateLabel: z.string().trim().min(1).max(120),
460
+ steps: z.array(BuildPlanSnapshotStepSchema).min(1)
461
+ })
462
+
463
+ export const AcqListMetadataSchema = z
464
+ .object({
465
+ buildPlanSnapshot: BuildPlanSnapshotSchema.optional()
466
+ })
467
+ .catchall(z.unknown())
468
+
469
+ export const ProspectingBuildTemplateIdSchema = z.string().trim().min(1).max(100).refine(isProspectingBuildTemplateId, {
470
+ message: 'buildTemplateId must match a known prospecting build template'
471
+ })
472
+
473
+ // ---------------------------------------------------------------------------
474
+ // List telemetry / progress schemas
475
+ // ---------------------------------------------------------------------------
476
+
477
+ export const ListStageCountsSchema = z.object({
478
+ // Attempted counts by canonical lead-gen stage. The detailed status
479
+ // distribution lives on ListProgress; telemetry keeps the overview payload small.
480
+ stageCounts: z.object({
481
+ populated: z.number().int(),
482
+ extracted: z.number().int(),
483
+ qualified: z.number().int(),
484
+ discovered: z.number().int(),
485
+ verified: z.number().int(),
486
+ personalized: z.number().int(),
487
+ uploaded: z.number().int()
488
+ }),
489
+ deliverability: z.object({
490
+ valid: z.number().int(),
491
+ risky: z.number().int(),
492
+ invalid: z.number().int(),
493
+ unknown: z.number().int(),
494
+ bounced: z.number().int()
495
+ })
496
+ })
497
+
498
+ export const ListTelemetrySchema = z.object({
499
+ listId: UuidSchema,
500
+ totalCompanies: z.number().int(),
501
+ totalContacts: z.number().int(),
502
+ stageCounts: ListStageCountsSchema.shape.stageCounts,
503
+ deliverability: ListStageCountsSchema.shape.deliverability,
504
+ activeWorkflows: z.array(z.string()).optional()
505
+ })
506
+
507
+ // ---------------------------------------------------------------------------
508
+ // Params
509
+ // ---------------------------------------------------------------------------
510
+
511
+ export const ListIdParamsSchema = z.object({
512
+ listId: UuidSchema
513
+ })
514
+
515
+ // ---------------------------------------------------------------------------
516
+ // Request body schemas (all use .strict() — rejects unknown fields)
517
+ // ---------------------------------------------------------------------------
518
+
519
+ export const CreateListRequestSchema = z
520
+ .object({
521
+ name: z.string().trim().min(1).max(255),
522
+ description: z.string().trim().nullable().optional(),
523
+ status: ListStatusSchema.optional(),
524
+ buildTemplateId: ProspectingBuildTemplateIdSchema.optional(),
525
+ scrapingConfig: ScrapingConfigSchema.optional(),
526
+ icp: IcpRubricSchema.optional(),
527
+ pipelineConfig: PipelineConfigSchema.optional()
528
+ })
529
+ .strict()
530
+
531
+ export const UpdateListRequestSchema = z
532
+ .object({
533
+ name: z.string().trim().min(1).max(255).optional(),
534
+ description: z.string().trim().nullable().optional(),
535
+ batchIds: z.array(z.string()).optional(),
536
+ buildTemplateId: ProspectingBuildTemplateIdSchema.optional(),
537
+ confirmBuildTemplateChange: z.literal(true).optional()
538
+ })
539
+ .strict()
540
+ .refine(
541
+ (data) =>
542
+ data.name !== undefined ||
543
+ data.description !== undefined ||
544
+ data.batchIds !== undefined ||
545
+ data.buildTemplateId !== undefined,
546
+ {
547
+ message: 'At least one field (name, description, batchIds, or buildTemplateId) must be provided'
548
+ }
549
+ )
550
+ .refine((data) => data.buildTemplateId === undefined || data.confirmBuildTemplateChange === true, {
551
+ message: 'confirmBuildTemplateChange must be true when changing buildTemplateId',
552
+ path: ['confirmBuildTemplateChange']
553
+ })
554
+
555
+ /**
556
+ * Status-only PATCH body for `/acquisition/lists/:listId/status`.
557
+ * Replaces the previous `transitionList` flow.
558
+ */
559
+ export const UpdateListStatusRequestSchema = z
560
+ .object({
561
+ status: ListStatusSchema
562
+ })
563
+ .strict()
564
+
565
+ /**
566
+ * Partial patch for the three jsonb config columns. UI sends only the edited
567
+ * subtree; server writes the field as-is (no deep merge — each column is
568
+ * replaced atomically when present in the patch).
569
+ */
570
+ export const UpdateListConfigRequestSchema = z
571
+ .object({
572
+ scrapingConfig: ScrapingConfigSchema.partial().optional(),
573
+ icp: IcpRubricSchema.partial().optional(),
574
+ pipelineConfig: PipelineConfigSchema.partial().optional()
575
+ })
576
+ .strict()
577
+ .refine((data) => data.scrapingConfig !== undefined || data.icp !== undefined || data.pipelineConfig !== undefined, {
578
+ message: 'At least one of scrapingConfig, icp, or pipelineConfig must be provided'
579
+ })
580
+
581
+ export const AddCompaniesToListRequestSchema = z
582
+ .object({
583
+ companyIds: z.array(UuidSchema).min(1).max(1000)
584
+ })
585
+ .strict()
586
+
587
+ export const RemoveCompaniesFromListRequestSchema = z
588
+ .object({
589
+ companyIds: z.array(UuidSchema).min(1).max(1000)
590
+ })
591
+ .strict()
592
+
593
+ export const AddContactsToListRequestSchema = z
594
+ .object({
595
+ contactIds: z.array(UuidSchema).min(1).max(1000)
596
+ })
597
+ .strict()
598
+
599
+ export const RecordListExecutionRequestSchema = z
600
+ .object({
601
+ executionId: UuidSchema,
602
+ configSnapshot: z.record(z.string(), z.unknown()).optional()
603
+ })
604
+ .strict()
605
+
606
+ // ---------------------------------------------------------------------------
607
+ // Response schemas (no .strict() — allows forward-compatible additions)
608
+ // ---------------------------------------------------------------------------
609
+
610
+ /**
611
+ * Single list as returned by /api/acquisition/lists/:id etc.
612
+ * Camel-cased domain shape matching AcqList in types.ts.
613
+ */
614
+ export const AcqListResponseSchema = z.object({
615
+ id: z.string(),
616
+ organizationId: z.string(),
617
+ name: z.string(),
618
+ description: z.string().nullable(),
619
+ batchIds: z.array(z.string()),
620
+ instantlyCampaignId: z.string().nullable(),
621
+ /** Lifecycle status (draft | enriching | launched | closing | archived). */
622
+ status: ListStatusSchema,
623
+ metadata: AcqListMetadataSchema,
624
+ launchedAt: z.string().nullable(),
625
+ completedAt: z.string().nullable(),
626
+ createdAt: z.string(),
627
+ /** Scraping criteria stored as jsonb on the row. */
628
+ scrapingConfig: ScrapingConfigSchema,
629
+ /** ICP / qualification rubric stored as jsonb on the row. */
630
+ icp: IcpRubricSchema,
631
+ /** Pipeline presentation contract stored as jsonb on the row. */
632
+ pipelineConfig: PipelineConfigSchema
633
+ })
634
+
635
+ export const AcqListListResponseSchema = z.array(AcqListResponseSchema)
636
+
637
+ export const ListTelemetryResponseSchema = ListTelemetrySchema
638
+
639
+ export const ListTelemetryListResponseSchema = z.array(ListTelemetrySchema)
640
+
641
+ /**
642
+ * Terminal row-level status for one lead-gen processing stage.
643
+ * Missing key still means not attempted; legacy boolean `true` is normalized
644
+ * to `success` by the API reader during rollout.
645
+ */
646
+ export const ProcessingStageStatusSchema = z.enum(['success', 'no_result', 'skipped', 'error'])
647
+
648
+ /**
649
+ * Per-stage progress aggregate for a single pipeline stage.
650
+ * `attempted` counts terminal statuses, including success, no-result, skipped,
651
+ * error, and tolerant-reader `other` values.
652
+ * `total` = total member/company count for the list.
653
+ */
654
+ export const ListStageProgressSchema = z.object({
655
+ total: z.number().int().min(0),
656
+ attempted: z.number().int().min(0),
657
+ success: z.number().int().min(0),
658
+ noResult: z.number().int().min(0),
659
+ skipped: z.number().int().min(0),
660
+ error: z.number().int().min(0),
661
+ other: z.number().int().min(0),
662
+ notAttempted: z.number().int().min(0)
663
+ })
664
+
665
+ /**
666
+ * Progress response for GET /acquisition/lists/:listId/progress.
667
+ * Aggregated on-demand from processing_state status values.
668
+ * Stage keys are discovered from observed processing_state keys.
669
+ */
670
+ export const ListProgressResponseSchema = z.object({
671
+ totalMembers: z.number().int().min(0),
672
+ totalCompanies: z.number().int().min(0),
673
+ byCompanyStage: z.record(z.string(), ListStageProgressSchema),
674
+ byContactStage: z.record(z.string(), ListStageProgressSchema)
675
+ })
676
+
677
+ /**
678
+ * Row from acq_list_executions joined with the execution summary,
679
+ * shaped for the /lists/:id/executions response.
680
+ */
681
+ export const ListExecutionSummarySchema = z.object({
682
+ executionId: z.string(),
683
+ resourceId: z.string(),
684
+ status: z.string(),
685
+ createdAt: z.string(),
686
+ completedAt: z.string().nullable(),
687
+ durationMs: z.number().int().nullable(),
688
+ input: z.unknown().nullable().optional()
689
+ })
690
+
691
+ export const ListExecutionsResponseSchema = z.array(ListExecutionSummarySchema)
692
+
693
+ // ---------------------------------------------------------------------------
694
+ // Company / Contact API Schemas
695
+ // ---------------------------------------------------------------------------
696
+
697
+ const QueryBooleanSchema = z.preprocess((value) => {
698
+ if (value === 'true' || value === '1' || value === true) return true
699
+ if (value === 'false' || value === '0' || value === false) return false
700
+ return value
701
+ }, z.boolean())
702
+
703
+ export const AcqCompanyStatusSchema = z.enum(['active', 'invalid'])
704
+ export const AcqContactStatusSchema = z.enum(['active', 'invalid'])
705
+ export const AcqEmailValidSchema = z.enum(['VALID', 'INVALID', 'RISKY', 'UNKNOWN'])
706
+
707
+ /**
708
+ * Zod schema mirroring the CompanyPipelineStatus interface from types.ts.
709
+ * Used by operations-layer CompanyRecord schemas to validate pipelineStatus
710
+ * against the canonical shape instead of using open passthrough().
711
+ */
712
+ export const CompanyPipelineStatusSchema = z
713
+ .object({
714
+ acquired: z.boolean().optional(),
715
+ enrichment: z
716
+ .record(
717
+ z.string(),
718
+ z
719
+ .object({
720
+ status: z.enum(['pending', 'complete', 'failed', 'skipped']),
721
+ completedAt: z.string().optional(),
722
+ error: z.string().optional()
723
+ })
724
+ .passthrough()
725
+ )
726
+ .optional()
727
+ })
728
+ .passthrough()
729
+
730
+ export const CompanyIdParamsSchema = z.object({
731
+ companyId: UuidSchema
732
+ })
733
+
734
+ export const ContactIdParamsSchema = z.object({
735
+ contactId: UuidSchema
736
+ })
737
+
738
+ export const ListCompaniesQuerySchema = z
739
+ .object({
740
+ search: z.string().trim().min(1).max(200).optional(),
741
+ listId: UuidSchema.optional(),
742
+ domain: z.string().trim().min(1).max(255).optional(),
743
+ website: z.string().trim().min(1).max(2048).optional(),
744
+ segment: z.string().trim().min(1).max(255).optional(),
745
+ category: z.string().trim().min(1).max(255).optional(),
746
+ batchId: z.string().trim().min(1).max(255).optional(),
747
+ status: AcqCompanyStatusSchema.optional(),
748
+ includeAll: QueryBooleanSchema.optional(),
749
+ limit: z.coerce.number().int().min(1).max(5000).default(50),
750
+ offset: z.coerce.number().int().min(0).default(0)
751
+ })
752
+ .strict()
753
+
754
+ export const ListContactsQuerySchema = z
755
+ .object({
756
+ search: z.string().trim().min(1).max(200).optional(),
757
+ listId: UuidSchema.optional(),
758
+ openingLineIsNull: QueryBooleanSchema.optional(),
759
+ batchId: z.string().trim().min(1).max(255).optional(),
760
+ contactStatus: AcqContactStatusSchema.optional(),
761
+ limit: z.coerce.number().int().min(1).max(5000).default(5000),
762
+ offset: z.coerce.number().int().min(0).default(0)
763
+ })
764
+ .strict()
765
+
766
+ export const CreateCompanyRequestSchema = z
767
+ .object({
768
+ name: z.string().trim().min(1).max(255),
769
+ domain: z.string().trim().min(1).max(255).optional(),
770
+ linkedinUrl: z.string().trim().url().optional(),
771
+ website: z.string().trim().url().optional(),
772
+ numEmployees: z.number().int().min(0).optional(),
773
+ foundedYear: z.number().int().optional(),
774
+ locationCity: z.string().trim().min(1).max(255).optional(),
775
+ locationState: z.string().trim().min(1).max(255).optional(),
776
+ category: z.string().trim().min(1).max(255).optional(),
777
+ source: z.string().trim().min(1).max(255).optional(),
778
+ batchId: z.string().trim().min(1).max(255).optional(),
779
+ verticalResearch: z.string().trim().min(1).max(5000).optional()
780
+ })
781
+ .strict()
782
+
783
+ export const UpdateCompanyRequestSchema = z
784
+ .object({
785
+ name: z.string().trim().min(1).max(255).optional(),
786
+ domain: z.string().trim().min(1).max(255).optional(),
787
+ linkedinUrl: z.string().trim().url().optional(),
788
+ website: z.string().trim().url().optional(),
789
+ numEmployees: z.number().int().min(0).optional(),
790
+ foundedYear: z.number().int().optional(),
791
+ locationCity: z.string().trim().min(1).max(255).optional(),
792
+ locationState: z.string().trim().min(1).max(255).optional(),
793
+ category: z.string().trim().min(1).max(255).optional(),
794
+ segment: z.string().trim().min(1).max(255).optional(),
795
+ pipelineStatus: z.record(z.string(), z.unknown()).optional(),
796
+ enrichmentData: z.record(z.string(), z.unknown()).optional(),
797
+ source: z.string().trim().min(1).max(255).optional(),
798
+ batchId: z.string().trim().min(1).max(255).optional(),
799
+ status: AcqCompanyStatusSchema.optional(),
800
+ verticalResearch: z.string().trim().min(1).max(5000).nullable().optional()
801
+ })
802
+ .strict()
803
+ .refine(
804
+ (data) =>
805
+ data.name !== undefined ||
806
+ data.domain !== undefined ||
807
+ data.linkedinUrl !== undefined ||
808
+ data.website !== undefined ||
809
+ data.numEmployees !== undefined ||
810
+ data.foundedYear !== undefined ||
811
+ data.locationCity !== undefined ||
812
+ data.locationState !== undefined ||
813
+ data.category !== undefined ||
814
+ data.segment !== undefined ||
815
+ data.pipelineStatus !== undefined ||
816
+ data.enrichmentData !== undefined ||
817
+ data.source !== undefined ||
818
+ data.batchId !== undefined ||
819
+ data.status !== undefined ||
820
+ data.verticalResearch !== undefined,
821
+ {
822
+ message: 'At least one field must be provided'
823
+ }
824
+ )
825
+
826
+ export const CreateContactRequestSchema = z
827
+ .object({
828
+ email: z.string().trim().email(),
829
+ companyId: UuidSchema.optional(),
830
+ firstName: z.string().trim().min(1).max(255).optional(),
831
+ lastName: z.string().trim().min(1).max(255).optional(),
832
+ linkedinUrl: z.string().trim().url().optional(),
833
+ title: z.string().trim().min(1).max(255).optional(),
834
+ source: z.string().trim().min(1).max(255).optional(),
835
+ sourceId: z.string().trim().min(1).max(255).optional(),
836
+ batchId: z.string().trim().min(1).max(255).optional()
837
+ })
838
+ .strict()
839
+
840
+ export const UpdateContactRequestSchema = z
841
+ .object({
842
+ companyId: UuidSchema.optional(),
843
+ emailValid: AcqEmailValidSchema.optional(),
844
+ firstName: z.string().trim().min(1).max(255).optional(),
845
+ lastName: z.string().trim().min(1).max(255).optional(),
846
+ linkedinUrl: z.string().trim().url().optional(),
847
+ title: z.string().trim().min(1).max(255).optional(),
848
+ headline: z.string().trim().min(1).max(5000).optional(),
849
+ filterReason: z.string().trim().min(1).max(5000).optional(),
850
+ openingLine: z.string().trim().min(1).max(5000).optional(),
851
+ pipelineStatus: z.record(z.string(), z.unknown()).optional(),
852
+ enrichmentData: z.record(z.string(), z.unknown()).optional(),
853
+ status: AcqContactStatusSchema.optional()
854
+ })
855
+ .strict()
856
+ .refine(
857
+ (data) =>
858
+ data.companyId !== undefined ||
859
+ data.emailValid !== undefined ||
860
+ data.firstName !== undefined ||
861
+ data.lastName !== undefined ||
862
+ data.linkedinUrl !== undefined ||
863
+ data.title !== undefined ||
864
+ data.headline !== undefined ||
865
+ data.filterReason !== undefined ||
866
+ data.openingLine !== undefined ||
867
+ data.pipelineStatus !== undefined ||
868
+ data.enrichmentData !== undefined ||
869
+ data.status !== undefined,
870
+ {
871
+ message: 'At least one field must be provided'
872
+ }
873
+ )
874
+
875
+ export const AcqCompanyResponseSchema = z.object({
876
+ id: z.string(),
877
+ organizationId: z.string(),
878
+ name: z.string(),
879
+ domain: z.string().nullable(),
880
+ linkedinUrl: z.string().nullable(),
881
+ website: z.string().nullable(),
882
+ numEmployees: z.number().nullable(),
883
+ foundedYear: z.number().nullable(),
884
+ locationCity: z.string().nullable(),
885
+ locationState: z.string().nullable(),
886
+ category: z.string().nullable(),
887
+ categoryPain: z.string().nullable(),
888
+ segment: z.string().nullable(),
889
+ pipelineStatus: z.record(z.string(), z.unknown()).nullable(),
890
+ enrichmentData: z.record(z.string(), z.unknown()).nullable(),
891
+ source: z.string().nullable(),
892
+ batchId: z.string().nullable(),
893
+ status: AcqCompanyStatusSchema,
894
+ contactCount: z.number().int().min(0),
895
+ verticalResearch: z.string().nullable(),
896
+ createdAt: z.string(),
897
+ updatedAt: z.string()
898
+ })
899
+
900
+ export const AcqCompanyListResponseSchema = z.object({
901
+ data: z.array(AcqCompanyResponseSchema),
902
+ total: z.number().int(),
903
+ limit: z.number().int(),
904
+ offset: z.number().int()
905
+ })
906
+
907
+ export const AcqCompanyFacetsResponseSchema = z.object({
908
+ segments: z.array(z.string()),
909
+ categories: z.array(z.string()),
910
+ statuses: z.array(AcqCompanyStatusSchema)
911
+ })
912
+
913
+ export const AcqContactCompanySummarySchema = z.object({
914
+ id: z.string(),
915
+ name: z.string(),
916
+ domain: z.string().nullable(),
917
+ website: z.string().nullable(),
918
+ linkedinUrl: z.string().nullable(),
919
+ segment: z.string().nullable(),
920
+ category: z.string().nullable(),
921
+ status: AcqCompanyStatusSchema
922
+ })
923
+
924
+ export const AcqContactResponseSchema = z.object({
925
+ id: z.string(),
926
+ organizationId: z.string(),
927
+ companyId: z.string().nullable(),
928
+ email: z.string(),
929
+ emailValid: AcqEmailValidSchema.nullable(),
930
+ firstName: z.string().nullable(),
931
+ lastName: z.string().nullable(),
932
+ linkedinUrl: z.string().nullable(),
933
+ title: z.string().nullable(),
934
+ headline: z.string().nullable(),
935
+ filterReason: z.string().nullable(),
936
+ openingLine: z.string().nullable(),
937
+ source: z.string().nullable(),
938
+ sourceId: z.string().nullable(),
939
+ pipelineStatus: z.record(z.string(), z.unknown()).nullable(),
940
+ enrichmentData: z.record(z.string(), z.unknown()).nullable(),
941
+ attioPersonId: z.string().nullable(),
942
+ batchId: z.string().nullable(),
943
+ status: AcqContactStatusSchema,
944
+ company: AcqContactCompanySummarySchema.nullable().optional(),
945
+ createdAt: z.string(),
946
+ updatedAt: z.string()
947
+ })
948
+
949
+ export const AcqContactListResponseSchema = z.object({
950
+ data: z.array(AcqContactResponseSchema),
951
+ total: z.number().int(),
952
+ limit: z.number().int(),
953
+ offset: z.number().int()
954
+ })
955
+
956
+ // ---------------------------------------------------------------------------
957
+ // Track A: Artifacts API Schemas
958
+ // ---------------------------------------------------------------------------
959
+
960
+ export const AcqArtifactOwnerKindSchema = z.enum(['company', 'contact', 'deal', 'list', 'list_member'])
961
+
962
+ export const ListArtifactsQuerySchema = z
963
+ .object({
964
+ ownerKind: AcqArtifactOwnerKindSchema,
965
+ ownerId: UuidSchema
966
+ })
967
+ .strict()
968
+
969
+ export const CreateArtifactRequestSchema = z
970
+ .object({
971
+ ownerKind: AcqArtifactOwnerKindSchema,
972
+ ownerId: UuidSchema,
973
+ kind: z.string().trim().min(1).max(255),
974
+ content: z.record(z.string(), z.unknown()),
975
+ sourceExecutionId: UuidSchema.optional()
976
+ })
977
+ .strict()
978
+
979
+ export const AcqArtifactResponseSchema = z.object({
980
+ id: z.string(),
981
+ organizationId: z.string(),
982
+ ownerKind: z.string(),
983
+ ownerId: z.string(),
984
+ kind: z.string(),
985
+ content: z.record(z.string(), z.unknown()),
986
+ sourceExecutionId: z.string().nullable(),
987
+ createdBy: z.string().nullable(),
988
+ createdAt: z.string(),
989
+ version: z.number().int()
990
+ })
991
+
992
+ export const AcqArtifactListResponseSchema = z.object({
993
+ artifacts: z.array(AcqArtifactResponseSchema)
994
+ })
995
+
996
+ // ---------------------------------------------------------------------------
997
+ // Track B: List Members API Schemas
998
+ // ---------------------------------------------------------------------------
999
+
1000
+ export const ListMembersQuerySchema = z
1001
+ .object({
1002
+ limit: z.coerce.number().int().min(1).max(500).default(50),
1003
+ offset: z.coerce.number().int().min(0).default(0)
1004
+ })
1005
+ .strict()
1006
+
1007
+ export const MemberIdParamsSchema = z.object({
1008
+ memberId: UuidSchema
1009
+ })
1010
+
1011
+ export const AcqListMemberContactSummarySchema = z.object({
1012
+ id: z.string(),
1013
+ email: z.string(),
1014
+ firstName: z.string().nullable(),
1015
+ lastName: z.string().nullable(),
1016
+ title: z.string().nullable(),
1017
+ linkedinUrl: z.string().nullable(),
1018
+ companyId: z.string().nullable()
1019
+ })
1020
+
1021
+ export const AcqListMemberResponseSchema = z.object({
1022
+ id: z.string(),
1023
+ listId: z.string(),
1024
+ contactId: z.string(),
1025
+ pipelineKey: z.string(),
1026
+ stageKey: z.string(),
1027
+ stateKey: z.string(),
1028
+ activityLog: z.unknown(),
1029
+ addedAt: z.string(),
1030
+ addedBy: z.string().nullable(),
1031
+ sourceExecutionId: z.string().nullable(),
1032
+ contact: AcqListMemberContactSummarySchema.nullable()
1033
+ })
1034
+
1035
+ export const AcqListMembersResponseSchema = z.object({
1036
+ members: z.array(AcqListMemberResponseSchema)
1037
+ })
1038
+
1039
+ // ---------------------------------------------------------------------------
1040
+ // Track B: List Companies API Schemas
1041
+ // ---------------------------------------------------------------------------
1042
+
1043
+ export const ListCompanyIdParamsSchema = z.object({
1044
+ listCompanyId: UuidSchema
1045
+ })
1046
+
1047
+ export const AcqListCompanyResponseSchema = z.object({
1048
+ id: z.string(),
1049
+ listId: z.string(),
1050
+ companyId: z.string(),
1051
+ pipelineKey: z.string(),
1052
+ stageKey: z.string(),
1053
+ stateKey: z.string(),
1054
+ activityLog: z.unknown(),
1055
+ addedAt: z.string(),
1056
+ addedBy: z.string().nullable(),
1057
+ sourceExecutionId: z.string().nullable()
1058
+ })
1059
+
1060
+ // ---------------------------------------------------------------------------
1061
+ // Track B: Transition Request (shared by list, list-member, list-company)
1062
+ // TransitionItemRequestSchema already exists above (for deals) — reuse it.
1063
+ // ---------------------------------------------------------------------------
1064
+
1065
+ export const AcqCompanySchemas = {
1066
+ CompanyIdParams: CompanyIdParamsSchema,
1067
+ ListCompaniesQuery: ListCompaniesQuerySchema,
1068
+ CreateCompanyRequest: CreateCompanyRequestSchema,
1069
+ UpdateCompanyRequest: UpdateCompanyRequestSchema,
1070
+ AcqCompanyResponse: AcqCompanyResponseSchema,
1071
+ AcqCompanyListResponse: AcqCompanyListResponseSchema,
1072
+ AcqCompanyFacetsResponse: AcqCompanyFacetsResponseSchema
1073
+ }
1074
+
1075
+ export const AcqContactSchemas = {
1076
+ ContactIdParams: ContactIdParamsSchema,
1077
+ ListContactsQuery: ListContactsQuerySchema,
1078
+ CreateContactRequest: CreateContactRequestSchema,
1079
+ UpdateContactRequest: UpdateContactRequestSchema,
1080
+ AcqContactResponse: AcqContactResponseSchema,
1081
+ AcqContactListResponse: AcqContactListResponseSchema
1082
+ }
1083
+
1084
+ export type AcqCompanyStatus = z.infer<typeof AcqCompanyStatusSchema>
1085
+ export type AcqContactStatus = z.infer<typeof AcqContactStatusSchema>
1086
+ export type AcqEmailValid = z.infer<typeof AcqEmailValidSchema>
1087
+ export type CompanyIdParams = z.infer<typeof CompanyIdParamsSchema>
1088
+ export type ContactIdParams = z.infer<typeof ContactIdParamsSchema>
1089
+ export type ListCompaniesQuery = z.infer<typeof ListCompaniesQuerySchema>
1090
+ export type ListContactsQuery = z.infer<typeof ListContactsQuerySchema>
1091
+ export type CreateCompanyRequest = z.infer<typeof CreateCompanyRequestSchema>
1092
+ export type UpdateCompanyRequest = z.infer<typeof UpdateCompanyRequestSchema>
1093
+ export type CreateContactRequest = z.infer<typeof CreateContactRequestSchema>
1094
+ export type UpdateContactRequest = z.infer<typeof UpdateContactRequestSchema>
1095
+ export type AcqCompanyResponse = z.infer<typeof AcqCompanyResponseSchema>
1096
+ export type AcqCompanyListResponse = z.infer<typeof AcqCompanyListResponseSchema>
1097
+ export type AcqCompanyFacetsResponse = z.infer<typeof AcqCompanyFacetsResponseSchema>
1098
+ export type AcqContactCompanySummary = z.infer<typeof AcqContactCompanySummarySchema>
1099
+ export type AcqContactResponse = z.infer<typeof AcqContactResponseSchema>
1100
+ export type AcqContactListResponse = z.infer<typeof AcqContactListResponseSchema>
1101
+
1102
+ // ---------------------------------------------------------------------------
1103
+ // Bundled export
1104
+ // ---------------------------------------------------------------------------
1105
+
1106
+ export const AcqListSchemas = {
1107
+ // Params
1108
+ ListIdParams: ListIdParamsSchema,
1109
+
1110
+ // Primitives (for UI / tests)
1111
+ ListStatus: ListStatusSchema,
1112
+ ScrapingConfig: ScrapingConfigSchema,
1113
+ IcpRubric: IcpRubricSchema,
1114
+ PipelineConfig: PipelineConfigSchema,
1115
+ PipelineStage: PipelineStageSchema,
1116
+ BuildPlanSnapshot: BuildPlanSnapshotSchema,
1117
+ BuildPlanSnapshotStep: BuildPlanSnapshotStepSchema,
1118
+ AcqListMetadata: AcqListMetadataSchema,
1119
+ ProcessingStageStatus: ProcessingStageStatusSchema,
1120
+ ListStageCounts: ListStageCountsSchema,
1121
+ ListTelemetry: ListTelemetrySchema,
1122
+
1123
+ // Requests
1124
+ CreateListRequest: CreateListRequestSchema,
1125
+ UpdateListRequest: UpdateListRequestSchema,
1126
+ UpdateListStatusRequest: UpdateListStatusRequestSchema,
1127
+ UpdateListConfigRequest: UpdateListConfigRequestSchema,
1128
+ AddCompaniesToListRequest: AddCompaniesToListRequestSchema,
1129
+ RemoveCompaniesFromListRequest: RemoveCompaniesFromListRequestSchema,
1130
+ AddContactsToListRequest: AddContactsToListRequestSchema,
1131
+ RecordListExecutionRequest: RecordListExecutionRequestSchema,
1132
+
1133
+ // Responses
1134
+ AcqListResponse: AcqListResponseSchema,
1135
+ AcqListListResponse: AcqListListResponseSchema,
1136
+ ListTelemetryResponse: ListTelemetryResponseSchema,
1137
+ ListTelemetryListResponse: ListTelemetryListResponseSchema,
1138
+ ListExecutionsResponse: ListExecutionsResponseSchema,
1139
+ ListProgressResponse: ListProgressResponseSchema
1140
+ }
1141
+
1142
+ // ---------------------------------------------------------------------------
1143
+ // Track A/B bundled schemas
1144
+ // ---------------------------------------------------------------------------
1145
+
1146
+ export const AcqSubstrateSchemas = {
1147
+ // Artifacts
1148
+ ListArtifactsQuery: ListArtifactsQuerySchema,
1149
+ CreateArtifactRequest: CreateArtifactRequestSchema,
1150
+ AcqArtifactResponse: AcqArtifactResponseSchema,
1151
+ AcqArtifactListResponse: AcqArtifactListResponseSchema,
1152
+
1153
+ // List members
1154
+ ListMembersQuery: ListMembersQuerySchema,
1155
+ MemberIdParams: MemberIdParamsSchema,
1156
+ AcqListMemberResponse: AcqListMemberResponseSchema,
1157
+ AcqListMembersResponse: AcqListMembersResponseSchema,
1158
+
1159
+ // List companies
1160
+ ListCompanyIdParams: ListCompanyIdParamsSchema,
1161
+ AcqListCompanyResponse: AcqListCompanyResponseSchema,
1162
+
1163
+ // Transition (shared with deals — TransitionItemRequestSchema)
1164
+ TransitionItemRequest: TransitionItemRequestSchema
1165
+ }
1166
+
1167
+ // ---------------------------------------------------------------------------
1168
+ // Inferred types
1169
+ // ---------------------------------------------------------------------------
1170
+
1171
+ // ---------------------------------------------------------------------------
1172
+ // Inferred types Track A/B substrate
1173
+ // ---------------------------------------------------------------------------
1174
+
1175
+ export type AcqArtifactOwnerKind = z.infer<typeof AcqArtifactOwnerKindSchema>
1176
+ export type ListArtifactsQuery = z.infer<typeof ListArtifactsQuerySchema>
1177
+ export type CreateArtifactRequest = z.infer<typeof CreateArtifactRequestSchema>
1178
+ export type AcqArtifactResponse = z.infer<typeof AcqArtifactResponseSchema>
1179
+ export type AcqArtifactListResponse = z.infer<typeof AcqArtifactListResponseSchema>
1180
+ export type ListMembersQuery = z.infer<typeof ListMembersQuerySchema>
1181
+ export type MemberIdParams = z.infer<typeof MemberIdParamsSchema>
1182
+ export type AcqListMemberContactSummary = z.infer<typeof AcqListMemberContactSummarySchema>
1183
+ export type AcqListMemberResponse = z.infer<typeof AcqListMemberResponseSchema>
1184
+ export type AcqListMembersResponse = z.infer<typeof AcqListMembersResponseSchema>
1185
+ export type ListCompanyIdParams = z.infer<typeof ListCompanyIdParamsSchema>
1186
+ export type AcqListCompanyResponse = z.infer<typeof AcqListCompanyResponseSchema>
1187
+
1188
+ // ---------------------------------------------------------------------------
1189
+
1190
+ export type ListStatus = z.infer<typeof ListStatusSchema>
1191
+ export type ScrapingConfig = z.infer<typeof ScrapingConfigSchema>
1192
+ export type IcpRubric = z.infer<typeof IcpRubricSchema>
1193
+ export type PipelineStage = z.infer<typeof PipelineStageSchema>
1194
+ export type PipelineConfig = z.infer<typeof PipelineConfigSchema>
1195
+ export type BuildPlanSnapshotStep = z.infer<typeof BuildPlanSnapshotStepSchema>
1196
+ export type BuildPlanSnapshot = z.infer<typeof BuildPlanSnapshotSchema>
1197
+ export type AcqListMetadata = z.infer<typeof AcqListMetadataSchema>
1198
+ export type ProcessingStageStatus = z.infer<typeof ProcessingStageStatusSchema>
1199
+ export type ListStageCountsInput = z.infer<typeof ListStageCountsSchema>['stageCounts']
1200
+ export type ListTelemetryInput = z.infer<typeof ListTelemetrySchema>
1201
+ export type ListIdParams = z.infer<typeof ListIdParamsSchema>
1202
+ export type CreateListRequest = z.infer<typeof CreateListRequestSchema>
1203
+ export type UpdateListRequest = z.infer<typeof UpdateListRequestSchema>
1204
+ export type UpdateListStatusRequest = z.infer<typeof UpdateListStatusRequestSchema>
1205
+ export type UpdateListConfigRequest = z.infer<typeof UpdateListConfigRequestSchema>
1206
+ export type AddCompaniesToListRequest = z.infer<typeof AddCompaniesToListRequestSchema>
1207
+ export type RemoveCompaniesFromListRequest = z.infer<typeof RemoveCompaniesFromListRequestSchema>
1208
+ export type AddContactsToListRequest = z.infer<typeof AddContactsToListRequestSchema>
1209
+ export type RecordListExecutionRequest = z.infer<typeof RecordListExecutionRequestSchema>
1210
+ export type AcqListResponse = z.infer<typeof AcqListResponseSchema>
1211
+ export type AcqListListResponse = z.infer<typeof AcqListListResponseSchema>
1212
+ export type ListTelemetryResponse = z.infer<typeof ListTelemetryResponseSchema>
1213
+ export type ListTelemetryListResponse = z.infer<typeof ListTelemetryListResponseSchema>
1214
+ export type ListExecutionSummaryInput = z.infer<typeof ListExecutionSummarySchema>
1215
+ export type ListExecutionsResponse = z.infer<typeof ListExecutionsResponseSchema>
1216
+ export type ListStageProgress = z.infer<typeof ListStageProgressSchema>
1217
+ export type ListProgress = z.infer<typeof ListProgressResponseSchema>