@elevasis/core 0.23.0 → 0.24.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (243) hide show
  1. package/dist/index.d.ts +4343 -2690
  2. package/dist/index.js +1101 -156
  3. package/dist/knowledge/index.d.ts +574 -210
  4. package/dist/knowledge/index.js +104 -1
  5. package/dist/organization-model/index.d.ts +4343 -2690
  6. package/dist/organization-model/index.js +1101 -156
  7. package/dist/test-utils/index.d.ts +483 -109
  8. package/dist/test-utils/index.js +904 -144
  9. package/package.json +3 -3
  10. package/src/README.md +14 -14
  11. package/src/__tests__/publish.test.ts +24 -24
  12. package/src/__tests__/template-core-compatibility.test.ts +9 -12
  13. package/src/_gen/__tests__/__snapshots__/contracts.md.snap +2137 -2093
  14. package/src/_gen/__tests__/scaffold-contracts.test.ts +30 -30
  15. package/src/auth/multi-tenancy/credentials/__tests__/encryption.test.ts +217 -217
  16. package/src/auth/multi-tenancy/credentials/server/encryption.ts +69 -69
  17. package/src/auth/multi-tenancy/credentials/server/kek-loader.ts +37 -37
  18. package/src/auth/multi-tenancy/index.ts +26 -26
  19. package/src/auth/multi-tenancy/invitations/api-schemas.ts +104 -104
  20. package/src/auth/multi-tenancy/memberships/api-schemas.ts +143 -143
  21. package/src/auth/multi-tenancy/memberships/index.ts +26 -26
  22. package/src/auth/multi-tenancy/memberships/membership.ts +130 -130
  23. package/src/auth/multi-tenancy/organizations/__tests__/api-schemas.test.ts +194 -194
  24. package/src/auth/multi-tenancy/organizations/api-schemas.ts +136 -136
  25. package/src/auth/multi-tenancy/permissions.test.ts +42 -42
  26. package/src/auth/multi-tenancy/permissions.ts +123 -123
  27. package/src/auth/multi-tenancy/role-management/api-schemas.ts +78 -78
  28. package/src/auth/multi-tenancy/role-management/index.ts +16 -16
  29. package/src/auth/multi-tenancy/theme-presets.ts +45 -45
  30. package/src/auth/multi-tenancy/types.ts +57 -57
  31. package/src/auth/multi-tenancy/users/api-schemas.ts +165 -165
  32. package/src/business/README.md +2 -2
  33. package/src/business/acquisition/activity-events.test.ts +250 -250
  34. package/src/business/acquisition/activity-events.ts +93 -93
  35. package/src/business/acquisition/api-schemas.test.ts +1883 -1843
  36. package/src/business/acquisition/api-schemas.ts +1492 -1497
  37. package/src/business/acquisition/build-templates.test.ts +240 -240
  38. package/src/business/acquisition/build-templates.ts +98 -98
  39. package/src/business/acquisition/crm-next-action.test.ts +262 -262
  40. package/src/business/acquisition/crm-next-action.ts +220 -220
  41. package/src/business/acquisition/crm-priority.test.ts +216 -216
  42. package/src/business/acquisition/crm-priority.ts +349 -349
  43. package/src/business/acquisition/crm-state-actions.test.ts +153 -153
  44. package/src/business/acquisition/deal-ownership.test.ts +351 -351
  45. package/src/business/acquisition/deal-ownership.ts +120 -120
  46. package/src/business/acquisition/derive-actions.test.ts +129 -104
  47. package/src/business/acquisition/derive-actions.ts +74 -84
  48. package/src/business/acquisition/index.ts +171 -170
  49. package/src/business/acquisition/ontology-validation.ts +309 -0
  50. package/src/business/acquisition/stateful.ts +30 -30
  51. package/src/business/acquisition/types.ts +396 -396
  52. package/src/business/clients/api-schemas.test.ts +115 -115
  53. package/src/business/clients/api-schemas.ts +158 -158
  54. package/src/business/clients/index.ts +1 -1
  55. package/src/business/crm/api-schemas.ts +40 -40
  56. package/src/business/crm/index.ts +1 -1
  57. package/src/business/deals/api-schemas.ts +87 -87
  58. package/src/business/deals/index.ts +1 -1
  59. package/src/business/index.ts +5 -5
  60. package/src/business/projects/types.ts +144 -144
  61. package/src/commands/queue/types/task.ts +15 -15
  62. package/src/execution/core/runner-types.ts +61 -61
  63. package/src/execution/core/sse-executions.ts +7 -7
  64. package/src/execution/engine/__tests__/fixtures/test-agents.ts +10 -10
  65. package/src/execution/engine/agent/core/__tests__/agent.test.ts +16 -16
  66. package/src/execution/engine/agent/core/__tests__/error-passthrough.test.ts +4 -4
  67. package/src/execution/engine/agent/core/types.ts +25 -25
  68. package/src/execution/engine/agent/index.ts +6 -6
  69. package/src/execution/engine/agent/reasoning/__tests__/request-builder.test.ts +24 -24
  70. package/src/execution/engine/index.ts +443 -443
  71. package/src/execution/engine/tools/integration/server/adapters/apify/__tests__/apify-run-actor.integration.test.ts +298 -298
  72. package/src/execution/engine/tools/integration/server/adapters/apify/apify-adapter.test.ts +55 -55
  73. package/src/execution/engine/tools/integration/server/adapters/apify/apify-adapter.ts +107 -107
  74. package/src/execution/engine/tools/integration/server/adapters/apollo/apollo-adapter.test.ts +48 -48
  75. package/src/execution/engine/tools/integration/server/adapters/apollo/apollo-adapter.ts +99 -99
  76. package/src/execution/engine/tools/integration/server/adapters/apollo/index.ts +1 -1
  77. package/src/execution/engine/tools/integration/server/adapters/attio/__tests__/attio-crud.integration.test.ts +363 -363
  78. package/src/execution/engine/tools/integration/server/adapters/attio/fetch/get-record/index.test.ts +162 -162
  79. package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-records/index.test.ts +316 -316
  80. package/src/execution/engine/tools/integration/server/adapters/clickup/clickup-adapter.test.ts +18 -18
  81. package/src/execution/engine/tools/integration/server/adapters/clickup/clickup-adapter.ts +194 -194
  82. package/src/execution/engine/tools/integration/server/adapters/clickup/index.ts +7 -7
  83. package/src/execution/engine/tools/integration/server/adapters/gmail/gmail-adapter.ts +204 -204
  84. package/src/execution/engine/tools/integration/server/adapters/gmail/gmail-tools.ts +105 -105
  85. package/src/execution/engine/tools/integration/server/adapters/google-calendar/google-calendar-adapter.ts +428 -428
  86. package/src/execution/engine/tools/integration/server/adapters/google-calendar/index.ts +2 -2
  87. package/src/execution/engine/tools/integration/server/adapters/google-sheets/__tests__/google-sheets.integration.test.ts +261 -261
  88. package/src/execution/engine/tools/integration/server/adapters/instantly/instantly-tools.ts +1474 -1474
  89. package/src/execution/engine/tools/integration/server/adapters/millionverifier/millionverifier-tools.ts +103 -103
  90. package/src/execution/engine/tools/integration/server/adapters/resend/fetch/send-email/index.test.ts +88 -88
  91. package/src/execution/engine/tools/integration/server/adapters/resend/fetch/send-email/index.ts +141 -141
  92. package/src/execution/engine/tools/integration/server/adapters/resend/fetch/utils/types.ts +76 -76
  93. package/src/execution/engine/tools/integration/server/adapters/signature-api/signature-api-tools.ts +182 -182
  94. package/src/execution/engine/tools/integration/server/adapters/stripe/stripe-tools.ts +310 -310
  95. package/src/execution/engine/tools/integration/service.test.ts +239 -239
  96. package/src/execution/engine/tools/integration/service.ts +172 -172
  97. package/src/execution/engine/tools/integration/tool.ts +255 -255
  98. package/src/execution/engine/tools/lead-service-types.ts +1005 -1005
  99. package/src/execution/engine/tools/messages.ts +43 -43
  100. package/src/execution/engine/tools/platform/acquisition/company-tools.ts +7 -7
  101. package/src/execution/engine/tools/platform/acquisition/contact-tools.ts +6 -6
  102. package/src/execution/engine/tools/platform/acquisition/list-tools.ts +6 -6
  103. package/src/execution/engine/tools/platform/acquisition/types.ts +280 -280
  104. package/src/execution/engine/tools/platform/email/types.ts +97 -97
  105. package/src/execution/engine/tools/registry.ts +704 -704
  106. package/src/execution/engine/tools/tool-maps.ts +831 -831
  107. package/src/execution/engine/tools/types.ts +234 -234
  108. package/src/execution/engine/workflow/types.ts +195 -197
  109. package/src/execution/external/__tests__/api-schemas.test.ts +127 -127
  110. package/src/execution/external/api-schemas.ts +40 -40
  111. package/src/execution/external/index.ts +1 -1
  112. package/src/index.ts +18 -18
  113. package/src/integrations/credentials/__tests__/api-schemas.test.ts +420 -420
  114. package/src/integrations/credentials/api-schemas.ts +146 -146
  115. package/src/integrations/credentials/schemas.ts +200 -200
  116. package/src/integrations/oauth/__tests__/provider-registry.test.ts +7 -7
  117. package/src/integrations/oauth/provider-registry.ts +74 -74
  118. package/src/integrations/oauth/server/credentials.ts +43 -43
  119. package/src/integrations/webhook-endpoints/__tests__/api-schemas.test.ts +327 -327
  120. package/src/integrations/webhook-endpoints/api-schemas.ts +103 -103
  121. package/src/integrations/webhook-endpoints/types.ts +58 -58
  122. package/src/knowledge/README.md +32 -32
  123. package/src/knowledge/__tests__/queries.test.ts +626 -535
  124. package/src/knowledge/format.ts +99 -99
  125. package/src/knowledge/index.ts +5 -5
  126. package/src/knowledge/published.ts +5 -5
  127. package/src/knowledge/queries.ts +269 -218
  128. package/src/operations/activities/api-schemas.ts +80 -80
  129. package/src/operations/activities/types.ts +64 -64
  130. package/src/organization-model/README.md +149 -149
  131. package/src/organization-model/__tests__/content-kinds-registry.test.ts +210 -210
  132. package/src/organization-model/__tests__/defaults.test.ts +168 -168
  133. package/src/organization-model/__tests__/domains/actions.test.ts +78 -56
  134. package/src/organization-model/__tests__/domains/customers.test.ts +299 -299
  135. package/src/organization-model/__tests__/domains/entities.test.ts +56 -56
  136. package/src/organization-model/__tests__/domains/goals.test.ts +493 -493
  137. package/src/organization-model/__tests__/domains/identity.test.ts +280 -280
  138. package/src/organization-model/__tests__/domains/navigation.test.ts +268 -268
  139. package/src/organization-model/__tests__/domains/offerings.test.ts +414 -414
  140. package/src/organization-model/__tests__/domains/policies.test.ts +323 -323
  141. package/src/organization-model/__tests__/domains/resource-mappings.test.ts +293 -293
  142. package/src/organization-model/__tests__/domains/resources.test.ts +387 -277
  143. package/src/organization-model/__tests__/domains/roles.test.ts +463 -463
  144. package/src/organization-model/__tests__/domains/statuses.test.ts +246 -246
  145. package/src/organization-model/__tests__/domains/systems.test.ts +209 -209
  146. package/src/organization-model/__tests__/domains/topology.test.ts +188 -0
  147. package/src/organization-model/__tests__/flatten-additive-merge.test.ts +362 -361
  148. package/src/organization-model/__tests__/foundation.test.ts +77 -77
  149. package/src/organization-model/__tests__/get-resources-for-system.test.ts +144 -144
  150. package/src/organization-model/__tests__/graph.test.ts +1312 -862
  151. package/src/organization-model/__tests__/icons.test.ts +10 -1
  152. package/src/organization-model/__tests__/knowledge.test.ts +251 -15
  153. package/src/organization-model/__tests__/lookup-helpers.test.ts +438 -438
  154. package/src/organization-model/__tests__/migration-helpers.test.ts +591 -591
  155. package/src/organization-model/__tests__/prospecting-ssot.test.ts +103 -103
  156. package/src/organization-model/__tests__/recursive-system-schema.test.ts +535 -506
  157. package/src/organization-model/__tests__/resolve.test.ts +274 -164
  158. package/src/organization-model/__tests__/schema.test.ts +844 -301
  159. package/src/organization-model/__tests__/surface-projection.test.ts +284 -284
  160. package/src/organization-model/catalogs/lead-gen.ts +144 -144
  161. package/src/organization-model/content-kinds/config.ts +36 -36
  162. package/src/organization-model/content-kinds/index.ts +76 -72
  163. package/src/organization-model/content-kinds/pipeline.ts +68 -68
  164. package/src/organization-model/content-kinds/registry.ts +44 -44
  165. package/src/organization-model/content-kinds/status.ts +71 -71
  166. package/src/organization-model/content-kinds/template.ts +83 -83
  167. package/src/organization-model/content-kinds/types.ts +117 -117
  168. package/src/organization-model/contracts.ts +27 -27
  169. package/src/organization-model/defaults.ts +42 -50
  170. package/src/organization-model/domains/actions.ts +333 -239
  171. package/src/organization-model/domains/customers.ts +78 -78
  172. package/src/organization-model/domains/entities.ts +144 -144
  173. package/src/organization-model/domains/goals.ts +83 -83
  174. package/src/organization-model/domains/knowledge.ts +117 -101
  175. package/src/organization-model/domains/navigation.ts +139 -139
  176. package/src/organization-model/domains/offerings.ts +71 -71
  177. package/src/organization-model/domains/policies.ts +102 -102
  178. package/src/organization-model/domains/projects.ts +14 -14
  179. package/src/organization-model/domains/prospecting.ts +395 -395
  180. package/src/organization-model/domains/resources.ts +202 -124
  181. package/src/organization-model/domains/roles.ts +96 -96
  182. package/src/organization-model/domains/sales.test.ts +218 -218
  183. package/src/organization-model/domains/sales.ts +380 -380
  184. package/src/organization-model/domains/shared.ts +63 -63
  185. package/src/organization-model/domains/statuses.ts +339 -339
  186. package/src/organization-model/domains/systems.ts +217 -172
  187. package/src/organization-model/domains/topology.ts +261 -0
  188. package/src/organization-model/foundation.ts +75 -75
  189. package/src/organization-model/graph/build.ts +1043 -867
  190. package/src/organization-model/graph/index.ts +4 -4
  191. package/src/organization-model/graph/link.ts +10 -10
  192. package/src/organization-model/graph/schema.ts +75 -68
  193. package/src/organization-model/graph/types.ts +71 -64
  194. package/src/organization-model/helpers.ts +289 -241
  195. package/src/organization-model/icons.ts +78 -66
  196. package/src/organization-model/index.ts +128 -125
  197. package/src/organization-model/migration-helpers.ts +247 -244
  198. package/src/organization-model/ontology.ts +658 -0
  199. package/src/organization-model/organization-graph.mdx +110 -90
  200. package/src/organization-model/organization-model.mdx +225 -213
  201. package/src/organization-model/published.ts +299 -222
  202. package/src/organization-model/resolve.ts +146 -91
  203. package/src/organization-model/schema.ts +818 -659
  204. package/src/organization-model/surface-projection.ts +212 -212
  205. package/src/organization-model/types.ts +179 -155
  206. package/src/platform/api/types.ts +38 -38
  207. package/src/platform/constants/versions.ts +3 -3
  208. package/src/platform/index.ts +23 -23
  209. package/src/platform/registry/__tests__/command-view.test.ts +10 -10
  210. package/src/platform/registry/__tests__/resource-link.test.ts +35 -35
  211. package/src/platform/registry/__tests__/resource-registry.integration.test.ts +20 -20
  212. package/src/platform/registry/__tests__/resource-registry.nested-systems.test.ts +245 -245
  213. package/src/platform/registry/__tests__/resource-registry.test.ts +2053 -2053
  214. package/src/platform/registry/__tests__/validation.test.ts +1444 -1259
  215. package/src/platform/registry/command-view.ts +10 -10
  216. package/src/platform/registry/index.ts +103 -103
  217. package/src/platform/registry/resource-link.ts +32 -32
  218. package/src/platform/registry/resource-registry.ts +886 -886
  219. package/src/platform/registry/serialization.ts +295 -295
  220. package/src/platform/registry/serialized-types.ts +166 -166
  221. package/src/platform/registry/stats-types.ts +68 -68
  222. package/src/platform/registry/types.ts +425 -425
  223. package/src/platform/registry/validation.ts +876 -684
  224. package/src/platform/utils/__tests__/validation.test.ts +1084 -1084
  225. package/src/platform/utils/validation.ts +425 -425
  226. package/src/projects/api-schemas.test.ts +39 -39
  227. package/src/projects/api-schemas.ts +291 -291
  228. package/src/reference/_generated/contracts.md +2136 -2093
  229. package/src/reference/glossary.md +76 -76
  230. package/src/scaffold-registry/__tests__/index.test.ts +206 -206
  231. package/src/scaffold-registry/__tests__/schema.test.ts +166 -166
  232. package/src/scaffold-registry/index.ts +392 -392
  233. package/src/scaffold-registry/schema.ts +243 -243
  234. package/src/server.ts +289 -289
  235. package/src/supabase/database.types.ts +3 -0
  236. package/src/test-utils/README.md +37 -37
  237. package/src/test-utils/entities.ts +108 -108
  238. package/src/test-utils/fixtures/memberships.ts +82 -82
  239. package/src/test-utils/index.ts +12 -12
  240. package/src/test-utils/organization-model.ts +65 -65
  241. package/src/test-utils/published.ts +6 -6
  242. package/src/test-utils/rls/RLSTestContext.ts +588 -588
  243. package/src/test-utils/test-utils.test.ts +44 -44
@@ -1,1521 +1,1516 @@
1
1
  import { z } from 'zod'
2
2
  import { UuidSchema, NonEmptyStringSchema } from '../../platform/utils/validation'
3
- import { CRM_PIPELINE_DEFINITION, LEAD_GEN_STAGE_CATALOG } from '../../organization-model/domains/sales'
4
- import {
5
- ACTION_REGISTRY,
6
- CredentialRequirementSchema,
7
- RecordColumnConfigSchema
8
- } from '../../organization-model/domains/prospecting'
3
+ import { CRM_PIPELINE_DEFINITION } from '../../organization-model/domains/sales'
4
+ import { CredentialRequirementSchema, RecordColumnConfigSchema } from '../../organization-model/domains/prospecting'
9
5
  import { isProspectingBuildTemplateId } from './build-templates'
6
+ import {
7
+ CRM_STAGE_KEYS_FROM_ONTOLOGY,
8
+ CRM_STATE_KEYS_FROM_ONTOLOGY,
9
+ isLeadGenActionKey,
10
+ isLeadGenRecordStageValidForEntity,
11
+ isLeadGenStageKey
12
+ } from './ontology-validation'
10
13
  export { CrmPriorityBucketKeySchema, CrmPriorityBucketOverrideSchema, CrmPriorityOverrideSchema } from './crm-priority'
11
14
  export type { CrmPriorityBucketOverride, CrmPriorityOverride, ResolvedCrmPriorityRuleConfig } from './crm-priority'
12
-
15
+
13
16
  export const ProcessingStageStatusSchema = z.enum(['success', 'no_result', 'skipped', 'error'])
14
17
 
15
18
  export const LeadGenStageKeySchema = z
16
19
  .string()
17
- .refine((value) => Object.prototype.hasOwnProperty.call(LEAD_GEN_STAGE_CATALOG, value), {
20
+ .refine((value) => isLeadGenStageKey(value), {
18
21
  message: 'processing state key must match LEAD_GEN_STAGE_CATALOG'
19
22
  })
20
23
 
21
- export const LeadGenActionKeySchema = z.string().refine((value) => ACTION_REGISTRY.some((c) => c.id === value), {
24
+ export const LeadGenActionKeySchema = z.string().refine((value) => isLeadGenActionKey(value), {
22
25
  message: 'actionKey must match ACTION_REGISTRY'
23
26
  })
24
27
 
25
- const crmStageKeys = CRM_PIPELINE_DEFINITION.stages.map((stage) => stage.stageKey) as [string, ...string[]]
26
- const crmStateKeys = CRM_PIPELINE_DEFINITION.stages.flatMap((stage) => stage.states.map((state) => state.stateKey)) as [
27
- string,
28
- ...string[]
29
- ]
30
-
31
- export const CrmStageKeySchema = z.enum(crmStageKeys)
32
- export const CrmStateKeySchema = z.enum(crmStateKeys)
33
-
34
- export const ProcessingStateEntrySchema = z
35
- .object({
36
- status: ProcessingStageStatusSchema,
37
- data: z.unknown().optional()
38
- })
39
- .passthrough()
40
-
41
- export const ProcessingStateSchema = z.record(LeadGenStageKeySchema, ProcessingStateEntrySchema)
42
- export const CompanyProcessingStateSchema = ProcessingStateSchema
43
- export const ContactProcessingStateSchema = ProcessingStateSchema
44
-
45
- /**
46
- * Deal Management API Schemas
47
- *
48
- * Request/response validation for /api/deals surface.
49
- * Used by both the API route handlers and the frontend hooks.
50
- *
51
- * Table mapping:
52
- * acq_deals -> DealSchemas (list/detail)
53
- * acq_deal_notes -> DealSchemas (note shapes)
54
- * acq_deal_tasks -> DealSchemas (task shapes)
55
- */
56
-
57
- // ---------------------------------------------------------------------------
58
- // Enum literals (must match DB CHECK constraints exactly)
59
- // ---------------------------------------------------------------------------
60
-
61
- export const DealStageSchema = CrmStageKeySchema
62
-
63
- export const AcqDealTaskKindSchema = z.enum(['call', 'email', 'meeting', 'other'])
64
-
65
- // ---------------------------------------------------------------------------
66
- // Params
67
- // ---------------------------------------------------------------------------
68
-
69
- export const DealIdParamsSchema = z.object({
70
- dealId: UuidSchema
71
- })
72
-
73
- export const DealTaskIdParamsSchema = z.object({
74
- dealId: UuidSchema,
75
- taskId: UuidSchema
76
- })
77
-
78
- // ---------------------------------------------------------------------------
79
- // Query schemas (coerce strings from query params)
80
- // ---------------------------------------------------------------------------
81
-
82
- export const ListDealsQuerySchema = z
83
- .object({
84
- stage: DealStageSchema.optional(),
85
- list: UuidSchema.optional(),
86
- batch: z.string().trim().min(1).max(255).optional(),
87
- staleSince: z.string().datetime().optional(),
88
- search: z.string().optional(),
89
- limit: z.coerce.number().int().positive().default(50),
90
- offset: z.coerce.number().int().min(0).default(0)
91
- })
92
- .strict()
93
-
94
- export const DealLookupQuerySchema = z
95
- .object({
96
- search: z.string().trim().min(1).max(200).optional(),
97
- limit: z.coerce.number().int().min(1).max(25).default(10)
98
- })
99
- .strict()
100
-
101
- export const ListDealTasksDueQuerySchema = z
102
- .object({
103
- window: z.enum(['overdue', 'today', 'today_and_overdue', 'upcoming']).optional(),
104
- assigneeUserId: UuidSchema.optional()
105
- })
106
- .strict()
107
-
108
- // ---------------------------------------------------------------------------
109
- // Request body schemas (all use .strict() — rejects unknown fields)
110
- // ---------------------------------------------------------------------------
111
-
112
- export const CreateDealNoteRequestSchema = z
113
- .object({
114
- body: z.string().trim().min(1).max(10000)
115
- })
116
- .strict()
117
-
118
- export const CreateDealTaskRequestSchema = z
119
- .object({
120
- title: z.string().trim().min(1).max(255),
121
- description: z.string().nullable().optional(),
122
- kind: AcqDealTaskKindSchema.optional(),
123
- dueAt: z.string().datetime().nullable().optional(),
124
- assigneeUserId: UuidSchema.nullable().optional()
125
- })
126
- .strict()
127
-
128
- export const TransitionItemRequestSchema = z
129
- .object({
130
- pipelineKey: z.string().min(1),
131
- stageKey: z.string().min(1),
132
- stateKey: z.string().min(1).nullable().optional(),
133
- reason: z.string().optional(),
134
- expectedUpdatedAt: z.string().datetime().optional()
135
- })
136
- .strict()
137
-
138
- export const CrmTransitionItemRequestSchema = z
139
- .object({
140
- pipelineKey: z.literal(CRM_PIPELINE_DEFINITION.pipelineKey),
141
- stageKey: CrmStageKeySchema,
142
- stateKey: CrmStateKeySchema.nullable().optional(),
143
- reason: z.string().optional(),
144
- expectedUpdatedAt: z.string().datetime().optional()
145
- })
146
- .strict()
147
-
148
- export const TransitionDealStateRequestSchema = z
149
- .object({
150
- stateKey: CrmStateKeySchema,
151
- reason: z.string().optional(),
152
- expectedUpdatedAt: z.string().datetime().optional()
153
- })
154
- .strict()
155
-
156
- export const ExecuteActionParamsSchema = z
157
- .object({
158
- dealId: UuidSchema,
159
- actionKey: NonEmptyStringSchema
160
- })
161
- .strict()
162
-
163
- export const ExecuteActionRequestSchema = z
164
- .object({
165
- payload: z.record(z.string(), z.unknown()).optional()
166
- })
167
- .strict()
168
-
169
- // ---------------------------------------------------------------------------
170
- // Response schemas (no .strict() — allows forward-compatible additions)
171
- // ---------------------------------------------------------------------------
172
-
173
- /**
174
- * Contact summary nested inside DealListItem / DealDetailResponse.
175
- * Matches the joined shape returned by useDeals / useDealDetail Supabase queries.
176
- */
177
- export const DealContactSummarySchema = z.object({
178
- id: z.string(),
179
- first_name: z.string().nullable(),
180
- last_name: z.string().nullable(),
181
- email: z.string(),
182
- title: z.string().nullable(),
183
- headline: z.string().nullable(),
184
- linkedin_url: z.string().nullable(),
185
- processing_state: ProcessingStateSchema.nullable(),
186
- enrichment_data: z.record(z.string(), z.unknown()).nullable(),
187
- company: z
188
- .object({
189
- id: z.string(),
190
- name: z.string(),
191
- domain: z.string().nullable(),
192
- website: z.string().nullable(),
193
- linkedin_url: z.string().nullable(),
194
- segment: z.string().nullable(),
195
- category: z.string().nullable(),
196
- num_employees: z.number().nullable()
197
- })
198
- .nullable()
199
- })
200
-
201
- export const DealPrioritySchema = z.object({
202
- bucketKey: z.enum(['needs_response', 'follow_up_due', 'waiting', 'stale', 'closed_low']),
203
- rank: z.number().int(),
204
- label: z.string(),
205
- color: z.string(),
206
- reason: z.string(),
207
- latestActivityAt: z.string().nullable(),
208
- nextActionAt: z.string().nullable()
209
- })
210
-
211
- /**
212
- * Deal list item with joined contact (and company via contact).
213
- * Matches DealListItem from @repo/core types.
214
- */
215
- export const DealListItemSchema = z.object({
216
- // acq_deals columns
217
- id: z.string(),
218
- organization_id: z.string(),
219
- client_id: z.string().nullable().optional(),
220
- contact_id: z.string().nullable(),
221
- contact_email: z.string(),
222
- pipeline_key: z.string(),
223
- stage_key: z.string().nullable(),
224
- state_key: z.string().nullable(),
225
- activity_log: z.unknown(),
226
- discovery_data: z.unknown().nullable(),
227
- discovery_submitted_at: z.string().nullable(),
228
- discovery_submitted_by: z.string().nullable(),
229
- proposal_data: z.unknown().nullable(),
230
- proposal_sent_at: z.string().nullable(),
231
- proposal_pdf_url: z.string().nullable(),
232
- signature_envelope_id: z.string().nullable(),
233
- source_list_id: z.string().nullable(),
234
- source_type: z.string().nullable(),
235
- initial_fee: z.number().nullable(),
236
- monthly_fee: z.number().nullable(),
237
- closed_lost_at: z.string().nullable(),
238
- closed_lost_reason: z.string().nullable(),
239
- created_at: z.string(),
240
- updated_at: z.string(),
241
- priority: DealPrioritySchema,
242
- ownership: z.enum(['us', 'them']).nullable(),
243
- nextAction: z.string().nullable(),
244
- // joined relation
245
- contact: DealContactSummarySchema.nullable()
246
- })
247
-
248
- export const DealListResponseSchema = z.object({
249
- data: z.array(DealListItemSchema),
250
- total: z.number().int(),
251
- limit: z.number().int(),
252
- offset: z.number().int()
253
- })
254
-
255
- export const DealStageSummarySchema = z.object({
256
- stage: z.string(),
257
- count: z.number().int(),
258
- totalValue: z.number(),
259
- oldestUpdatedAt: z.string().nullable(),
260
- newestUpdatedAt: z.string().nullable()
261
- })
262
-
263
- export const StaleDealSummarySchema = z.object({
264
- id: z.string(),
265
- contactEmail: z.string(),
266
- stageKey: z.string(),
267
- updatedAt: z.string(),
268
- daysStale: z.number().int()
269
- })
270
-
271
- export const DealSummaryResponseSchema = z.object({
272
- totalDeals: z.number().int(),
273
- openDeals: z.number().int(),
274
- wonDeals: z.number().int(),
275
- lostDeals: z.number().int(),
276
- winRate: z.number(),
277
- avgDealSize: z.number(),
278
- totalPipelineValue: z.number(),
279
- stageSummary: z.array(DealStageSummarySchema),
280
- staleDeals: z.array(StaleDealSummarySchema)
281
- })
282
-
283
- export const DealLookupItemSchema = z.object({
284
- id: z.string(),
285
- contactEmail: z.string(),
286
- stageKey: z.string().nullable(),
287
- updatedAt: z.string(),
288
- contactName: z.string().nullable(),
289
- companyName: z.string().nullable(),
290
- displayLabel: z.string()
291
- })
292
-
293
- export const DealLookupResponseSchema = z.array(DealLookupItemSchema)
294
-
295
- export const ConversationMessageSchema = z.object({
296
- id: z.string(),
297
- direction: z.enum(['inbound', 'outbound']),
298
- fromEmail: z.string(),
299
- toEmail: z.string(),
300
- subject: z.string().nullable(),
301
- body: z.string(),
302
- sentAt: z.string().nullable()
303
- })
304
-
305
- export const DealConversationSchema = z.object({
306
- messages: z.array(ConversationMessageSchema)
307
- })
308
-
309
- export const DealLineageListRefSchema = z.object({
310
- id: z.string(),
311
- name: z.string(),
312
- status: z.string()
313
- })
314
-
315
- export const DealLineageProjectRefSchema = z.object({
316
- id: z.string(),
317
- name: z.string(),
318
- kind: z.string(),
319
- status: z.string(),
320
- updatedAt: z.string()
321
- })
322
-
323
- export const DealLineageClientRefSchema = z.object({
324
- id: z.string(),
325
- name: z.string(),
326
- status: z.string()
327
- })
328
-
329
- export const DealLineageSchema = z.object({
330
- list: DealLineageListRefSchema.nullable(),
331
- projects: z.array(DealLineageProjectRefSchema),
332
- client: DealLineageClientRefSchema.nullable()
333
- })
334
-
335
- /**
336
- * Deal detail shape — currently the same as a list item (full joined record).
337
- * Additive fields keep existing DealListItem callers compatible.
338
- */
339
- export const DealDetailResponseSchema = DealListItemSchema.extend({
340
- conversation: DealConversationSchema,
341
- lineage: DealLineageSchema.optional()
342
- })
343
-
344
- /**
345
- * Single acq_deal_notes row (camelCase API representation).
346
- */
347
- export const DealNoteResponseSchema = z.object({
348
- id: z.string(),
349
- dealId: z.string(),
350
- organizationId: z.string(),
351
- authorUserId: z.string().nullable(),
352
- body: z.string(),
353
- createdAt: z.string(),
354
- updatedAt: z.string()
355
- })
356
-
357
- export const DealNoteListResponseSchema = z.array(DealNoteResponseSchema)
358
-
359
- /**
360
- * Single acq_deal_tasks row (camelCase API representation).
361
- * Matches AcqDealTask domain type from types.ts.
362
- */
363
- export const DealTaskResponseSchema = z.object({
364
- id: z.string(),
365
- organizationId: z.string(),
366
- dealId: z.string(),
367
- title: z.string(),
368
- description: z.string().nullable(),
369
- kind: AcqDealTaskKindSchema,
370
- dueAt: z.string().nullable(),
371
- assigneeUserId: z.string().nullable(),
372
- completedAt: z.string().nullable(),
373
- completedByUserId: z.string().nullable(),
374
- createdAt: z.string(),
375
- updatedAt: z.string(),
376
- createdByUserId: z.string().nullable()
377
- })
378
-
379
- export const DealTaskListResponseSchema = z.array(DealTaskResponseSchema)
380
-
381
- // ---------------------------------------------------------------------------
382
- // Bundled export
383
- // ---------------------------------------------------------------------------
384
-
385
- export const DealSchemas = {
386
- // Primitives
387
- CrmStageKey: CrmStageKeySchema,
388
- CrmStateKey: CrmStateKeySchema,
389
- DealStage: DealStageSchema,
390
-
391
- // Params
392
- DealIdParams: DealIdParamsSchema,
393
- DealTaskIdParams: DealTaskIdParamsSchema,
394
-
395
- // Queries
396
- ListDealsQuery: ListDealsQuerySchema,
397
- DealLookupQuery: DealLookupQuerySchema,
398
- ListDealTasksDueQuery: ListDealTasksDueQuerySchema,
399
-
400
- // Request bodies
401
- CreateDealNoteRequest: CreateDealNoteRequestSchema,
402
- CreateDealTaskRequest: CreateDealTaskRequestSchema,
403
- TransitionItemRequest: CrmTransitionItemRequestSchema,
404
- TransitionDealStateRequest: TransitionDealStateRequestSchema,
405
- ExecuteActionParams: ExecuteActionParamsSchema,
406
- ExecuteActionRequest: ExecuteActionRequestSchema,
407
-
408
- // Responses
409
- DealPriority: DealPrioritySchema,
410
- DealListResponse: DealListResponseSchema,
411
- DealSummaryResponse: DealSummaryResponseSchema,
412
- DealLookupResponse: DealLookupResponseSchema,
413
- ConversationMessage: ConversationMessageSchema,
414
- DealLineageListRef: DealLineageListRefSchema,
415
- DealLineageProjectRef: DealLineageProjectRefSchema,
416
- DealLineageClientRef: DealLineageClientRefSchema,
417
- DealLineage: DealLineageSchema,
418
- DealDetailResponse: DealDetailResponseSchema,
419
- DealNoteResponse: DealNoteResponseSchema,
420
- DealNoteListResponse: DealNoteListResponseSchema,
421
- DealTaskResponse: DealTaskResponseSchema,
422
- DealTaskListResponse: DealTaskListResponseSchema
423
- }
424
-
425
- // ---------------------------------------------------------------------------
426
- // Inferred types
427
- // ---------------------------------------------------------------------------
428
-
429
- export type DealStage = z.infer<typeof DealStageSchema>
430
- export type CrmStageKey = z.infer<typeof CrmStageKeySchema>
431
- export type CrmStateKey = z.infer<typeof CrmStateKeySchema>
432
- export type AcqDealTaskKind = z.infer<typeof AcqDealTaskKindSchema>
433
- export type DealIdParams = z.infer<typeof DealIdParamsSchema>
434
- export type DealTaskIdParams = z.infer<typeof DealTaskIdParamsSchema>
435
- export type ListDealsQuery = z.infer<typeof ListDealsQuerySchema>
436
- export type DealLookupQuery = z.infer<typeof DealLookupQuerySchema>
437
- export type ListDealTasksDueQuery = z.infer<typeof ListDealTasksDueQuerySchema>
438
- export type CreateDealNoteRequest = z.infer<typeof CreateDealNoteRequestSchema>
439
- export type CreateDealTaskRequest = z.infer<typeof CreateDealTaskRequestSchema>
440
- export type TransitionItemRequest = z.infer<typeof TransitionItemRequestSchema>
441
- export type CrmTransitionItemRequest = z.infer<typeof CrmTransitionItemRequestSchema>
442
- export type TransitionDealStateRequest = z.infer<typeof TransitionDealStateRequestSchema>
443
- export type ExecuteActionParams = z.infer<typeof ExecuteActionParamsSchema>
444
- export type ExecuteActionRequest = z.infer<typeof ExecuteActionRequestSchema>
445
- export type DealPriorityResponse = z.infer<typeof DealPrioritySchema>
446
- export type DealListResponse = z.infer<typeof DealListResponseSchema>
447
- export type DealSummaryResponse = z.infer<typeof DealSummaryResponseSchema>
448
- export type DealLookupItem = z.infer<typeof DealLookupItemSchema>
449
- export type DealLookupResponse = z.infer<typeof DealLookupResponseSchema>
450
- export type ConversationMessage = z.infer<typeof ConversationMessageSchema>
451
- export type DealLineageListRef = z.infer<typeof DealLineageListRefSchema>
452
- export type DealLineageProjectRef = z.infer<typeof DealLineageProjectRefSchema>
453
- export type DealLineageClientRef = z.infer<typeof DealLineageClientRefSchema>
454
- export type DealLineage = z.infer<typeof DealLineageSchema>
455
- export type DealDetailResponse = z.infer<typeof DealDetailResponseSchema>
456
- export type DealNoteResponse = z.infer<typeof DealNoteResponseSchema>
457
- export type DealNoteListResponse = z.infer<typeof DealNoteListResponseSchema>
458
- export type DealTaskResponse = z.infer<typeof DealTaskResponseSchema>
459
- export type DealTaskListResponse = z.infer<typeof DealTaskListResponseSchema>
460
-
461
- // ---------------------------------------------------------------------------
462
- // List Management API Schemas
463
- //
464
- // Request/response validation for /api/acquisition/lists surface.
465
- // Used by both the API route handlers and the frontend hooks.
466
- //
467
- // Table mapping:
468
- // acq_lists -> AcqListSchemas (list/detail/progress)
469
- // acq_list_companies -> AcqListSchemas (add/remove company membership)
470
- // acq_list_contacts -> AcqListSchemas (add/remove contact membership)
471
- // acq_list_executions -> AcqListSchemas (execution history)
472
- // ---------------------------------------------------------------------------
473
-
474
- // ---------------------------------------------------------------------------
475
- // Primitives — list status enum + jsonb config schemas
476
- // ---------------------------------------------------------------------------
477
-
478
- /**
479
- * Lifecycle status enum for `acq_lists.status` (mirrors DB CHECK constraint
480
- * from migration 20260428000003_lead_gen_acq_lists_status_and_config.sql).
481
- */
482
- export const ListStatusSchema = z.enum(['draft', 'enriching', 'launched', 'closing', 'archived'])
483
-
484
- /**
485
- * Scraping criteria stored in `acq_lists.scraping_config` jsonb.
486
- * Edited via the UI; consumed by lgn-01 prospecting workflows (Apify input shape,
487
- * geography, vertical, size). All fields are optional — empty config is valid.
488
- */
489
- export const ScrapingConfigSchema = z.object({
490
- vertical: z.string().trim().max(255).optional(),
491
- geography: z.string().trim().max(500).optional(),
492
- size: z.string().trim().max(255).optional(),
493
- apifyInput: z.record(z.string(), z.unknown()).optional()
494
- })
495
-
496
- /**
497
- * ICP / qualification rubric stored in `acq_lists.icp` jsonb.
498
- * Replaces the legacy `config.qualification` blob. Consumed by the
499
- * company-qualification workflow.
500
- */
501
- export const IcpRubricSchema = z.object({
502
- qualificationRubricKey: z.string().trim().max(255).nullish(),
503
- targetDescription: z.string().optional(),
504
- minReviewCount: z.number().int().min(0).optional(),
505
- minRating: z.number().min(0).max(5).optional(),
506
- excludeFranchises: z.boolean().optional(),
507
- customRules: z.string().optional()
508
- })
509
-
510
- /**
511
- * One stage entry in a list's `pipeline_config.stages[]`. The `key` is
512
- * validated against `LEAD_GEN_STAGE_CATALOG` so list pipeline definitions
513
- * stay aligned with the org-os semantic layer.
514
- */
28
+ const crmStageKeys = CRM_STAGE_KEYS_FROM_ONTOLOGY
29
+ const crmStateKeys = CRM_STATE_KEYS_FROM_ONTOLOGY
30
+
31
+ export const CrmStageKeySchema = z.enum(crmStageKeys)
32
+ export const CrmStateKeySchema = z.enum(crmStateKeys)
33
+
34
+ export const ProcessingStateEntrySchema = z
35
+ .object({
36
+ status: ProcessingStageStatusSchema,
37
+ data: z.unknown().optional()
38
+ })
39
+ .passthrough()
40
+
41
+ export const ProcessingStateSchema = z.record(LeadGenStageKeySchema, ProcessingStateEntrySchema)
42
+ export const CompanyProcessingStateSchema = ProcessingStateSchema
43
+ export const ContactProcessingStateSchema = ProcessingStateSchema
44
+
45
+ /**
46
+ * Deal Management API Schemas
47
+ *
48
+ * Request/response validation for /api/deals surface.
49
+ * Used by both the API route handlers and the frontend hooks.
50
+ *
51
+ * Table mapping:
52
+ * acq_deals -> DealSchemas (list/detail)
53
+ * acq_deal_notes -> DealSchemas (note shapes)
54
+ * acq_deal_tasks -> DealSchemas (task shapes)
55
+ */
56
+
57
+ // ---------------------------------------------------------------------------
58
+ // Enum literals (must match DB CHECK constraints exactly)
59
+ // ---------------------------------------------------------------------------
60
+
61
+ export const DealStageSchema = CrmStageKeySchema
62
+
63
+ export const AcqDealTaskKindSchema = z.enum(['call', 'email', 'meeting', 'other'])
64
+
65
+ // ---------------------------------------------------------------------------
66
+ // Params
67
+ // ---------------------------------------------------------------------------
68
+
69
+ export const DealIdParamsSchema = z.object({
70
+ dealId: UuidSchema
71
+ })
72
+
73
+ export const DealTaskIdParamsSchema = z.object({
74
+ dealId: UuidSchema,
75
+ taskId: UuidSchema
76
+ })
77
+
78
+ // ---------------------------------------------------------------------------
79
+ // Query schemas (coerce strings from query params)
80
+ // ---------------------------------------------------------------------------
81
+
82
+ export const ListDealsQuerySchema = z
83
+ .object({
84
+ stage: DealStageSchema.optional(),
85
+ list: UuidSchema.optional(),
86
+ batch: z.string().trim().min(1).max(255).optional(),
87
+ staleSince: z.string().datetime().optional(),
88
+ search: z.string().optional(),
89
+ limit: z.coerce.number().int().positive().default(50),
90
+ offset: z.coerce.number().int().min(0).default(0)
91
+ })
92
+ .strict()
93
+
94
+ export const DealLookupQuerySchema = z
95
+ .object({
96
+ search: z.string().trim().min(1).max(200).optional(),
97
+ limit: z.coerce.number().int().min(1).max(25).default(10)
98
+ })
99
+ .strict()
100
+
101
+ export const ListDealTasksDueQuerySchema = z
102
+ .object({
103
+ window: z.enum(['overdue', 'today', 'today_and_overdue', 'upcoming']).optional(),
104
+ assigneeUserId: UuidSchema.optional()
105
+ })
106
+ .strict()
107
+
108
+ // ---------------------------------------------------------------------------
109
+ // Request body schemas (all use .strict() — rejects unknown fields)
110
+ // ---------------------------------------------------------------------------
111
+
112
+ export const CreateDealNoteRequestSchema = z
113
+ .object({
114
+ body: z.string().trim().min(1).max(10000)
115
+ })
116
+ .strict()
117
+
118
+ export const CreateDealTaskRequestSchema = z
119
+ .object({
120
+ title: z.string().trim().min(1).max(255),
121
+ description: z.string().nullable().optional(),
122
+ kind: AcqDealTaskKindSchema.optional(),
123
+ dueAt: z.string().datetime().nullable().optional(),
124
+ assigneeUserId: UuidSchema.nullable().optional()
125
+ })
126
+ .strict()
127
+
128
+ export const TransitionItemRequestSchema = z
129
+ .object({
130
+ pipelineKey: z.string().min(1),
131
+ stageKey: z.string().min(1),
132
+ stateKey: z.string().min(1).nullable().optional(),
133
+ reason: z.string().optional(),
134
+ expectedUpdatedAt: z.string().datetime().optional()
135
+ })
136
+ .strict()
137
+
138
+ export const CrmTransitionItemRequestSchema = z
139
+ .object({
140
+ pipelineKey: z.literal(CRM_PIPELINE_DEFINITION.pipelineKey),
141
+ stageKey: CrmStageKeySchema,
142
+ stateKey: CrmStateKeySchema.nullable().optional(),
143
+ reason: z.string().optional(),
144
+ expectedUpdatedAt: z.string().datetime().optional()
145
+ })
146
+ .strict()
147
+
148
+ export const TransitionDealStateRequestSchema = z
149
+ .object({
150
+ stateKey: CrmStateKeySchema,
151
+ reason: z.string().optional(),
152
+ expectedUpdatedAt: z.string().datetime().optional()
153
+ })
154
+ .strict()
155
+
156
+ export const ExecuteActionParamsSchema = z
157
+ .object({
158
+ dealId: UuidSchema,
159
+ actionKey: NonEmptyStringSchema
160
+ })
161
+ .strict()
162
+
163
+ export const ExecuteActionRequestSchema = z
164
+ .object({
165
+ payload: z.record(z.string(), z.unknown()).optional()
166
+ })
167
+ .strict()
168
+
169
+ // ---------------------------------------------------------------------------
170
+ // Response schemas (no .strict() — allows forward-compatible additions)
171
+ // ---------------------------------------------------------------------------
172
+
173
+ /**
174
+ * Contact summary nested inside DealListItem / DealDetailResponse.
175
+ * Matches the joined shape returned by useDeals / useDealDetail Supabase queries.
176
+ */
177
+ export const DealContactSummarySchema = z.object({
178
+ id: z.string(),
179
+ first_name: z.string().nullable(),
180
+ last_name: z.string().nullable(),
181
+ email: z.string(),
182
+ title: z.string().nullable(),
183
+ headline: z.string().nullable(),
184
+ linkedin_url: z.string().nullable(),
185
+ processing_state: ProcessingStateSchema.nullable(),
186
+ enrichment_data: z.record(z.string(), z.unknown()).nullable(),
187
+ company: z
188
+ .object({
189
+ id: z.string(),
190
+ name: z.string(),
191
+ domain: z.string().nullable(),
192
+ website: z.string().nullable(),
193
+ linkedin_url: z.string().nullable(),
194
+ segment: z.string().nullable(),
195
+ category: z.string().nullable(),
196
+ num_employees: z.number().nullable()
197
+ })
198
+ .nullable()
199
+ })
200
+
201
+ export const DealPrioritySchema = z.object({
202
+ bucketKey: z.enum(['needs_response', 'follow_up_due', 'waiting', 'stale', 'closed_low']),
203
+ rank: z.number().int(),
204
+ label: z.string(),
205
+ color: z.string(),
206
+ reason: z.string(),
207
+ latestActivityAt: z.string().nullable(),
208
+ nextActionAt: z.string().nullable()
209
+ })
210
+
211
+ /**
212
+ * Deal list item with joined contact (and company via contact).
213
+ * Matches DealListItem from @repo/core types.
214
+ */
215
+ export const DealListItemSchema = z.object({
216
+ // acq_deals columns
217
+ id: z.string(),
218
+ organization_id: z.string(),
219
+ client_id: z.string().nullable().optional(),
220
+ contact_id: z.string().nullable(),
221
+ contact_email: z.string(),
222
+ pipeline_key: z.string(),
223
+ stage_key: z.string().nullable(),
224
+ state_key: z.string().nullable(),
225
+ activity_log: z.unknown(),
226
+ discovery_data: z.unknown().nullable(),
227
+ discovery_submitted_at: z.string().nullable(),
228
+ discovery_submitted_by: z.string().nullable(),
229
+ proposal_data: z.unknown().nullable(),
230
+ proposal_sent_at: z.string().nullable(),
231
+ proposal_pdf_url: z.string().nullable(),
232
+ signature_envelope_id: z.string().nullable(),
233
+ source_list_id: z.string().nullable(),
234
+ source_type: z.string().nullable(),
235
+ initial_fee: z.number().nullable(),
236
+ monthly_fee: z.number().nullable(),
237
+ closed_lost_at: z.string().nullable(),
238
+ closed_lost_reason: z.string().nullable(),
239
+ created_at: z.string(),
240
+ updated_at: z.string(),
241
+ priority: DealPrioritySchema,
242
+ ownership: z.enum(['us', 'them']).nullable(),
243
+ nextAction: z.string().nullable(),
244
+ // joined relation
245
+ contact: DealContactSummarySchema.nullable()
246
+ })
247
+
248
+ export const DealListResponseSchema = z.object({
249
+ data: z.array(DealListItemSchema),
250
+ total: z.number().int(),
251
+ limit: z.number().int(),
252
+ offset: z.number().int()
253
+ })
254
+
255
+ export const DealStageSummarySchema = z.object({
256
+ stage: z.string(),
257
+ count: z.number().int(),
258
+ totalValue: z.number(),
259
+ oldestUpdatedAt: z.string().nullable(),
260
+ newestUpdatedAt: z.string().nullable()
261
+ })
262
+
263
+ export const StaleDealSummarySchema = z.object({
264
+ id: z.string(),
265
+ contactEmail: z.string(),
266
+ stageKey: z.string(),
267
+ updatedAt: z.string(),
268
+ daysStale: z.number().int()
269
+ })
270
+
271
+ export const DealSummaryResponseSchema = z.object({
272
+ totalDeals: z.number().int(),
273
+ openDeals: z.number().int(),
274
+ wonDeals: z.number().int(),
275
+ lostDeals: z.number().int(),
276
+ winRate: z.number(),
277
+ avgDealSize: z.number(),
278
+ totalPipelineValue: z.number(),
279
+ stageSummary: z.array(DealStageSummarySchema),
280
+ staleDeals: z.array(StaleDealSummarySchema)
281
+ })
282
+
283
+ export const DealLookupItemSchema = z.object({
284
+ id: z.string(),
285
+ contactEmail: z.string(),
286
+ stageKey: z.string().nullable(),
287
+ updatedAt: z.string(),
288
+ contactName: z.string().nullable(),
289
+ companyName: z.string().nullable(),
290
+ displayLabel: z.string()
291
+ })
292
+
293
+ export const DealLookupResponseSchema = z.array(DealLookupItemSchema)
294
+
295
+ export const ConversationMessageSchema = z.object({
296
+ id: z.string(),
297
+ direction: z.enum(['inbound', 'outbound']),
298
+ fromEmail: z.string(),
299
+ toEmail: z.string(),
300
+ subject: z.string().nullable(),
301
+ body: z.string(),
302
+ sentAt: z.string().nullable()
303
+ })
304
+
305
+ export const DealConversationSchema = z.object({
306
+ messages: z.array(ConversationMessageSchema)
307
+ })
308
+
309
+ export const DealLineageListRefSchema = z.object({
310
+ id: z.string(),
311
+ name: z.string(),
312
+ status: z.string()
313
+ })
314
+
315
+ export const DealLineageProjectRefSchema = z.object({
316
+ id: z.string(),
317
+ name: z.string(),
318
+ kind: z.string(),
319
+ status: z.string(),
320
+ updatedAt: z.string()
321
+ })
322
+
323
+ export const DealLineageClientRefSchema = z.object({
324
+ id: z.string(),
325
+ name: z.string(),
326
+ status: z.string()
327
+ })
328
+
329
+ export const DealLineageSchema = z.object({
330
+ list: DealLineageListRefSchema.nullable(),
331
+ projects: z.array(DealLineageProjectRefSchema),
332
+ client: DealLineageClientRefSchema.nullable()
333
+ })
334
+
335
+ /**
336
+ * Deal detail shape — currently the same as a list item (full joined record).
337
+ * Additive fields keep existing DealListItem callers compatible.
338
+ */
339
+ export const DealDetailResponseSchema = DealListItemSchema.extend({
340
+ conversation: DealConversationSchema,
341
+ lineage: DealLineageSchema.optional()
342
+ })
343
+
344
+ /**
345
+ * Single acq_deal_notes row (camelCase API representation).
346
+ */
347
+ export const DealNoteResponseSchema = z.object({
348
+ id: z.string(),
349
+ dealId: z.string(),
350
+ organizationId: z.string(),
351
+ authorUserId: z.string().nullable(),
352
+ body: z.string(),
353
+ createdAt: z.string(),
354
+ updatedAt: z.string()
355
+ })
356
+
357
+ export const DealNoteListResponseSchema = z.array(DealNoteResponseSchema)
358
+
359
+ /**
360
+ * Single acq_deal_tasks row (camelCase API representation).
361
+ * Matches AcqDealTask domain type from types.ts.
362
+ */
363
+ export const DealTaskResponseSchema = z.object({
364
+ id: z.string(),
365
+ organizationId: z.string(),
366
+ dealId: z.string(),
367
+ title: z.string(),
368
+ description: z.string().nullable(),
369
+ kind: AcqDealTaskKindSchema,
370
+ dueAt: z.string().nullable(),
371
+ assigneeUserId: z.string().nullable(),
372
+ completedAt: z.string().nullable(),
373
+ completedByUserId: z.string().nullable(),
374
+ createdAt: z.string(),
375
+ updatedAt: z.string(),
376
+ createdByUserId: z.string().nullable()
377
+ })
378
+
379
+ export const DealTaskListResponseSchema = z.array(DealTaskResponseSchema)
380
+
381
+ // ---------------------------------------------------------------------------
382
+ // Bundled export
383
+ // ---------------------------------------------------------------------------
384
+
385
+ export const DealSchemas = {
386
+ // Primitives
387
+ CrmStageKey: CrmStageKeySchema,
388
+ CrmStateKey: CrmStateKeySchema,
389
+ DealStage: DealStageSchema,
390
+
391
+ // Params
392
+ DealIdParams: DealIdParamsSchema,
393
+ DealTaskIdParams: DealTaskIdParamsSchema,
394
+
395
+ // Queries
396
+ ListDealsQuery: ListDealsQuerySchema,
397
+ DealLookupQuery: DealLookupQuerySchema,
398
+ ListDealTasksDueQuery: ListDealTasksDueQuerySchema,
399
+
400
+ // Request bodies
401
+ CreateDealNoteRequest: CreateDealNoteRequestSchema,
402
+ CreateDealTaskRequest: CreateDealTaskRequestSchema,
403
+ TransitionItemRequest: CrmTransitionItemRequestSchema,
404
+ TransitionDealStateRequest: TransitionDealStateRequestSchema,
405
+ ExecuteActionParams: ExecuteActionParamsSchema,
406
+ ExecuteActionRequest: ExecuteActionRequestSchema,
407
+
408
+ // Responses
409
+ DealPriority: DealPrioritySchema,
410
+ DealListResponse: DealListResponseSchema,
411
+ DealSummaryResponse: DealSummaryResponseSchema,
412
+ DealLookupResponse: DealLookupResponseSchema,
413
+ ConversationMessage: ConversationMessageSchema,
414
+ DealLineageListRef: DealLineageListRefSchema,
415
+ DealLineageProjectRef: DealLineageProjectRefSchema,
416
+ DealLineageClientRef: DealLineageClientRefSchema,
417
+ DealLineage: DealLineageSchema,
418
+ DealDetailResponse: DealDetailResponseSchema,
419
+ DealNoteResponse: DealNoteResponseSchema,
420
+ DealNoteListResponse: DealNoteListResponseSchema,
421
+ DealTaskResponse: DealTaskResponseSchema,
422
+ DealTaskListResponse: DealTaskListResponseSchema
423
+ }
424
+
425
+ // ---------------------------------------------------------------------------
426
+ // Inferred types
427
+ // ---------------------------------------------------------------------------
428
+
429
+ export type DealStage = z.infer<typeof DealStageSchema>
430
+ export type CrmStageKey = z.infer<typeof CrmStageKeySchema>
431
+ export type CrmStateKey = z.infer<typeof CrmStateKeySchema>
432
+ export type AcqDealTaskKind = z.infer<typeof AcqDealTaskKindSchema>
433
+ export type DealIdParams = z.infer<typeof DealIdParamsSchema>
434
+ export type DealTaskIdParams = z.infer<typeof DealTaskIdParamsSchema>
435
+ export type ListDealsQuery = z.infer<typeof ListDealsQuerySchema>
436
+ export type DealLookupQuery = z.infer<typeof DealLookupQuerySchema>
437
+ export type ListDealTasksDueQuery = z.infer<typeof ListDealTasksDueQuerySchema>
438
+ export type CreateDealNoteRequest = z.infer<typeof CreateDealNoteRequestSchema>
439
+ export type CreateDealTaskRequest = z.infer<typeof CreateDealTaskRequestSchema>
440
+ export type TransitionItemRequest = z.infer<typeof TransitionItemRequestSchema>
441
+ export type CrmTransitionItemRequest = z.infer<typeof CrmTransitionItemRequestSchema>
442
+ export type TransitionDealStateRequest = z.infer<typeof TransitionDealStateRequestSchema>
443
+ export type ExecuteActionParams = z.infer<typeof ExecuteActionParamsSchema>
444
+ export type ExecuteActionRequest = z.infer<typeof ExecuteActionRequestSchema>
445
+ export type DealPriorityResponse = z.infer<typeof DealPrioritySchema>
446
+ export type DealListResponse = z.infer<typeof DealListResponseSchema>
447
+ export type DealSummaryResponse = z.infer<typeof DealSummaryResponseSchema>
448
+ export type DealLookupItem = z.infer<typeof DealLookupItemSchema>
449
+ export type DealLookupResponse = z.infer<typeof DealLookupResponseSchema>
450
+ export type ConversationMessage = z.infer<typeof ConversationMessageSchema>
451
+ export type DealLineageListRef = z.infer<typeof DealLineageListRefSchema>
452
+ export type DealLineageProjectRef = z.infer<typeof DealLineageProjectRefSchema>
453
+ export type DealLineageClientRef = z.infer<typeof DealLineageClientRefSchema>
454
+ export type DealLineage = z.infer<typeof DealLineageSchema>
455
+ export type DealDetailResponse = z.infer<typeof DealDetailResponseSchema>
456
+ export type DealNoteResponse = z.infer<typeof DealNoteResponseSchema>
457
+ export type DealNoteListResponse = z.infer<typeof DealNoteListResponseSchema>
458
+ export type DealTaskResponse = z.infer<typeof DealTaskResponseSchema>
459
+ export type DealTaskListResponse = z.infer<typeof DealTaskListResponseSchema>
460
+
461
+ // ---------------------------------------------------------------------------
462
+ // List Management API Schemas
463
+ //
464
+ // Request/response validation for /api/acquisition/lists surface.
465
+ // Used by both the API route handlers and the frontend hooks.
466
+ //
467
+ // Table mapping:
468
+ // acq_lists -> AcqListSchemas (list/detail/progress)
469
+ // acq_list_companies -> AcqListSchemas (add/remove company membership)
470
+ // acq_list_contacts -> AcqListSchemas (add/remove contact membership)
471
+ // acq_list_executions -> AcqListSchemas (execution history)
472
+ // ---------------------------------------------------------------------------
473
+
474
+ // ---------------------------------------------------------------------------
475
+ // Primitives — list status enum + jsonb config schemas
476
+ // ---------------------------------------------------------------------------
477
+
478
+ /**
479
+ * Lifecycle status enum for `acq_lists.status` (mirrors DB CHECK constraint
480
+ * from migration 20260428000003_lead_gen_acq_lists_status_and_config.sql).
481
+ */
482
+ export const ListStatusSchema = z.enum(['draft', 'enriching', 'launched', 'closing', 'archived'])
483
+
484
+ /**
485
+ * Scraping criteria stored in `acq_lists.scraping_config` jsonb.
486
+ * Edited via the UI; consumed by lgn-01 prospecting workflows (Apify input shape,
487
+ * geography, vertical, size). All fields are optional — empty config is valid.
488
+ */
489
+ export const ScrapingConfigSchema = z.object({
490
+ vertical: z.string().trim().max(255).optional(),
491
+ geography: z.string().trim().max(500).optional(),
492
+ size: z.string().trim().max(255).optional(),
493
+ apifyInput: z.record(z.string(), z.unknown()).optional()
494
+ })
495
+
496
+ /**
497
+ * ICP / qualification rubric stored in `acq_lists.icp` jsonb.
498
+ * Replaces the legacy `config.qualification` blob. Consumed by the
499
+ * company-qualification workflow.
500
+ */
501
+ export const IcpRubricSchema = z.object({
502
+ qualificationRubricKey: z.string().trim().max(255).nullish(),
503
+ targetDescription: z.string().optional(),
504
+ minReviewCount: z.number().int().min(0).optional(),
505
+ minRating: z.number().min(0).max(5).optional(),
506
+ excludeFranchises: z.boolean().optional(),
507
+ customRules: z.string().optional()
508
+ })
509
+
510
+ /**
511
+ * One stage entry in a list's `pipeline_config.stages[]`. The `key` is
512
+ * validated against `LEAD_GEN_STAGE_CATALOG` so list pipeline definitions
513
+ * stay aligned with the org-os semantic layer.
514
+ */
515
515
  export const PipelineStageSchema = z.object({
516
- key: z.string().refine((value) => Object.prototype.hasOwnProperty.call(LEAD_GEN_STAGE_CATALOG, value), {
516
+ key: z.string().refine((value) => isLeadGenStageKey(value), {
517
517
  message: 'pipeline stage key must match LEAD_GEN_STAGE_CATALOG'
518
518
  }),
519
- label: z.string().optional(),
520
- enabled: z.boolean().optional(),
521
- order: z.number().int().optional()
522
- })
523
-
524
- /**
525
- * Pipeline presentation contract stored in `acq_lists.pipeline_config` jsonb.
526
- * `stages[].key` validates against the catalog; the rest is presentation only.
527
- */
528
- export const PipelineConfigSchema = z.object({
529
- stages: z.array(PipelineStageSchema).optional()
530
- })
531
-
532
- export const BuildPlanSnapshotStepSchema = z
533
- .object({
534
- id: z.string().trim().min(1).max(100),
535
- label: z.string().trim().min(1).max(120),
536
- description: z.string().trim().min(1).max(2000).optional(),
537
- primaryEntity: z.enum(['company', 'contact']),
538
- outputs: z.array(z.enum(['company', 'contact', 'export'])).min(1),
539
- stageKey: LeadGenStageKeySchema,
540
- recordEntity: z.enum(['company', 'contact']).optional(),
541
- recordsStageKey: LeadGenStageKeySchema.optional(),
542
- recordSourceStageKey: LeadGenStageKeySchema.optional(),
543
- dependsOn: z.array(z.string().trim().min(1).max(100)).optional(),
544
- dependencyMode: z.literal('per-record-eligibility'),
545
- actionKey: LeadGenActionKeySchema,
546
- defaultBatchSize: z.number().int().positive(),
547
- maxBatchSize: z.number().int().positive(),
548
- recordColumns: z
549
- .object({
550
- company: z.array(RecordColumnConfigSchema).optional(),
551
- contact: z.array(RecordColumnConfigSchema).optional()
552
- })
553
- .optional(),
554
- credentialRequirements: z.array(CredentialRequirementSchema).optional()
555
- })
556
- .refine((step) => step.defaultBatchSize <= step.maxBatchSize, {
557
- message: 'defaultBatchSize must be less than or equal to maxBatchSize',
558
- path: ['defaultBatchSize']
559
- })
560
-
561
- export const BuildPlanSnapshotSchema = z
562
- .object({
563
- templateId: z.string().trim().min(1).max(100),
564
- templateLabel: z.string().trim().min(1).max(120),
565
- steps: z.array(BuildPlanSnapshotStepSchema).min(1)
566
- })
567
- .superRefine((snapshot, ctx) => {
568
- const stepIds = new Set<string>()
569
-
570
- snapshot.steps.forEach((step, index) => {
571
- if (stepIds.has(step.id)) {
572
- ctx.addIssue({
573
- code: z.ZodIssueCode.custom,
574
- message: `duplicate build-plan step id "${step.id}"`,
575
- path: ['steps', index, 'id']
576
- })
577
- }
578
- stepIds.add(step.id)
579
- })
580
-
581
- snapshot.steps.forEach((step, index) => {
582
- for (const dependencyId of step.dependsOn ?? []) {
583
- if (!stepIds.has(dependencyId)) {
584
- ctx.addIssue({
585
- code: z.ZodIssueCode.custom,
586
- message: `dependsOn references unknown build-plan step "${dependencyId}"`,
587
- path: ['steps', index, 'dependsOn']
588
- })
589
- }
590
- }
591
- })
592
- })
593
-
594
- export const AcqListMetadataSchema = z
595
- .object({
596
- buildPlanSnapshot: BuildPlanSnapshotSchema.optional()
597
- })
598
- .catchall(z.unknown())
599
-
600
- export const ProspectingBuildTemplateIdSchema = z.string().trim().min(1).max(100).refine(isProspectingBuildTemplateId, {
601
- message: 'buildTemplateId must match a known prospecting build template'
602
- })
603
-
604
- // ---------------------------------------------------------------------------
605
- // List telemetry / progress schemas
606
- // ---------------------------------------------------------------------------
607
-
608
- export const ListStageCountsSchema = z.object({
609
- // Attempted counts by canonical lead-gen stage. The detailed status
610
- // distribution lives on ListProgress; telemetry keeps the overview payload small.
611
- stageCounts: z.object({
612
- populated: z.number().int(),
613
- extracted: z.number().int(),
614
- qualified: z.number().int(),
615
- discovered: z.number().int(),
616
- verified: z.number().int(),
617
- personalized: z.number().int(),
618
- uploaded: z.number().int()
619
- }),
620
- deliverability: z.object({
621
- valid: z.number().int(),
622
- risky: z.number().int(),
623
- invalid: z.number().int(),
624
- unknown: z.number().int(),
625
- bounced: z.number().int()
626
- })
627
- })
628
-
629
- export const ListTelemetrySchema = z.object({
630
- listId: UuidSchema,
631
- totalCompanies: z.number().int(),
632
- totalContacts: z.number().int(),
633
- stageCounts: ListStageCountsSchema.shape.stageCounts,
634
- deliverability: ListStageCountsSchema.shape.deliverability,
635
- activeWorkflows: z.array(z.string()).optional()
636
- })
637
-
638
- // ---------------------------------------------------------------------------
639
- // Params
640
- // ---------------------------------------------------------------------------
641
-
642
- export const ListIdParamsSchema = z.object({
643
- listId: UuidSchema
644
- })
645
-
646
- // ---------------------------------------------------------------------------
647
- // Request body schemas (all use .strict() — rejects unknown fields)
648
- // ---------------------------------------------------------------------------
649
-
650
- export const CreateListRequestSchema = z
651
- .object({
652
- name: z.string().trim().min(1).max(255),
653
- description: z.string().trim().nullable().optional(),
654
- status: ListStatusSchema.optional(),
655
- buildTemplateId: ProspectingBuildTemplateIdSchema.optional(),
656
- scrapingConfig: ScrapingConfigSchema.optional(),
657
- icp: IcpRubricSchema.optional(),
658
- pipelineConfig: PipelineConfigSchema.optional()
659
- })
660
- .strict()
661
-
662
- export const UpdateListRequestSchema = z
663
- .object({
664
- name: z.string().trim().min(1).max(255).optional(),
665
- description: z.string().trim().nullable().optional(),
666
- batchIds: z.array(z.string()).optional(),
667
- buildTemplateId: ProspectingBuildTemplateIdSchema.optional(),
668
- confirmBuildTemplateChange: z.literal(true).optional()
669
- })
670
- .strict()
671
- .refine(
672
- (data) =>
673
- data.name !== undefined ||
674
- data.description !== undefined ||
675
- data.batchIds !== undefined ||
676
- data.buildTemplateId !== undefined,
677
- {
678
- message: 'At least one field (name, description, batchIds, or buildTemplateId) must be provided'
679
- }
680
- )
681
- .refine((data) => data.buildTemplateId === undefined || data.confirmBuildTemplateChange === true, {
682
- message: 'confirmBuildTemplateChange must be true when changing buildTemplateId',
683
- path: ['confirmBuildTemplateChange']
684
- })
685
-
686
- /**
687
- * Status-only PATCH body for `/acquisition/lists/:listId/status`.
688
- * Replaces the previous `transitionList` flow.
689
- */
690
- export const UpdateListStatusRequestSchema = z
691
- .object({
692
- status: ListStatusSchema
693
- })
694
- .strict()
695
-
696
- /**
697
- * Partial patch for the three jsonb config columns. UI sends only the edited
698
- * subtree; server writes the field as-is (no deep merge — each column is
699
- * replaced atomically when present in the patch).
700
- */
701
- export const UpdateListConfigRequestSchema = z
702
- .object({
703
- scrapingConfig: ScrapingConfigSchema.partial().optional(),
704
- icp: IcpRubricSchema.partial().optional(),
705
- pipelineConfig: PipelineConfigSchema.partial().optional()
706
- })
707
- .strict()
708
- .refine((data) => data.scrapingConfig !== undefined || data.icp !== undefined || data.pipelineConfig !== undefined, {
709
- message: 'At least one of scrapingConfig, icp, or pipelineConfig must be provided'
710
- })
711
-
712
- export const AddCompaniesToListRequestSchema = z
713
- .object({
714
- companyIds: z.array(UuidSchema).min(1).max(1000)
715
- })
716
- .strict()
717
-
718
- export const RemoveCompaniesFromListRequestSchema = z
719
- .object({
720
- companyIds: z.array(UuidSchema).min(1).max(1000)
721
- })
722
- .strict()
723
-
724
- export const AddContactsToListRequestSchema = z
725
- .object({
726
- contactIds: z.array(UuidSchema).min(1).max(1000)
727
- })
728
- .strict()
729
-
730
- export const RecordListExecutionRequestSchema = z
731
- .object({
732
- executionId: UuidSchema,
733
- configSnapshot: z.record(z.string(), z.unknown()).optional()
734
- })
735
- .strict()
736
-
737
- // ---------------------------------------------------------------------------
738
- // Response schemas (no .strict() — allows forward-compatible additions)
739
- // ---------------------------------------------------------------------------
740
-
741
- /**
742
- * Single list as returned by /api/acquisition/lists/:id etc.
743
- * Camel-cased domain shape matching AcqList in types.ts.
744
- */
745
- export const AcqListResponseSchema = z.object({
746
- id: z.string(),
747
- organizationId: z.string(),
748
- name: z.string(),
749
- description: z.string().nullable(),
750
- batchIds: z.array(z.string()),
751
- instantlyCampaignId: z.string().nullable(),
752
- /** Lifecycle status (draft | enriching | launched | closing | archived). */
753
- status: ListStatusSchema,
754
- metadata: AcqListMetadataSchema,
755
- launchedAt: z.string().nullable(),
756
- completedAt: z.string().nullable(),
757
- createdAt: z.string(),
758
- /** Scraping criteria stored as jsonb on the row. */
759
- scrapingConfig: ScrapingConfigSchema,
760
- /** ICP / qualification rubric stored as jsonb on the row. */
761
- icp: IcpRubricSchema,
762
- /** Pipeline presentation contract stored as jsonb on the row. */
763
- pipelineConfig: PipelineConfigSchema
764
- })
765
-
766
- export const AcqListListResponseSchema = z.array(AcqListResponseSchema)
767
-
768
- export const ListTelemetryResponseSchema = ListTelemetrySchema
769
-
770
- export const ListTelemetryListResponseSchema = z.array(ListTelemetrySchema)
771
-
772
- const QueryBooleanSchema = z.preprocess((value) => {
773
- if (value === 'true' || value === '1' || value === true) return true
774
- if (value === 'false' || value === '0' || value === false) return false
775
- return value
776
- }, z.boolean())
777
-
778
- export const ListReadQuerySchema = z
779
- .object({
780
- status: ListStatusSchema.optional(),
781
- batch: z.string().trim().min(1).max(255).optional(),
782
- vertical: z.string().trim().min(1).max(255).optional(),
783
- limit: z.coerce.number().int().min(1).max(500).optional(),
784
- offset: z.coerce.number().int().min(0).optional()
785
- })
786
- .strict()
787
-
788
- export const GetListQuerySchema = z
789
- .object({
790
- includeDeals: QueryBooleanSchema.default(true),
791
- includeProgress: QueryBooleanSchema.default(false),
792
- dealLimit: z.coerce.number().int().min(0).max(100).default(25)
793
- })
794
- .strict()
795
-
796
- /**
797
- * Per-stage progress aggregate for a single pipeline stage.
798
- * `attempted` counts terminal statuses, including success, no-result, skipped,
799
- * error, and tolerant-reader `other` values.
800
- * `total` = total member/company count for the list.
801
- */
802
- export const ListStageProgressSchema = z.object({
803
- total: z.number().int().min(0),
804
- attempted: z.number().int().min(0),
805
- success: z.number().int().min(0),
806
- noResult: z.number().int().min(0),
807
- skipped: z.number().int().min(0),
808
- error: z.number().int().min(0),
809
- other: z.number().int().min(0),
810
- notAttempted: z.number().int().min(0)
811
- })
812
-
813
- /**
814
- * Progress response for GET /acquisition/lists/:listId/progress.
815
- * Aggregated on-demand from processing_state status values.
816
- * Stage keys are discovered from observed processing_state keys.
817
- */
818
- export const ListProgressResponseSchema = z.object({
819
- totalMembers: z.number().int().min(0),
820
- totalCompanies: z.number().int().min(0),
821
- byCompanyStage: z.record(z.string(), ListStageProgressSchema),
822
- byContactStage: z.record(z.string(), ListStageProgressSchema)
823
- })
824
-
825
- export const AcqListDealRefSchema = z.object({
826
- id: z.string(),
827
- contactEmail: z.string(),
828
- stageKey: z.string().nullable(),
829
- stateKey: z.string().nullable(),
830
- sourceType: z.string().nullable(),
831
- lastActivityAt: z.string()
832
- })
833
-
834
- export const AcqListLineageSchema = z.object({
835
- deals: z.object({
836
- total: z.number().int().min(0),
837
- refs: z.array(AcqListDealRefSchema),
838
- truncated: z.boolean()
839
- })
840
- })
841
-
842
- export const AcqListDetailResponseSchema = AcqListResponseSchema.extend({
843
- lineage: AcqListLineageSchema.optional(),
844
- progress: ListProgressResponseSchema.optional()
845
- })
846
-
847
- export const AcqListStatusListItemSchema = z.object({
848
- listId: z.string(),
849
- name: z.string(),
850
- status: ListStatusSchema,
851
- totalCompanies: z.number().int().min(0),
852
- totalContacts: z.number().int().min(0),
853
- totalDeals: z.number().int().min(0),
854
- createdAt: z.string()
855
- })
856
-
857
- export const AcqListStatusResponseSchema = z.object({
858
- totalLists: z.number().int().min(0),
859
- totalCompanies: z.number().int().min(0),
860
- totalContacts: z.number().int().min(0),
861
- totalDeals: z.number().int().min(0),
862
- byStatus: z.record(z.string(), z.number().int().min(0)),
863
- lists: z.array(AcqListStatusListItemSchema)
864
- })
865
-
866
- /**
867
- * Row from acq_list_executions joined with the execution summary,
868
- * shaped for the /lists/:id/executions response.
869
- */
870
- export const ListExecutionSummarySchema = z.object({
871
- executionId: z.string(),
872
- resourceId: z.string(),
873
- status: z.string(),
874
- createdAt: z.string(),
875
- completedAt: z.string().nullable(),
876
- durationMs: z.number().int().nullable(),
877
- input: z.unknown().nullable().optional()
878
- })
879
-
880
- export const ListExecutionsResponseSchema = z.array(ListExecutionSummarySchema)
881
-
882
- // ---------------------------------------------------------------------------
883
- // Company / Contact API Schemas
884
- // ---------------------------------------------------------------------------
885
-
886
- export const AcqCompanyStatusSchema = z.enum(['active', 'invalid'])
887
- export const AcqContactStatusSchema = z.enum(['active', 'invalid'])
888
- export const AcqEmailValidSchema = z.enum(['VALID', 'INVALID', 'RISKY', 'UNKNOWN'])
889
-
890
- export const CompanyIdParamsSchema = z.object({
891
- companyId: UuidSchema
892
- })
893
-
894
- export const ContactIdParamsSchema = z.object({
895
- contactId: UuidSchema
896
- })
897
-
898
- export const ListCompaniesQuerySchema = z
899
- .object({
900
- search: z.string().trim().min(1).max(200).optional(),
901
- listId: UuidSchema.optional(),
902
- domain: z.string().trim().min(1).max(255).optional(),
903
- website: z.string().trim().min(1).max(2048).optional(),
904
- segment: z.string().trim().min(1).max(255).optional(),
905
- category: z.string().trim().min(1).max(255).optional(),
906
- pipelineStatus: z.unknown().optional(),
907
- batchId: z.string().trim().min(1).max(255).optional(),
908
- status: AcqCompanyStatusSchema.optional(),
909
- includeAll: QueryBooleanSchema.optional(),
910
- limit: z.coerce.number().int().min(1).max(5000).default(50),
911
- offset: z.coerce.number().int().min(0).default(0)
912
- })
913
- .strict()
914
-
915
- export const ListContactsQuerySchema = z
916
- .object({
917
- search: z.string().trim().min(1).max(200).optional(),
918
- listId: UuidSchema.optional(),
919
- openingLineIsNull: QueryBooleanSchema.optional(),
920
- batchId: z.string().trim().min(1).max(255).optional(),
921
- contactStatus: AcqContactStatusSchema.optional(),
922
- limit: z.coerce.number().int().min(1).max(5000).default(5000),
923
- offset: z.coerce.number().int().min(0).default(0)
924
- })
925
- .strict()
926
-
927
- export const CreateCompanyRequestSchema = z
928
- .object({
929
- name: z.string().trim().min(1).max(255),
930
- clientId: UuidSchema.nullable().optional(),
931
- domain: z.string().trim().min(1).max(255).optional(),
932
- linkedinUrl: z.string().trim().url().optional(),
933
- website: z.string().trim().url().optional(),
934
- numEmployees: z.number().int().min(0).optional(),
935
- foundedYear: z.number().int().optional(),
936
- locationCity: z.string().trim().min(1).max(255).optional(),
937
- locationState: z.string().trim().min(1).max(255).optional(),
938
- category: z.string().trim().min(1).max(255).optional(),
939
- source: z.string().trim().min(1).max(255).optional(),
940
- batchId: z.string().trim().min(1).max(255).optional(),
941
- pipelineStatus: z.unknown().optional(),
942
- verticalResearch: z.string().trim().min(1).max(5000).optional()
943
- })
944
- .strict()
945
-
946
- export const UpdateCompanyRequestSchema = z
947
- .object({
948
- name: z.string().trim().min(1).max(255).optional(),
949
- clientId: UuidSchema.nullable().optional(),
950
- domain: z.string().trim().min(1).max(255).optional(),
951
- linkedinUrl: z.string().trim().url().optional(),
952
- website: z.string().trim().url().optional(),
953
- numEmployees: z.number().int().min(0).optional(),
954
- foundedYear: z.number().int().optional(),
955
- locationCity: z.string().trim().min(1).max(255).optional(),
956
- locationState: z.string().trim().min(1).max(255).optional(),
957
- category: z.string().trim().min(1).max(255).optional(),
958
- segment: z.string().trim().min(1).max(255).optional(),
959
- processingState: CompanyProcessingStateSchema.optional(),
960
- pipelineStatus: z.unknown().optional(),
961
- enrichmentData: z.record(z.string(), z.unknown()).optional(),
962
- source: z.string().trim().min(1).max(255).optional(),
963
- batchId: z.string().trim().min(1).max(255).optional(),
964
- status: AcqCompanyStatusSchema.optional(),
965
- verticalResearch: z.string().trim().min(1).max(5000).nullable().optional()
966
- })
967
- .strict()
968
- .refine(
969
- (data) =>
970
- data.name !== undefined ||
971
- data.clientId !== undefined ||
972
- data.domain !== undefined ||
973
- data.linkedinUrl !== undefined ||
974
- data.website !== undefined ||
975
- data.numEmployees !== undefined ||
976
- data.foundedYear !== undefined ||
977
- data.locationCity !== undefined ||
978
- data.locationState !== undefined ||
979
- data.category !== undefined ||
980
- data.segment !== undefined ||
981
- data.processingState !== undefined ||
982
- data.pipelineStatus !== undefined ||
983
- data.enrichmentData !== undefined ||
984
- data.source !== undefined ||
985
- data.batchId !== undefined ||
986
- data.status !== undefined ||
987
- data.verticalResearch !== undefined,
988
- {
989
- message: 'At least one field must be provided'
990
- }
991
- )
992
-
993
- export const CreateContactRequestSchema = z
994
- .object({
995
- email: z.string().trim().email(),
996
- clientId: UuidSchema.nullable().optional(),
997
- companyId: UuidSchema.optional(),
998
- firstName: z.string().trim().min(1).max(255).optional(),
999
- lastName: z.string().trim().min(1).max(255).optional(),
1000
- linkedinUrl: z.string().trim().url().optional(),
1001
- title: z.string().trim().min(1).max(255).optional(),
1002
- source: z.string().trim().min(1).max(255).optional(),
1003
- sourceId: z.string().trim().min(1).max(255).optional(),
1004
- batchId: z.string().trim().min(1).max(255).optional(),
1005
- pipelineStatus: z.unknown().optional()
1006
- })
1007
- .strict()
1008
-
1009
- export const UpdateContactRequestSchema = z
1010
- .object({
1011
- companyId: UuidSchema.optional(),
1012
- clientId: UuidSchema.nullable().optional(),
1013
- emailValid: AcqEmailValidSchema.optional(),
1014
- firstName: z.string().trim().min(1).max(255).optional(),
1015
- lastName: z.string().trim().min(1).max(255).optional(),
1016
- linkedinUrl: z.string().trim().url().optional(),
1017
- title: z.string().trim().min(1).max(255).optional(),
1018
- headline: z.string().trim().min(1).max(5000).optional(),
1019
- filterReason: z.string().trim().min(1).max(5000).optional(),
1020
- openingLine: z.string().trim().min(1).max(5000).optional(),
1021
- processingState: ContactProcessingStateSchema.optional(),
1022
- pipelineStatus: z.unknown().optional(),
1023
- enrichmentData: z.record(z.string(), z.unknown()).optional(),
1024
- status: AcqContactStatusSchema.optional()
1025
- })
1026
- .strict()
1027
- .refine(
1028
- (data) =>
1029
- data.companyId !== undefined ||
1030
- data.clientId !== undefined ||
1031
- data.emailValid !== undefined ||
1032
- data.firstName !== undefined ||
1033
- data.lastName !== undefined ||
1034
- data.linkedinUrl !== undefined ||
1035
- data.title !== undefined ||
1036
- data.headline !== undefined ||
1037
- data.filterReason !== undefined ||
1038
- data.openingLine !== undefined ||
1039
- data.processingState !== undefined ||
1040
- data.pipelineStatus !== undefined ||
1041
- data.enrichmentData !== undefined ||
1042
- data.status !== undefined,
1043
- {
1044
- message: 'At least one field must be provided'
1045
- }
1046
- )
1047
-
1048
- export const AcqCompanyResponseSchema = z.object({
1049
- id: z.string(),
1050
- organizationId: z.string(),
1051
- clientId: z.string().nullable().optional(),
1052
- name: z.string(),
1053
- domain: z.string().nullable(),
1054
- linkedinUrl: z.string().nullable(),
1055
- website: z.string().nullable(),
1056
- numEmployees: z.number().nullable(),
1057
- foundedYear: z.number().nullable(),
1058
- locationCity: z.string().nullable(),
1059
- locationState: z.string().nullable(),
1060
- category: z.string().nullable(),
1061
- categoryPain: z.string().nullable(),
1062
- segment: z.string().nullable(),
1063
- processingState: CompanyProcessingStateSchema.nullable(),
1064
- pipelineStatus: z.unknown().nullable().optional(),
1065
- enrichmentData: z.record(z.string(), z.unknown()).nullable(),
1066
- source: z.string().nullable(),
1067
- batchId: z.string().nullable(),
1068
- status: AcqCompanyStatusSchema,
1069
- contactCount: z.number().int().min(0),
1070
- verticalResearch: z.string().nullable(),
1071
- createdAt: z.string(),
1072
- updatedAt: z.string()
1073
- })
1074
-
1075
- export const AcqCompanyListResponseSchema = z.object({
1076
- data: z.array(AcqCompanyResponseSchema),
1077
- total: z.number().int(),
1078
- limit: z.number().int(),
1079
- offset: z.number().int()
1080
- })
1081
-
1082
- export const AcqCompanyFacetsResponseSchema = z.object({
1083
- segments: z.array(z.string()),
1084
- categories: z.array(z.string()),
1085
- statuses: z.array(AcqCompanyStatusSchema)
1086
- })
1087
-
1088
- export const AcqContactCompanySummarySchema = z.object({
1089
- id: z.string(),
1090
- name: z.string(),
1091
- domain: z.string().nullable(),
1092
- website: z.string().nullable(),
1093
- linkedinUrl: z.string().nullable(),
1094
- segment: z.string().nullable(),
1095
- category: z.string().nullable(),
1096
- status: AcqCompanyStatusSchema
1097
- })
1098
-
1099
- export const AcqContactResponseSchema = z.object({
1100
- id: z.string(),
1101
- organizationId: z.string(),
1102
- clientId: z.string().nullable().optional(),
1103
- companyId: z.string().nullable(),
1104
- email: z.string(),
1105
- emailValid: AcqEmailValidSchema.nullable(),
1106
- firstName: z.string().nullable(),
1107
- lastName: z.string().nullable(),
1108
- linkedinUrl: z.string().nullable(),
1109
- title: z.string().nullable(),
1110
- headline: z.string().nullable(),
1111
- filterReason: z.string().nullable(),
1112
- openingLine: z.string().nullable(),
1113
- source: z.string().nullable(),
1114
- sourceId: z.string().nullable(),
1115
- processingState: ContactProcessingStateSchema.nullable(),
1116
- pipelineStatus: z.unknown().nullable().optional(),
1117
- enrichmentData: z.record(z.string(), z.unknown()).nullable(),
1118
- attioPersonId: z.string().nullable(),
1119
- batchId: z.string().nullable(),
1120
- status: AcqContactStatusSchema,
1121
- company: AcqContactCompanySummarySchema.nullable().optional(),
1122
- createdAt: z.string(),
1123
- updatedAt: z.string()
1124
- })
1125
-
1126
- export const AcqContactListResponseSchema = z.object({
1127
- data: z.array(AcqContactResponseSchema),
1128
- total: z.number().int(),
1129
- limit: z.number().int(),
1130
- offset: z.number().int()
1131
- })
1132
-
1133
- // ---------------------------------------------------------------------------
1134
- // Track A: Artifacts API Schemas
1135
- // ---------------------------------------------------------------------------
1136
-
1137
- export const AcqArtifactOwnerKindSchema = z.enum(['company', 'contact', 'deal', 'list', 'list_member'])
1138
-
1139
- export const ListArtifactsQuerySchema = z
1140
- .object({
1141
- ownerKind: AcqArtifactOwnerKindSchema,
1142
- ownerId: UuidSchema
1143
- })
1144
- .strict()
1145
-
1146
- export const CreateArtifactRequestSchema = z
1147
- .object({
1148
- ownerKind: AcqArtifactOwnerKindSchema,
1149
- ownerId: UuidSchema,
1150
- kind: z.string().trim().min(1).max(255),
1151
- content: z.record(z.string(), z.unknown()),
1152
- sourceExecutionId: UuidSchema.optional()
1153
- })
1154
- .strict()
1155
-
1156
- export const AcqArtifactResponseSchema = z.object({
1157
- id: z.string(),
1158
- organizationId: z.string(),
1159
- ownerKind: z.string(),
1160
- ownerId: z.string(),
1161
- kind: z.string(),
1162
- content: z.record(z.string(), z.unknown()),
1163
- sourceExecutionId: z.string().nullable(),
1164
- createdBy: z.string().nullable(),
1165
- createdAt: z.string(),
1166
- version: z.number().int()
1167
- })
1168
-
1169
- export const AcqArtifactListResponseSchema = z.object({
1170
- artifacts: z.array(AcqArtifactResponseSchema)
1171
- })
1172
-
1173
- // ---------------------------------------------------------------------------
1174
- // Track B: List Members API Schemas
1175
- // ---------------------------------------------------------------------------
1176
-
1177
- export const ListMembersQuerySchema = z
1178
- .object({
1179
- limit: z.coerce.number().int().min(1).max(500).default(50),
1180
- offset: z.coerce.number().int().min(0).default(0)
1181
- })
1182
- .strict()
1183
-
1184
- export const ListRecordEntitySchema = z.enum(['company', 'contact'])
1185
-
1186
- export const ListRecordsQuerySchema = z
1187
- .object({
1188
- entity: ListRecordEntitySchema,
1189
- stage: LeadGenStageKeySchema.optional(),
1190
- limit: z.coerce.number().int().min(1).max(500).default(50),
1191
- offset: z.coerce.number().int().min(0).default(0)
1192
- })
1193
- .strict()
519
+ label: z.string().optional(),
520
+ enabled: z.boolean().optional(),
521
+ order: z.number().int().optional()
522
+ })
523
+
524
+ /**
525
+ * Pipeline presentation contract stored in `acq_lists.pipeline_config` jsonb.
526
+ * `stages[].key` validates against the catalog; the rest is presentation only.
527
+ */
528
+ export const PipelineConfigSchema = z.object({
529
+ stages: z.array(PipelineStageSchema).optional()
530
+ })
531
+
532
+ export const BuildPlanSnapshotStepSchema = z
533
+ .object({
534
+ id: z.string().trim().min(1).max(100),
535
+ label: z.string().trim().min(1).max(120),
536
+ description: z.string().trim().min(1).max(2000).optional(),
537
+ primaryEntity: z.enum(['company', 'contact']),
538
+ outputs: z.array(z.enum(['company', 'contact', 'export'])).min(1),
539
+ stageKey: LeadGenStageKeySchema,
540
+ recordEntity: z.enum(['company', 'contact']).optional(),
541
+ recordsStageKey: LeadGenStageKeySchema.optional(),
542
+ recordSourceStageKey: LeadGenStageKeySchema.optional(),
543
+ dependsOn: z.array(z.string().trim().min(1).max(100)).optional(),
544
+ dependencyMode: z.literal('per-record-eligibility'),
545
+ actionKey: LeadGenActionKeySchema,
546
+ defaultBatchSize: z.number().int().positive(),
547
+ maxBatchSize: z.number().int().positive(),
548
+ recordColumns: z
549
+ .object({
550
+ company: z.array(RecordColumnConfigSchema).optional(),
551
+ contact: z.array(RecordColumnConfigSchema).optional()
552
+ })
553
+ .optional(),
554
+ credentialRequirements: z.array(CredentialRequirementSchema).optional()
555
+ })
556
+ .refine((step) => step.defaultBatchSize <= step.maxBatchSize, {
557
+ message: 'defaultBatchSize must be less than or equal to maxBatchSize',
558
+ path: ['defaultBatchSize']
559
+ })
560
+
561
+ export const BuildPlanSnapshotSchema = z
562
+ .object({
563
+ templateId: z.string().trim().min(1).max(100),
564
+ templateLabel: z.string().trim().min(1).max(120),
565
+ steps: z.array(BuildPlanSnapshotStepSchema).min(1)
566
+ })
567
+ .superRefine((snapshot, ctx) => {
568
+ const stepIds = new Set<string>()
569
+
570
+ snapshot.steps.forEach((step, index) => {
571
+ if (stepIds.has(step.id)) {
572
+ ctx.addIssue({
573
+ code: z.ZodIssueCode.custom,
574
+ message: `duplicate build-plan step id "${step.id}"`,
575
+ path: ['steps', index, 'id']
576
+ })
577
+ }
578
+ stepIds.add(step.id)
579
+ })
580
+
581
+ snapshot.steps.forEach((step, index) => {
582
+ for (const dependencyId of step.dependsOn ?? []) {
583
+ if (!stepIds.has(dependencyId)) {
584
+ ctx.addIssue({
585
+ code: z.ZodIssueCode.custom,
586
+ message: `dependsOn references unknown build-plan step "${dependencyId}"`,
587
+ path: ['steps', index, 'dependsOn']
588
+ })
589
+ }
590
+ }
591
+ })
592
+ })
593
+
594
+ export const AcqListMetadataSchema = z
595
+ .object({
596
+ buildPlanSnapshot: BuildPlanSnapshotSchema.optional()
597
+ })
598
+ .catchall(z.unknown())
599
+
600
+ export const ProspectingBuildTemplateIdSchema = z.string().trim().min(1).max(100).refine(isProspectingBuildTemplateId, {
601
+ message: 'buildTemplateId must match a known prospecting build template'
602
+ })
603
+
604
+ // ---------------------------------------------------------------------------
605
+ // List telemetry / progress schemas
606
+ // ---------------------------------------------------------------------------
607
+
608
+ export const ListStageCountsSchema = z.object({
609
+ // Attempted counts by canonical lead-gen stage. The detailed status
610
+ // distribution lives on ListProgress; telemetry keeps the overview payload small.
611
+ stageCounts: z.object({
612
+ populated: z.number().int(),
613
+ extracted: z.number().int(),
614
+ qualified: z.number().int(),
615
+ discovered: z.number().int(),
616
+ verified: z.number().int(),
617
+ personalized: z.number().int(),
618
+ uploaded: z.number().int()
619
+ }),
620
+ deliverability: z.object({
621
+ valid: z.number().int(),
622
+ risky: z.number().int(),
623
+ invalid: z.number().int(),
624
+ unknown: z.number().int(),
625
+ bounced: z.number().int()
626
+ })
627
+ })
628
+
629
+ export const ListTelemetrySchema = z.object({
630
+ listId: UuidSchema,
631
+ totalCompanies: z.number().int(),
632
+ totalContacts: z.number().int(),
633
+ stageCounts: ListStageCountsSchema.shape.stageCounts,
634
+ deliverability: ListStageCountsSchema.shape.deliverability,
635
+ activeWorkflows: z.array(z.string()).optional()
636
+ })
637
+
638
+ // ---------------------------------------------------------------------------
639
+ // Params
640
+ // ---------------------------------------------------------------------------
641
+
642
+ export const ListIdParamsSchema = z.object({
643
+ listId: UuidSchema
644
+ })
645
+
646
+ // ---------------------------------------------------------------------------
647
+ // Request body schemas (all use .strict() — rejects unknown fields)
648
+ // ---------------------------------------------------------------------------
649
+
650
+ export const CreateListRequestSchema = z
651
+ .object({
652
+ name: z.string().trim().min(1).max(255),
653
+ description: z.string().trim().nullable().optional(),
654
+ status: ListStatusSchema.optional(),
655
+ buildTemplateId: ProspectingBuildTemplateIdSchema.optional(),
656
+ scrapingConfig: ScrapingConfigSchema.optional(),
657
+ icp: IcpRubricSchema.optional(),
658
+ pipelineConfig: PipelineConfigSchema.optional()
659
+ })
660
+ .strict()
661
+
662
+ export const UpdateListRequestSchema = z
663
+ .object({
664
+ name: z.string().trim().min(1).max(255).optional(),
665
+ description: z.string().trim().nullable().optional(),
666
+ batchIds: z.array(z.string()).optional(),
667
+ buildTemplateId: ProspectingBuildTemplateIdSchema.optional(),
668
+ confirmBuildTemplateChange: z.literal(true).optional()
669
+ })
670
+ .strict()
671
+ .refine(
672
+ (data) =>
673
+ data.name !== undefined ||
674
+ data.description !== undefined ||
675
+ data.batchIds !== undefined ||
676
+ data.buildTemplateId !== undefined,
677
+ {
678
+ message: 'At least one field (name, description, batchIds, or buildTemplateId) must be provided'
679
+ }
680
+ )
681
+ .refine((data) => data.buildTemplateId === undefined || data.confirmBuildTemplateChange === true, {
682
+ message: 'confirmBuildTemplateChange must be true when changing buildTemplateId',
683
+ path: ['confirmBuildTemplateChange']
684
+ })
685
+
686
+ /**
687
+ * Status-only PATCH body for `/acquisition/lists/:listId/status`.
688
+ * Replaces the previous `transitionList` flow.
689
+ */
690
+ export const UpdateListStatusRequestSchema = z
691
+ .object({
692
+ status: ListStatusSchema
693
+ })
694
+ .strict()
695
+
696
+ /**
697
+ * Partial patch for the three jsonb config columns. UI sends only the edited
698
+ * subtree; server writes the field as-is (no deep merge — each column is
699
+ * replaced atomically when present in the patch).
700
+ */
701
+ export const UpdateListConfigRequestSchema = z
702
+ .object({
703
+ scrapingConfig: ScrapingConfigSchema.partial().optional(),
704
+ icp: IcpRubricSchema.partial().optional(),
705
+ pipelineConfig: PipelineConfigSchema.partial().optional()
706
+ })
707
+ .strict()
708
+ .refine((data) => data.scrapingConfig !== undefined || data.icp !== undefined || data.pipelineConfig !== undefined, {
709
+ message: 'At least one of scrapingConfig, icp, or pipelineConfig must be provided'
710
+ })
711
+
712
+ export const AddCompaniesToListRequestSchema = z
713
+ .object({
714
+ companyIds: z.array(UuidSchema).min(1).max(1000)
715
+ })
716
+ .strict()
717
+
718
+ export const RemoveCompaniesFromListRequestSchema = z
719
+ .object({
720
+ companyIds: z.array(UuidSchema).min(1).max(1000)
721
+ })
722
+ .strict()
723
+
724
+ export const AddContactsToListRequestSchema = z
725
+ .object({
726
+ contactIds: z.array(UuidSchema).min(1).max(1000)
727
+ })
728
+ .strict()
729
+
730
+ export const RecordListExecutionRequestSchema = z
731
+ .object({
732
+ executionId: UuidSchema,
733
+ configSnapshot: z.record(z.string(), z.unknown()).optional()
734
+ })
735
+ .strict()
736
+
737
+ // ---------------------------------------------------------------------------
738
+ // Response schemas (no .strict() — allows forward-compatible additions)
739
+ // ---------------------------------------------------------------------------
740
+
741
+ /**
742
+ * Single list as returned by /api/acquisition/lists/:id etc.
743
+ * Camel-cased domain shape matching AcqList in types.ts.
744
+ */
745
+ export const AcqListResponseSchema = z.object({
746
+ id: z.string(),
747
+ organizationId: z.string(),
748
+ name: z.string(),
749
+ description: z.string().nullable(),
750
+ batchIds: z.array(z.string()),
751
+ instantlyCampaignId: z.string().nullable(),
752
+ /** Lifecycle status (draft | enriching | launched | closing | archived). */
753
+ status: ListStatusSchema,
754
+ metadata: AcqListMetadataSchema,
755
+ launchedAt: z.string().nullable(),
756
+ completedAt: z.string().nullable(),
757
+ createdAt: z.string(),
758
+ /** Scraping criteria stored as jsonb on the row. */
759
+ scrapingConfig: ScrapingConfigSchema,
760
+ /** ICP / qualification rubric stored as jsonb on the row. */
761
+ icp: IcpRubricSchema,
762
+ /** Pipeline presentation contract stored as jsonb on the row. */
763
+ pipelineConfig: PipelineConfigSchema
764
+ })
765
+
766
+ export const AcqListListResponseSchema = z.array(AcqListResponseSchema)
767
+
768
+ export const ListTelemetryResponseSchema = ListTelemetrySchema
769
+
770
+ export const ListTelemetryListResponseSchema = z.array(ListTelemetrySchema)
771
+
772
+ const QueryBooleanSchema = z.preprocess((value) => {
773
+ if (value === 'true' || value === '1' || value === true) return true
774
+ if (value === 'false' || value === '0' || value === false) return false
775
+ return value
776
+ }, z.boolean())
777
+
778
+ export const ListReadQuerySchema = z
779
+ .object({
780
+ status: ListStatusSchema.optional(),
781
+ batch: z.string().trim().min(1).max(255).optional(),
782
+ vertical: z.string().trim().min(1).max(255).optional(),
783
+ limit: z.coerce.number().int().min(1).max(500).optional(),
784
+ offset: z.coerce.number().int().min(0).optional()
785
+ })
786
+ .strict()
787
+
788
+ export const GetListQuerySchema = z
789
+ .object({
790
+ includeDeals: QueryBooleanSchema.default(true),
791
+ includeProgress: QueryBooleanSchema.default(false),
792
+ dealLimit: z.coerce.number().int().min(0).max(100).default(25)
793
+ })
794
+ .strict()
795
+
796
+ /**
797
+ * Per-stage progress aggregate for a single pipeline stage.
798
+ * `attempted` counts terminal statuses, including success, no-result, skipped,
799
+ * error, and tolerant-reader `other` values.
800
+ * `total` = total member/company count for the list.
801
+ */
802
+ export const ListStageProgressSchema = z.object({
803
+ total: z.number().int().min(0),
804
+ attempted: z.number().int().min(0),
805
+ success: z.number().int().min(0),
806
+ noResult: z.number().int().min(0),
807
+ skipped: z.number().int().min(0),
808
+ error: z.number().int().min(0),
809
+ other: z.number().int().min(0),
810
+ notAttempted: z.number().int().min(0)
811
+ })
812
+
813
+ /**
814
+ * Progress response for GET /acquisition/lists/:listId/progress.
815
+ * Aggregated on-demand from processing_state status values.
816
+ * Stage keys are discovered from observed processing_state keys.
817
+ */
818
+ export const ListProgressResponseSchema = z.object({
819
+ totalMembers: z.number().int().min(0),
820
+ totalCompanies: z.number().int().min(0),
821
+ byCompanyStage: z.record(z.string(), ListStageProgressSchema),
822
+ byContactStage: z.record(z.string(), ListStageProgressSchema)
823
+ })
824
+
825
+ export const AcqListDealRefSchema = z.object({
826
+ id: z.string(),
827
+ contactEmail: z.string(),
828
+ stageKey: z.string().nullable(),
829
+ stateKey: z.string().nullable(),
830
+ sourceType: z.string().nullable(),
831
+ lastActivityAt: z.string()
832
+ })
833
+
834
+ export const AcqListLineageSchema = z.object({
835
+ deals: z.object({
836
+ total: z.number().int().min(0),
837
+ refs: z.array(AcqListDealRefSchema),
838
+ truncated: z.boolean()
839
+ })
840
+ })
841
+
842
+ export const AcqListDetailResponseSchema = AcqListResponseSchema.extend({
843
+ lineage: AcqListLineageSchema.optional(),
844
+ progress: ListProgressResponseSchema.optional()
845
+ })
846
+
847
+ export const AcqListStatusListItemSchema = z.object({
848
+ listId: z.string(),
849
+ name: z.string(),
850
+ status: ListStatusSchema,
851
+ totalCompanies: z.number().int().min(0),
852
+ totalContacts: z.number().int().min(0),
853
+ totalDeals: z.number().int().min(0),
854
+ createdAt: z.string()
855
+ })
856
+
857
+ export const AcqListStatusResponseSchema = z.object({
858
+ totalLists: z.number().int().min(0),
859
+ totalCompanies: z.number().int().min(0),
860
+ totalContacts: z.number().int().min(0),
861
+ totalDeals: z.number().int().min(0),
862
+ byStatus: z.record(z.string(), z.number().int().min(0)),
863
+ lists: z.array(AcqListStatusListItemSchema)
864
+ })
865
+
866
+ /**
867
+ * Row from acq_list_executions joined with the execution summary,
868
+ * shaped for the /lists/:id/executions response.
869
+ */
870
+ export const ListExecutionSummarySchema = z.object({
871
+ executionId: z.string(),
872
+ resourceId: z.string(),
873
+ status: z.string(),
874
+ createdAt: z.string(),
875
+ completedAt: z.string().nullable(),
876
+ durationMs: z.number().int().nullable(),
877
+ input: z.unknown().nullable().optional()
878
+ })
879
+
880
+ export const ListExecutionsResponseSchema = z.array(ListExecutionSummarySchema)
881
+
882
+ // ---------------------------------------------------------------------------
883
+ // Company / Contact API Schemas
884
+ // ---------------------------------------------------------------------------
885
+
886
+ export const AcqCompanyStatusSchema = z.enum(['active', 'invalid'])
887
+ export const AcqContactStatusSchema = z.enum(['active', 'invalid'])
888
+ export const AcqEmailValidSchema = z.enum(['VALID', 'INVALID', 'RISKY', 'UNKNOWN'])
889
+
890
+ export const CompanyIdParamsSchema = z.object({
891
+ companyId: UuidSchema
892
+ })
893
+
894
+ export const ContactIdParamsSchema = z.object({
895
+ contactId: UuidSchema
896
+ })
897
+
898
+ export const ListCompaniesQuerySchema = z
899
+ .object({
900
+ search: z.string().trim().min(1).max(200).optional(),
901
+ listId: UuidSchema.optional(),
902
+ domain: z.string().trim().min(1).max(255).optional(),
903
+ website: z.string().trim().min(1).max(2048).optional(),
904
+ segment: z.string().trim().min(1).max(255).optional(),
905
+ category: z.string().trim().min(1).max(255).optional(),
906
+ pipelineStatus: z.unknown().optional(),
907
+ batchId: z.string().trim().min(1).max(255).optional(),
908
+ status: AcqCompanyStatusSchema.optional(),
909
+ includeAll: QueryBooleanSchema.optional(),
910
+ limit: z.coerce.number().int().min(1).max(5000).default(50),
911
+ offset: z.coerce.number().int().min(0).default(0)
912
+ })
913
+ .strict()
914
+
915
+ export const ListContactsQuerySchema = z
916
+ .object({
917
+ search: z.string().trim().min(1).max(200).optional(),
918
+ listId: UuidSchema.optional(),
919
+ openingLineIsNull: QueryBooleanSchema.optional(),
920
+ batchId: z.string().trim().min(1).max(255).optional(),
921
+ contactStatus: AcqContactStatusSchema.optional(),
922
+ limit: z.coerce.number().int().min(1).max(5000).default(5000),
923
+ offset: z.coerce.number().int().min(0).default(0)
924
+ })
925
+ .strict()
926
+
927
+ export const CreateCompanyRequestSchema = z
928
+ .object({
929
+ name: z.string().trim().min(1).max(255),
930
+ clientId: UuidSchema.nullable().optional(),
931
+ domain: z.string().trim().min(1).max(255).optional(),
932
+ linkedinUrl: z.string().trim().url().optional(),
933
+ website: z.string().trim().url().optional(),
934
+ numEmployees: z.number().int().min(0).optional(),
935
+ foundedYear: z.number().int().optional(),
936
+ locationCity: z.string().trim().min(1).max(255).optional(),
937
+ locationState: z.string().trim().min(1).max(255).optional(),
938
+ category: z.string().trim().min(1).max(255).optional(),
939
+ source: z.string().trim().min(1).max(255).optional(),
940
+ batchId: z.string().trim().min(1).max(255).optional(),
941
+ pipelineStatus: z.unknown().optional(),
942
+ verticalResearch: z.string().trim().min(1).max(5000).optional()
943
+ })
944
+ .strict()
945
+
946
+ export const UpdateCompanyRequestSchema = z
947
+ .object({
948
+ name: z.string().trim().min(1).max(255).optional(),
949
+ clientId: UuidSchema.nullable().optional(),
950
+ domain: z.string().trim().min(1).max(255).optional(),
951
+ linkedinUrl: z.string().trim().url().optional(),
952
+ website: z.string().trim().url().optional(),
953
+ numEmployees: z.number().int().min(0).optional(),
954
+ foundedYear: z.number().int().optional(),
955
+ locationCity: z.string().trim().min(1).max(255).optional(),
956
+ locationState: z.string().trim().min(1).max(255).optional(),
957
+ category: z.string().trim().min(1).max(255).optional(),
958
+ segment: z.string().trim().min(1).max(255).optional(),
959
+ processingState: CompanyProcessingStateSchema.optional(),
960
+ pipelineStatus: z.unknown().optional(),
961
+ enrichmentData: z.record(z.string(), z.unknown()).optional(),
962
+ source: z.string().trim().min(1).max(255).optional(),
963
+ batchId: z.string().trim().min(1).max(255).optional(),
964
+ status: AcqCompanyStatusSchema.optional(),
965
+ verticalResearch: z.string().trim().min(1).max(5000).nullable().optional()
966
+ })
967
+ .strict()
968
+ .refine(
969
+ (data) =>
970
+ data.name !== undefined ||
971
+ data.clientId !== undefined ||
972
+ data.domain !== undefined ||
973
+ data.linkedinUrl !== undefined ||
974
+ data.website !== undefined ||
975
+ data.numEmployees !== undefined ||
976
+ data.foundedYear !== undefined ||
977
+ data.locationCity !== undefined ||
978
+ data.locationState !== undefined ||
979
+ data.category !== undefined ||
980
+ data.segment !== undefined ||
981
+ data.processingState !== undefined ||
982
+ data.pipelineStatus !== undefined ||
983
+ data.enrichmentData !== undefined ||
984
+ data.source !== undefined ||
985
+ data.batchId !== undefined ||
986
+ data.status !== undefined ||
987
+ data.verticalResearch !== undefined,
988
+ {
989
+ message: 'At least one field must be provided'
990
+ }
991
+ )
992
+
993
+ export const CreateContactRequestSchema = z
994
+ .object({
995
+ email: z.string().trim().email(),
996
+ clientId: UuidSchema.nullable().optional(),
997
+ companyId: UuidSchema.optional(),
998
+ firstName: z.string().trim().min(1).max(255).optional(),
999
+ lastName: z.string().trim().min(1).max(255).optional(),
1000
+ linkedinUrl: z.string().trim().url().optional(),
1001
+ title: z.string().trim().min(1).max(255).optional(),
1002
+ source: z.string().trim().min(1).max(255).optional(),
1003
+ sourceId: z.string().trim().min(1).max(255).optional(),
1004
+ batchId: z.string().trim().min(1).max(255).optional(),
1005
+ pipelineStatus: z.unknown().optional()
1006
+ })
1007
+ .strict()
1008
+
1009
+ export const UpdateContactRequestSchema = z
1010
+ .object({
1011
+ companyId: UuidSchema.optional(),
1012
+ clientId: UuidSchema.nullable().optional(),
1013
+ emailValid: AcqEmailValidSchema.optional(),
1014
+ firstName: z.string().trim().min(1).max(255).optional(),
1015
+ lastName: z.string().trim().min(1).max(255).optional(),
1016
+ linkedinUrl: z.string().trim().url().optional(),
1017
+ title: z.string().trim().min(1).max(255).optional(),
1018
+ headline: z.string().trim().min(1).max(5000).optional(),
1019
+ filterReason: z.string().trim().min(1).max(5000).optional(),
1020
+ openingLine: z.string().trim().min(1).max(5000).optional(),
1021
+ processingState: ContactProcessingStateSchema.optional(),
1022
+ pipelineStatus: z.unknown().optional(),
1023
+ enrichmentData: z.record(z.string(), z.unknown()).optional(),
1024
+ status: AcqContactStatusSchema.optional()
1025
+ })
1026
+ .strict()
1027
+ .refine(
1028
+ (data) =>
1029
+ data.companyId !== undefined ||
1030
+ data.clientId !== undefined ||
1031
+ data.emailValid !== undefined ||
1032
+ data.firstName !== undefined ||
1033
+ data.lastName !== undefined ||
1034
+ data.linkedinUrl !== undefined ||
1035
+ data.title !== undefined ||
1036
+ data.headline !== undefined ||
1037
+ data.filterReason !== undefined ||
1038
+ data.openingLine !== undefined ||
1039
+ data.processingState !== undefined ||
1040
+ data.pipelineStatus !== undefined ||
1041
+ data.enrichmentData !== undefined ||
1042
+ data.status !== undefined,
1043
+ {
1044
+ message: 'At least one field must be provided'
1045
+ }
1046
+ )
1047
+
1048
+ export const AcqCompanyResponseSchema = z.object({
1049
+ id: z.string(),
1050
+ organizationId: z.string(),
1051
+ clientId: z.string().nullable().optional(),
1052
+ name: z.string(),
1053
+ domain: z.string().nullable(),
1054
+ linkedinUrl: z.string().nullable(),
1055
+ website: z.string().nullable(),
1056
+ numEmployees: z.number().nullable(),
1057
+ foundedYear: z.number().nullable(),
1058
+ locationCity: z.string().nullable(),
1059
+ locationState: z.string().nullable(),
1060
+ category: z.string().nullable(),
1061
+ categoryPain: z.string().nullable(),
1062
+ segment: z.string().nullable(),
1063
+ processingState: CompanyProcessingStateSchema.nullable(),
1064
+ pipelineStatus: z.unknown().nullable().optional(),
1065
+ enrichmentData: z.record(z.string(), z.unknown()).nullable(),
1066
+ source: z.string().nullable(),
1067
+ batchId: z.string().nullable(),
1068
+ status: AcqCompanyStatusSchema,
1069
+ contactCount: z.number().int().min(0),
1070
+ verticalResearch: z.string().nullable(),
1071
+ createdAt: z.string(),
1072
+ updatedAt: z.string()
1073
+ })
1074
+
1075
+ export const AcqCompanyListResponseSchema = z.object({
1076
+ data: z.array(AcqCompanyResponseSchema),
1077
+ total: z.number().int(),
1078
+ limit: z.number().int(),
1079
+ offset: z.number().int()
1080
+ })
1081
+
1082
+ export const AcqCompanyFacetsResponseSchema = z.object({
1083
+ segments: z.array(z.string()),
1084
+ categories: z.array(z.string()),
1085
+ statuses: z.array(AcqCompanyStatusSchema)
1086
+ })
1087
+
1088
+ export const AcqContactCompanySummarySchema = z.object({
1089
+ id: z.string(),
1090
+ name: z.string(),
1091
+ domain: z.string().nullable(),
1092
+ website: z.string().nullable(),
1093
+ linkedinUrl: z.string().nullable(),
1094
+ segment: z.string().nullable(),
1095
+ category: z.string().nullable(),
1096
+ status: AcqCompanyStatusSchema
1097
+ })
1098
+
1099
+ export const AcqContactResponseSchema = z.object({
1100
+ id: z.string(),
1101
+ organizationId: z.string(),
1102
+ clientId: z.string().nullable().optional(),
1103
+ companyId: z.string().nullable(),
1104
+ email: z.string(),
1105
+ emailValid: AcqEmailValidSchema.nullable(),
1106
+ firstName: z.string().nullable(),
1107
+ lastName: z.string().nullable(),
1108
+ linkedinUrl: z.string().nullable(),
1109
+ title: z.string().nullable(),
1110
+ headline: z.string().nullable(),
1111
+ filterReason: z.string().nullable(),
1112
+ openingLine: z.string().nullable(),
1113
+ source: z.string().nullable(),
1114
+ sourceId: z.string().nullable(),
1115
+ processingState: ContactProcessingStateSchema.nullable(),
1116
+ pipelineStatus: z.unknown().nullable().optional(),
1117
+ enrichmentData: z.record(z.string(), z.unknown()).nullable(),
1118
+ attioPersonId: z.string().nullable(),
1119
+ batchId: z.string().nullable(),
1120
+ status: AcqContactStatusSchema,
1121
+ company: AcqContactCompanySummarySchema.nullable().optional(),
1122
+ createdAt: z.string(),
1123
+ updatedAt: z.string()
1124
+ })
1125
+
1126
+ export const AcqContactListResponseSchema = z.object({
1127
+ data: z.array(AcqContactResponseSchema),
1128
+ total: z.number().int(),
1129
+ limit: z.number().int(),
1130
+ offset: z.number().int()
1131
+ })
1132
+
1133
+ // ---------------------------------------------------------------------------
1134
+ // Track A: Artifacts API Schemas
1135
+ // ---------------------------------------------------------------------------
1136
+
1137
+ export const AcqArtifactOwnerKindSchema = z.enum(['company', 'contact', 'deal', 'list', 'list_member'])
1138
+
1139
+ export const ListArtifactsQuerySchema = z
1140
+ .object({
1141
+ ownerKind: AcqArtifactOwnerKindSchema,
1142
+ ownerId: UuidSchema
1143
+ })
1144
+ .strict()
1145
+
1146
+ export const CreateArtifactRequestSchema = z
1147
+ .object({
1148
+ ownerKind: AcqArtifactOwnerKindSchema,
1149
+ ownerId: UuidSchema,
1150
+ kind: z.string().trim().min(1).max(255),
1151
+ content: z.record(z.string(), z.unknown()),
1152
+ sourceExecutionId: UuidSchema.optional()
1153
+ })
1154
+ .strict()
1155
+
1156
+ export const AcqArtifactResponseSchema = z.object({
1157
+ id: z.string(),
1158
+ organizationId: z.string(),
1159
+ ownerKind: z.string(),
1160
+ ownerId: z.string(),
1161
+ kind: z.string(),
1162
+ content: z.record(z.string(), z.unknown()),
1163
+ sourceExecutionId: z.string().nullable(),
1164
+ createdBy: z.string().nullable(),
1165
+ createdAt: z.string(),
1166
+ version: z.number().int()
1167
+ })
1168
+
1169
+ export const AcqArtifactListResponseSchema = z.object({
1170
+ artifacts: z.array(AcqArtifactResponseSchema)
1171
+ })
1172
+
1173
+ // ---------------------------------------------------------------------------
1174
+ // Track B: List Members API Schemas
1175
+ // ---------------------------------------------------------------------------
1176
+
1177
+ export const ListMembersQuerySchema = z
1178
+ .object({
1179
+ limit: z.coerce.number().int().min(1).max(500).default(50),
1180
+ offset: z.coerce.number().int().min(0).default(0)
1181
+ })
1182
+ .strict()
1183
+
1184
+ export const ListRecordEntitySchema = z.enum(['company', 'contact'])
1185
+
1186
+ export const ListRecordsQuerySchema = z
1187
+ .object({
1188
+ entity: ListRecordEntitySchema,
1189
+ stage: LeadGenStageKeySchema.optional(),
1190
+ limit: z.coerce.number().int().min(1).max(500).default(50),
1191
+ offset: z.coerce.number().int().min(0).default(0)
1192
+ })
1193
+ .strict()
1194
1194
  .superRefine((query, ctx) => {
1195
1195
  if (!query.stage) return
1196
1196
 
1197
- const stage = LEAD_GEN_STAGE_CATALOG[query.stage]
1198
- const validEntity =
1199
- stage?.entity === query.entity ||
1200
- stage?.additionalEntities?.includes(query.entity) ||
1201
- stage?.recordEntity === query.entity
1202
- if (!validEntity) {
1197
+ if (!isLeadGenRecordStageValidForEntity(query.stage, query.entity)) {
1203
1198
  ctx.addIssue({
1204
1199
  code: z.ZodIssueCode.custom,
1205
1200
  message: `stage "${query.stage}" is not valid for ${query.entity} records`,
1206
- path: ['stage']
1207
- })
1208
- }
1209
- })
1210
-
1211
- export const MemberIdParamsSchema = z.object({
1212
- memberId: UuidSchema
1213
- })
1214
-
1215
- export const AcqListMemberContactSummarySchema = z.object({
1216
- id: z.string(),
1217
- email: z.string(),
1218
- firstName: z.string().nullable(),
1219
- lastName: z.string().nullable(),
1220
- title: z.string().nullable(),
1221
- linkedinUrl: z.string().nullable(),
1222
- companyId: z.string().nullable()
1223
- })
1224
-
1225
- export const AcqListMemberResponseSchema = z.object({
1226
- id: z.string(),
1227
- listId: z.string(),
1228
- contactId: z.string(),
1229
- pipelineKey: z.string(),
1230
- stageKey: z.string(),
1231
- stateKey: z.string(),
1232
- activityLog: z.unknown(),
1233
- addedAt: z.string(),
1234
- addedBy: z.string().nullable(),
1235
- sourceExecutionId: z.string().nullable(),
1236
- contact: AcqListMemberContactSummarySchema.nullable()
1237
- })
1238
-
1239
- export const AcqListMembersResponseSchema = z.object({
1240
- members: z.array(AcqListMemberResponseSchema)
1241
- })
1242
-
1243
- export const AcqListRecordCompanySummarySchema = z.object({
1244
- id: z.string(),
1245
- name: z.string(),
1246
- domain: z.string().nullable(),
1247
- website: z.string().nullable(),
1248
- linkedinUrl: z.string().nullable(),
1249
- numEmployees: z.number().nullable(),
1250
- foundedYear: z.number().nullable(),
1251
- locationCity: z.string().nullable(),
1252
- locationState: z.string().nullable(),
1253
- category: z.string().nullable(),
1254
- segment: z.string().nullable(),
1255
- status: AcqCompanyStatusSchema,
1256
- qualificationScore: z.number().nullable(),
1257
- qualificationSignals: z.record(z.string(), z.unknown()).nullable(),
1258
- qualificationRubricKey: z.string().nullable()
1259
- })
1260
-
1261
- export const AcqListRecordContactSummarySchema = z.object({
1262
- id: z.string(),
1263
- email: z.string(),
1264
- firstName: z.string().nullable(),
1265
- lastName: z.string().nullable(),
1266
- title: z.string().nullable(),
1267
- headline: z.string().nullable(),
1268
- linkedinUrl: z.string().nullable(),
1269
- companyId: z.string().nullable(),
1270
- status: AcqContactStatusSchema,
1271
- qualificationScore: z.number().nullable(),
1272
- qualificationSignals: z.record(z.string(), z.unknown()).nullable(),
1273
- qualificationRubricKey: z.string().nullable()
1274
- })
1275
-
1276
- const ListRecordBaseSchema = z.object({
1277
- id: z.string(),
1278
- listId: z.string(),
1279
- pipelineKey: z.string(),
1280
- stageKey: z.string(),
1281
- stateKey: z.string(),
1282
- activityLog: z.unknown(),
1283
- addedAt: z.string(),
1284
- addedBy: z.string().nullable(),
1285
- sourceExecutionId: z.string().nullable(),
1286
- processingState: z.record(z.string(), z.unknown()).nullable(),
1287
- enrichmentData: z.record(z.string(), z.unknown()).nullable()
1288
- })
1289
-
1290
- export const AcqListCompanyRecordRowSchema = ListRecordBaseSchema.extend({
1291
- entity: z.literal('company'),
1292
- companyId: z.string(),
1293
- company: AcqListRecordCompanySummarySchema.nullable()
1294
- })
1295
-
1296
- export const AcqListContactRecordRowSchema = ListRecordBaseSchema.extend({
1297
- entity: z.literal('contact'),
1298
- contactId: z.string(),
1299
- contact: AcqListRecordContactSummarySchema.nullable()
1300
- })
1301
-
1302
- export const ListRecordRowSchema = z.discriminatedUnion('entity', [
1303
- AcqListCompanyRecordRowSchema,
1304
- AcqListContactRecordRowSchema
1305
- ])
1306
-
1307
- export const ListRecordsResponseSchema = z.object({
1308
- data: z.array(ListRecordRowSchema),
1309
- total: z.number().int().min(0),
1310
- limit: z.number().int().min(1),
1311
- offset: z.number().int().min(0)
1312
- })
1313
-
1314
- // ---------------------------------------------------------------------------
1315
- // Track B: List Companies API Schemas
1316
- // ---------------------------------------------------------------------------
1317
-
1318
- export const ListCompanyIdParamsSchema = z.object({
1319
- listCompanyId: UuidSchema
1320
- })
1321
-
1322
- export const AcqListCompanyResponseSchema = z.object({
1323
- id: z.string(),
1324
- listId: z.string(),
1325
- companyId: z.string(),
1326
- pipelineKey: z.string(),
1327
- stageKey: z.string(),
1328
- stateKey: z.string(),
1329
- activityLog: z.unknown(),
1330
- addedAt: z.string(),
1331
- addedBy: z.string().nullable(),
1332
- sourceExecutionId: z.string().nullable()
1333
- })
1334
-
1335
- // ---------------------------------------------------------------------------
1336
- // Track B: Transition request for list, list-member, and list-company substrate routes.
1337
- // CRM deals use DealSchemas.TransitionItemRequest, which is catalog-backed.
1338
- // ---------------------------------------------------------------------------
1339
-
1340
- export const AcqCompanySchemas = {
1341
- CompanyProcessingState: CompanyProcessingStateSchema,
1342
- CompanyIdParams: CompanyIdParamsSchema,
1343
- ListCompaniesQuery: ListCompaniesQuerySchema,
1344
- CreateCompanyRequest: CreateCompanyRequestSchema,
1345
- UpdateCompanyRequest: UpdateCompanyRequestSchema,
1346
- AcqCompanyResponse: AcqCompanyResponseSchema,
1347
- AcqCompanyListResponse: AcqCompanyListResponseSchema,
1348
- AcqCompanyFacetsResponse: AcqCompanyFacetsResponseSchema
1349
- }
1350
-
1351
- export const AcqContactSchemas = {
1352
- ContactProcessingState: ContactProcessingStateSchema,
1353
- ContactIdParams: ContactIdParamsSchema,
1354
- ListContactsQuery: ListContactsQuerySchema,
1355
- CreateContactRequest: CreateContactRequestSchema,
1356
- UpdateContactRequest: UpdateContactRequestSchema,
1357
- AcqContactResponse: AcqContactResponseSchema,
1358
- AcqContactListResponse: AcqContactListResponseSchema
1359
- }
1360
-
1361
- export type AcqCompanyStatus = z.infer<typeof AcqCompanyStatusSchema>
1362
- export type AcqContactStatus = z.infer<typeof AcqContactStatusSchema>
1363
- export type AcqEmailValid = z.infer<typeof AcqEmailValidSchema>
1364
- export type CompanyIdParams = z.infer<typeof CompanyIdParamsSchema>
1365
- export type ContactIdParams = z.infer<typeof ContactIdParamsSchema>
1366
- export type ListCompaniesQuery = z.infer<typeof ListCompaniesQuerySchema>
1367
- export type ListContactsQuery = z.infer<typeof ListContactsQuerySchema>
1368
- export type CreateCompanyRequest = z.infer<typeof CreateCompanyRequestSchema>
1369
- export type UpdateCompanyRequest = z.infer<typeof UpdateCompanyRequestSchema>
1370
- export type CreateContactRequest = z.infer<typeof CreateContactRequestSchema>
1371
- export type UpdateContactRequest = z.infer<typeof UpdateContactRequestSchema>
1372
- export type AcqCompanyResponse = z.infer<typeof AcqCompanyResponseSchema>
1373
- export type AcqCompanyListResponse = z.infer<typeof AcqCompanyListResponseSchema>
1374
- export type AcqCompanyFacetsResponse = z.infer<typeof AcqCompanyFacetsResponseSchema>
1375
- export type AcqContactCompanySummary = z.infer<typeof AcqContactCompanySummarySchema>
1376
- export type AcqContactResponse = z.infer<typeof AcqContactResponseSchema>
1377
- export type AcqContactListResponse = z.infer<typeof AcqContactListResponseSchema>
1378
-
1379
- // ---------------------------------------------------------------------------
1380
- // Bundled export
1381
- // ---------------------------------------------------------------------------
1382
-
1383
- export const AcqListSchemas = {
1384
- // Params
1385
- ListIdParams: ListIdParamsSchema,
1386
-
1387
- // Primitives (for UI / tests)
1388
- ListStatus: ListStatusSchema,
1389
- ScrapingConfig: ScrapingConfigSchema,
1390
- IcpRubric: IcpRubricSchema,
1391
- PipelineConfig: PipelineConfigSchema,
1392
- PipelineStage: PipelineStageSchema,
1393
- BuildPlanSnapshot: BuildPlanSnapshotSchema,
1394
- BuildPlanSnapshotStep: BuildPlanSnapshotStepSchema,
1395
- AcqListMetadata: AcqListMetadataSchema,
1396
- LeadGenStageKey: LeadGenStageKeySchema,
1397
- LeadGenActionKey: LeadGenActionKeySchema,
1398
- ProcessingStageStatus: ProcessingStageStatusSchema,
1399
- ProcessingState: ProcessingStateSchema,
1400
- ListStageCounts: ListStageCountsSchema,
1401
- ListTelemetry: ListTelemetrySchema,
1402
-
1403
- // Requests
1404
- ListReadQuery: ListReadQuerySchema,
1405
- GetListQuery: GetListQuerySchema,
1406
- CreateListRequest: CreateListRequestSchema,
1407
- UpdateListRequest: UpdateListRequestSchema,
1408
- UpdateListStatusRequest: UpdateListStatusRequestSchema,
1409
- UpdateListConfigRequest: UpdateListConfigRequestSchema,
1410
- AddCompaniesToListRequest: AddCompaniesToListRequestSchema,
1411
- RemoveCompaniesFromListRequest: RemoveCompaniesFromListRequestSchema,
1412
- AddContactsToListRequest: AddContactsToListRequestSchema,
1413
- RecordListExecutionRequest: RecordListExecutionRequestSchema,
1414
-
1415
- // Responses
1416
- AcqListResponse: AcqListResponseSchema,
1417
- AcqListDetailResponse: AcqListDetailResponseSchema,
1418
- AcqListListResponse: AcqListListResponseSchema,
1419
- AcqListDealRef: AcqListDealRefSchema,
1420
- AcqListLineage: AcqListLineageSchema,
1421
- AcqListStatusResponse: AcqListStatusResponseSchema,
1422
- ListTelemetryResponse: ListTelemetryResponseSchema,
1423
- ListTelemetryListResponse: ListTelemetryListResponseSchema,
1424
- ListExecutionsResponse: ListExecutionsResponseSchema,
1425
- ListProgressResponse: ListProgressResponseSchema
1426
- }
1427
-
1428
- // ---------------------------------------------------------------------------
1429
- // Track A/B bundled schemas
1430
- // ---------------------------------------------------------------------------
1431
-
1432
- export const AcqSubstrateSchemas = {
1433
- // Artifacts
1434
- ListArtifactsQuery: ListArtifactsQuerySchema,
1435
- CreateArtifactRequest: CreateArtifactRequestSchema,
1436
- AcqArtifactResponse: AcqArtifactResponseSchema,
1437
- AcqArtifactListResponse: AcqArtifactListResponseSchema,
1438
-
1439
- // List members
1440
- ListMembersQuery: ListMembersQuerySchema,
1441
- ListRecordsQuery: ListRecordsQuerySchema,
1442
- MemberIdParams: MemberIdParamsSchema,
1443
- AcqListMemberResponse: AcqListMemberResponseSchema,
1444
- AcqListMembersResponse: AcqListMembersResponseSchema,
1445
- AcqListCompanyRecordRow: AcqListCompanyRecordRowSchema,
1446
- AcqListContactRecordRow: AcqListContactRecordRowSchema,
1447
- ListRecordRow: ListRecordRowSchema,
1448
- ListRecordsResponse: ListRecordsResponseSchema,
1449
-
1450
- // List companies
1451
- ListCompanyIdParams: ListCompanyIdParamsSchema,
1452
- AcqListCompanyResponse: AcqListCompanyResponseSchema,
1453
-
1454
- // Transition (generic stateful substrate)
1455
- TransitionItemRequest: TransitionItemRequestSchema
1456
- }
1457
-
1458
- // ---------------------------------------------------------------------------
1459
- // Inferred types
1460
- // ---------------------------------------------------------------------------
1461
-
1462
- // ---------------------------------------------------------------------------
1463
- // Inferred types — Track A/B substrate
1464
- // ---------------------------------------------------------------------------
1465
-
1466
- export type AcqArtifactOwnerKind = z.infer<typeof AcqArtifactOwnerKindSchema>
1467
- export type ListArtifactsQuery = z.infer<typeof ListArtifactsQuerySchema>
1468
- export type CreateArtifactRequest = z.infer<typeof CreateArtifactRequestSchema>
1469
- export type AcqArtifactResponse = z.infer<typeof AcqArtifactResponseSchema>
1470
- export type AcqArtifactListResponse = z.infer<typeof AcqArtifactListResponseSchema>
1471
- export type ListMembersQuery = z.infer<typeof ListMembersQuerySchema>
1472
- export type ListRecordEntity = z.infer<typeof ListRecordEntitySchema>
1473
- export type ListRecordsQuery = z.infer<typeof ListRecordsQuerySchema>
1474
- export type MemberIdParams = z.infer<typeof MemberIdParamsSchema>
1475
- export type AcqListMemberContactSummary = z.infer<typeof AcqListMemberContactSummarySchema>
1476
- export type AcqListMemberResponse = z.infer<typeof AcqListMemberResponseSchema>
1477
- export type AcqListMembersResponse = z.infer<typeof AcqListMembersResponseSchema>
1478
- export type AcqListCompanyRecordRow = z.infer<typeof AcqListCompanyRecordRowSchema>
1479
- export type AcqListContactRecordRow = z.infer<typeof AcqListContactRecordRowSchema>
1480
- export type ListRecordRow = z.infer<typeof ListRecordRowSchema>
1481
- export type ListRecordsResponse = z.infer<typeof ListRecordsResponseSchema>
1482
- export type ListCompanyIdParams = z.infer<typeof ListCompanyIdParamsSchema>
1483
- export type AcqListCompanyResponse = z.infer<typeof AcqListCompanyResponseSchema>
1484
-
1485
- // ---------------------------------------------------------------------------
1486
-
1487
- export type ListStatus = z.infer<typeof ListStatusSchema>
1488
- export type ScrapingConfig = z.infer<typeof ScrapingConfigSchema>
1489
- export type IcpRubric = z.infer<typeof IcpRubricSchema>
1490
- export type PipelineStage = z.infer<typeof PipelineStageSchema>
1491
- export type PipelineConfig = z.infer<typeof PipelineConfigSchema>
1492
- export type BuildPlanSnapshotStep = z.infer<typeof BuildPlanSnapshotStepSchema>
1493
- export type BuildPlanSnapshot = z.infer<typeof BuildPlanSnapshotSchema>
1494
- export type AcqListMetadata = z.infer<typeof AcqListMetadataSchema>
1495
- export type LeadGenActionKey = z.infer<typeof LeadGenActionKeySchema>
1496
- export type ProcessingStageStatus = z.infer<typeof ProcessingStageStatusSchema>
1497
- export type ListStageCountsInput = z.infer<typeof ListStageCountsSchema>['stageCounts']
1498
- export type ListTelemetryInput = z.infer<typeof ListTelemetrySchema>
1499
- export type ListIdParams = z.infer<typeof ListIdParamsSchema>
1500
- export type ListReadQuery = z.infer<typeof ListReadQuerySchema>
1501
- export type GetListQuery = z.infer<typeof GetListQuerySchema>
1502
- export type CreateListRequest = z.infer<typeof CreateListRequestSchema>
1503
- export type UpdateListRequest = z.infer<typeof UpdateListRequestSchema>
1504
- export type UpdateListStatusRequest = z.infer<typeof UpdateListStatusRequestSchema>
1505
- export type UpdateListConfigRequest = z.infer<typeof UpdateListConfigRequestSchema>
1506
- export type AddCompaniesToListRequest = z.infer<typeof AddCompaniesToListRequestSchema>
1507
- export type RemoveCompaniesFromListRequest = z.infer<typeof RemoveCompaniesFromListRequestSchema>
1508
- export type AddContactsToListRequest = z.infer<typeof AddContactsToListRequestSchema>
1509
- export type RecordListExecutionRequest = z.infer<typeof RecordListExecutionRequestSchema>
1510
- export type AcqListResponse = z.infer<typeof AcqListResponseSchema>
1511
- export type AcqListDetailResponse = z.infer<typeof AcqListDetailResponseSchema>
1512
- export type AcqListListResponse = z.infer<typeof AcqListListResponseSchema>
1513
- export type AcqListDealRef = z.infer<typeof AcqListDealRefSchema>
1514
- export type AcqListLineage = z.infer<typeof AcqListLineageSchema>
1515
- export type AcqListStatusResponse = z.infer<typeof AcqListStatusResponseSchema>
1516
- export type ListTelemetryResponse = z.infer<typeof ListTelemetryResponseSchema>
1517
- export type ListTelemetryListResponse = z.infer<typeof ListTelemetryListResponseSchema>
1518
- export type ListExecutionSummaryInput = z.infer<typeof ListExecutionSummarySchema>
1519
- export type ListExecutionsResponse = z.infer<typeof ListExecutionsResponseSchema>
1520
- export type ListStageProgress = z.infer<typeof ListStageProgressSchema>
1521
- export type ListProgress = z.infer<typeof ListProgressResponseSchema>
1201
+ path: ['stage']
1202
+ })
1203
+ }
1204
+ })
1205
+
1206
+ export const MemberIdParamsSchema = z.object({
1207
+ memberId: UuidSchema
1208
+ })
1209
+
1210
+ export const AcqListMemberContactSummarySchema = z.object({
1211
+ id: z.string(),
1212
+ email: z.string(),
1213
+ firstName: z.string().nullable(),
1214
+ lastName: z.string().nullable(),
1215
+ title: z.string().nullable(),
1216
+ linkedinUrl: z.string().nullable(),
1217
+ companyId: z.string().nullable()
1218
+ })
1219
+
1220
+ export const AcqListMemberResponseSchema = z.object({
1221
+ id: z.string(),
1222
+ listId: z.string(),
1223
+ contactId: z.string(),
1224
+ pipelineKey: z.string(),
1225
+ stageKey: z.string(),
1226
+ stateKey: z.string(),
1227
+ activityLog: z.unknown(),
1228
+ addedAt: z.string(),
1229
+ addedBy: z.string().nullable(),
1230
+ sourceExecutionId: z.string().nullable(),
1231
+ contact: AcqListMemberContactSummarySchema.nullable()
1232
+ })
1233
+
1234
+ export const AcqListMembersResponseSchema = z.object({
1235
+ members: z.array(AcqListMemberResponseSchema)
1236
+ })
1237
+
1238
+ export const AcqListRecordCompanySummarySchema = z.object({
1239
+ id: z.string(),
1240
+ name: z.string(),
1241
+ domain: z.string().nullable(),
1242
+ website: z.string().nullable(),
1243
+ linkedinUrl: z.string().nullable(),
1244
+ numEmployees: z.number().nullable(),
1245
+ foundedYear: z.number().nullable(),
1246
+ locationCity: z.string().nullable(),
1247
+ locationState: z.string().nullable(),
1248
+ category: z.string().nullable(),
1249
+ segment: z.string().nullable(),
1250
+ status: AcqCompanyStatusSchema,
1251
+ qualificationScore: z.number().nullable(),
1252
+ qualificationSignals: z.record(z.string(), z.unknown()).nullable(),
1253
+ qualificationRubricKey: z.string().nullable()
1254
+ })
1255
+
1256
+ export const AcqListRecordContactSummarySchema = z.object({
1257
+ id: z.string(),
1258
+ email: z.string(),
1259
+ firstName: z.string().nullable(),
1260
+ lastName: z.string().nullable(),
1261
+ title: z.string().nullable(),
1262
+ headline: z.string().nullable(),
1263
+ linkedinUrl: z.string().nullable(),
1264
+ companyId: z.string().nullable(),
1265
+ status: AcqContactStatusSchema,
1266
+ qualificationScore: z.number().nullable(),
1267
+ qualificationSignals: z.record(z.string(), z.unknown()).nullable(),
1268
+ qualificationRubricKey: z.string().nullable()
1269
+ })
1270
+
1271
+ const ListRecordBaseSchema = z.object({
1272
+ id: z.string(),
1273
+ listId: z.string(),
1274
+ pipelineKey: z.string(),
1275
+ stageKey: z.string(),
1276
+ stateKey: z.string(),
1277
+ activityLog: z.unknown(),
1278
+ addedAt: z.string(),
1279
+ addedBy: z.string().nullable(),
1280
+ sourceExecutionId: z.string().nullable(),
1281
+ processingState: z.record(z.string(), z.unknown()).nullable(),
1282
+ enrichmentData: z.record(z.string(), z.unknown()).nullable()
1283
+ })
1284
+
1285
+ export const AcqListCompanyRecordRowSchema = ListRecordBaseSchema.extend({
1286
+ entity: z.literal('company'),
1287
+ companyId: z.string(),
1288
+ company: AcqListRecordCompanySummarySchema.nullable()
1289
+ })
1290
+
1291
+ export const AcqListContactRecordRowSchema = ListRecordBaseSchema.extend({
1292
+ entity: z.literal('contact'),
1293
+ contactId: z.string(),
1294
+ contact: AcqListRecordContactSummarySchema.nullable()
1295
+ })
1296
+
1297
+ export const ListRecordRowSchema = z.discriminatedUnion('entity', [
1298
+ AcqListCompanyRecordRowSchema,
1299
+ AcqListContactRecordRowSchema
1300
+ ])
1301
+
1302
+ export const ListRecordsResponseSchema = z.object({
1303
+ data: z.array(ListRecordRowSchema),
1304
+ total: z.number().int().min(0),
1305
+ limit: z.number().int().min(1),
1306
+ offset: z.number().int().min(0)
1307
+ })
1308
+
1309
+ // ---------------------------------------------------------------------------
1310
+ // Track B: List Companies API Schemas
1311
+ // ---------------------------------------------------------------------------
1312
+
1313
+ export const ListCompanyIdParamsSchema = z.object({
1314
+ listCompanyId: UuidSchema
1315
+ })
1316
+
1317
+ export const AcqListCompanyResponseSchema = z.object({
1318
+ id: z.string(),
1319
+ listId: z.string(),
1320
+ companyId: z.string(),
1321
+ pipelineKey: z.string(),
1322
+ stageKey: z.string(),
1323
+ stateKey: z.string(),
1324
+ activityLog: z.unknown(),
1325
+ addedAt: z.string(),
1326
+ addedBy: z.string().nullable(),
1327
+ sourceExecutionId: z.string().nullable()
1328
+ })
1329
+
1330
+ // ---------------------------------------------------------------------------
1331
+ // Track B: Transition request for list, list-member, and list-company substrate routes.
1332
+ // CRM deals use DealSchemas.TransitionItemRequest, which is catalog-backed.
1333
+ // ---------------------------------------------------------------------------
1334
+
1335
+ export const AcqCompanySchemas = {
1336
+ CompanyProcessingState: CompanyProcessingStateSchema,
1337
+ CompanyIdParams: CompanyIdParamsSchema,
1338
+ ListCompaniesQuery: ListCompaniesQuerySchema,
1339
+ CreateCompanyRequest: CreateCompanyRequestSchema,
1340
+ UpdateCompanyRequest: UpdateCompanyRequestSchema,
1341
+ AcqCompanyResponse: AcqCompanyResponseSchema,
1342
+ AcqCompanyListResponse: AcqCompanyListResponseSchema,
1343
+ AcqCompanyFacetsResponse: AcqCompanyFacetsResponseSchema
1344
+ }
1345
+
1346
+ export const AcqContactSchemas = {
1347
+ ContactProcessingState: ContactProcessingStateSchema,
1348
+ ContactIdParams: ContactIdParamsSchema,
1349
+ ListContactsQuery: ListContactsQuerySchema,
1350
+ CreateContactRequest: CreateContactRequestSchema,
1351
+ UpdateContactRequest: UpdateContactRequestSchema,
1352
+ AcqContactResponse: AcqContactResponseSchema,
1353
+ AcqContactListResponse: AcqContactListResponseSchema
1354
+ }
1355
+
1356
+ export type AcqCompanyStatus = z.infer<typeof AcqCompanyStatusSchema>
1357
+ export type AcqContactStatus = z.infer<typeof AcqContactStatusSchema>
1358
+ export type AcqEmailValid = z.infer<typeof AcqEmailValidSchema>
1359
+ export type CompanyIdParams = z.infer<typeof CompanyIdParamsSchema>
1360
+ export type ContactIdParams = z.infer<typeof ContactIdParamsSchema>
1361
+ export type ListCompaniesQuery = z.infer<typeof ListCompaniesQuerySchema>
1362
+ export type ListContactsQuery = z.infer<typeof ListContactsQuerySchema>
1363
+ export type CreateCompanyRequest = z.infer<typeof CreateCompanyRequestSchema>
1364
+ export type UpdateCompanyRequest = z.infer<typeof UpdateCompanyRequestSchema>
1365
+ export type CreateContactRequest = z.infer<typeof CreateContactRequestSchema>
1366
+ export type UpdateContactRequest = z.infer<typeof UpdateContactRequestSchema>
1367
+ export type AcqCompanyResponse = z.infer<typeof AcqCompanyResponseSchema>
1368
+ export type AcqCompanyListResponse = z.infer<typeof AcqCompanyListResponseSchema>
1369
+ export type AcqCompanyFacetsResponse = z.infer<typeof AcqCompanyFacetsResponseSchema>
1370
+ export type AcqContactCompanySummary = z.infer<typeof AcqContactCompanySummarySchema>
1371
+ export type AcqContactResponse = z.infer<typeof AcqContactResponseSchema>
1372
+ export type AcqContactListResponse = z.infer<typeof AcqContactListResponseSchema>
1373
+
1374
+ // ---------------------------------------------------------------------------
1375
+ // Bundled export
1376
+ // ---------------------------------------------------------------------------
1377
+
1378
+ export const AcqListSchemas = {
1379
+ // Params
1380
+ ListIdParams: ListIdParamsSchema,
1381
+
1382
+ // Primitives (for UI / tests)
1383
+ ListStatus: ListStatusSchema,
1384
+ ScrapingConfig: ScrapingConfigSchema,
1385
+ IcpRubric: IcpRubricSchema,
1386
+ PipelineConfig: PipelineConfigSchema,
1387
+ PipelineStage: PipelineStageSchema,
1388
+ BuildPlanSnapshot: BuildPlanSnapshotSchema,
1389
+ BuildPlanSnapshotStep: BuildPlanSnapshotStepSchema,
1390
+ AcqListMetadata: AcqListMetadataSchema,
1391
+ LeadGenStageKey: LeadGenStageKeySchema,
1392
+ LeadGenActionKey: LeadGenActionKeySchema,
1393
+ ProcessingStageStatus: ProcessingStageStatusSchema,
1394
+ ProcessingState: ProcessingStateSchema,
1395
+ ListStageCounts: ListStageCountsSchema,
1396
+ ListTelemetry: ListTelemetrySchema,
1397
+
1398
+ // Requests
1399
+ ListReadQuery: ListReadQuerySchema,
1400
+ GetListQuery: GetListQuerySchema,
1401
+ CreateListRequest: CreateListRequestSchema,
1402
+ UpdateListRequest: UpdateListRequestSchema,
1403
+ UpdateListStatusRequest: UpdateListStatusRequestSchema,
1404
+ UpdateListConfigRequest: UpdateListConfigRequestSchema,
1405
+ AddCompaniesToListRequest: AddCompaniesToListRequestSchema,
1406
+ RemoveCompaniesFromListRequest: RemoveCompaniesFromListRequestSchema,
1407
+ AddContactsToListRequest: AddContactsToListRequestSchema,
1408
+ RecordListExecutionRequest: RecordListExecutionRequestSchema,
1409
+
1410
+ // Responses
1411
+ AcqListResponse: AcqListResponseSchema,
1412
+ AcqListDetailResponse: AcqListDetailResponseSchema,
1413
+ AcqListListResponse: AcqListListResponseSchema,
1414
+ AcqListDealRef: AcqListDealRefSchema,
1415
+ AcqListLineage: AcqListLineageSchema,
1416
+ AcqListStatusResponse: AcqListStatusResponseSchema,
1417
+ ListTelemetryResponse: ListTelemetryResponseSchema,
1418
+ ListTelemetryListResponse: ListTelemetryListResponseSchema,
1419
+ ListExecutionsResponse: ListExecutionsResponseSchema,
1420
+ ListProgressResponse: ListProgressResponseSchema
1421
+ }
1422
+
1423
+ // ---------------------------------------------------------------------------
1424
+ // Track A/B bundled schemas
1425
+ // ---------------------------------------------------------------------------
1426
+
1427
+ export const AcqSubstrateSchemas = {
1428
+ // Artifacts
1429
+ ListArtifactsQuery: ListArtifactsQuerySchema,
1430
+ CreateArtifactRequest: CreateArtifactRequestSchema,
1431
+ AcqArtifactResponse: AcqArtifactResponseSchema,
1432
+ AcqArtifactListResponse: AcqArtifactListResponseSchema,
1433
+
1434
+ // List members
1435
+ ListMembersQuery: ListMembersQuerySchema,
1436
+ ListRecordsQuery: ListRecordsQuerySchema,
1437
+ MemberIdParams: MemberIdParamsSchema,
1438
+ AcqListMemberResponse: AcqListMemberResponseSchema,
1439
+ AcqListMembersResponse: AcqListMembersResponseSchema,
1440
+ AcqListCompanyRecordRow: AcqListCompanyRecordRowSchema,
1441
+ AcqListContactRecordRow: AcqListContactRecordRowSchema,
1442
+ ListRecordRow: ListRecordRowSchema,
1443
+ ListRecordsResponse: ListRecordsResponseSchema,
1444
+
1445
+ // List companies
1446
+ ListCompanyIdParams: ListCompanyIdParamsSchema,
1447
+ AcqListCompanyResponse: AcqListCompanyResponseSchema,
1448
+
1449
+ // Transition (generic stateful substrate)
1450
+ TransitionItemRequest: TransitionItemRequestSchema
1451
+ }
1452
+
1453
+ // ---------------------------------------------------------------------------
1454
+ // Inferred types
1455
+ // ---------------------------------------------------------------------------
1456
+
1457
+ // ---------------------------------------------------------------------------
1458
+ // Inferred types — Track A/B substrate
1459
+ // ---------------------------------------------------------------------------
1460
+
1461
+ export type AcqArtifactOwnerKind = z.infer<typeof AcqArtifactOwnerKindSchema>
1462
+ export type ListArtifactsQuery = z.infer<typeof ListArtifactsQuerySchema>
1463
+ export type CreateArtifactRequest = z.infer<typeof CreateArtifactRequestSchema>
1464
+ export type AcqArtifactResponse = z.infer<typeof AcqArtifactResponseSchema>
1465
+ export type AcqArtifactListResponse = z.infer<typeof AcqArtifactListResponseSchema>
1466
+ export type ListMembersQuery = z.infer<typeof ListMembersQuerySchema>
1467
+ export type ListRecordEntity = z.infer<typeof ListRecordEntitySchema>
1468
+ export type ListRecordsQuery = z.infer<typeof ListRecordsQuerySchema>
1469
+ export type MemberIdParams = z.infer<typeof MemberIdParamsSchema>
1470
+ export type AcqListMemberContactSummary = z.infer<typeof AcqListMemberContactSummarySchema>
1471
+ export type AcqListMemberResponse = z.infer<typeof AcqListMemberResponseSchema>
1472
+ export type AcqListMembersResponse = z.infer<typeof AcqListMembersResponseSchema>
1473
+ export type AcqListCompanyRecordRow = z.infer<typeof AcqListCompanyRecordRowSchema>
1474
+ export type AcqListContactRecordRow = z.infer<typeof AcqListContactRecordRowSchema>
1475
+ export type ListRecordRow = z.infer<typeof ListRecordRowSchema>
1476
+ export type ListRecordsResponse = z.infer<typeof ListRecordsResponseSchema>
1477
+ export type ListCompanyIdParams = z.infer<typeof ListCompanyIdParamsSchema>
1478
+ export type AcqListCompanyResponse = z.infer<typeof AcqListCompanyResponseSchema>
1479
+
1480
+ // ---------------------------------------------------------------------------
1481
+
1482
+ export type ListStatus = z.infer<typeof ListStatusSchema>
1483
+ export type ScrapingConfig = z.infer<typeof ScrapingConfigSchema>
1484
+ export type IcpRubric = z.infer<typeof IcpRubricSchema>
1485
+ export type PipelineStage = z.infer<typeof PipelineStageSchema>
1486
+ export type PipelineConfig = z.infer<typeof PipelineConfigSchema>
1487
+ export type BuildPlanSnapshotStep = z.infer<typeof BuildPlanSnapshotStepSchema>
1488
+ export type BuildPlanSnapshot = z.infer<typeof BuildPlanSnapshotSchema>
1489
+ export type AcqListMetadata = z.infer<typeof AcqListMetadataSchema>
1490
+ export type LeadGenActionKey = z.infer<typeof LeadGenActionKeySchema>
1491
+ export type ProcessingStageStatus = z.infer<typeof ProcessingStageStatusSchema>
1492
+ export type ListStageCountsInput = z.infer<typeof ListStageCountsSchema>['stageCounts']
1493
+ export type ListTelemetryInput = z.infer<typeof ListTelemetrySchema>
1494
+ export type ListIdParams = z.infer<typeof ListIdParamsSchema>
1495
+ export type ListReadQuery = z.infer<typeof ListReadQuerySchema>
1496
+ export type GetListQuery = z.infer<typeof GetListQuerySchema>
1497
+ export type CreateListRequest = z.infer<typeof CreateListRequestSchema>
1498
+ export type UpdateListRequest = z.infer<typeof UpdateListRequestSchema>
1499
+ export type UpdateListStatusRequest = z.infer<typeof UpdateListStatusRequestSchema>
1500
+ export type UpdateListConfigRequest = z.infer<typeof UpdateListConfigRequestSchema>
1501
+ export type AddCompaniesToListRequest = z.infer<typeof AddCompaniesToListRequestSchema>
1502
+ export type RemoveCompaniesFromListRequest = z.infer<typeof RemoveCompaniesFromListRequestSchema>
1503
+ export type AddContactsToListRequest = z.infer<typeof AddContactsToListRequestSchema>
1504
+ export type RecordListExecutionRequest = z.infer<typeof RecordListExecutionRequestSchema>
1505
+ export type AcqListResponse = z.infer<typeof AcqListResponseSchema>
1506
+ export type AcqListDetailResponse = z.infer<typeof AcqListDetailResponseSchema>
1507
+ export type AcqListListResponse = z.infer<typeof AcqListListResponseSchema>
1508
+ export type AcqListDealRef = z.infer<typeof AcqListDealRefSchema>
1509
+ export type AcqListLineage = z.infer<typeof AcqListLineageSchema>
1510
+ export type AcqListStatusResponse = z.infer<typeof AcqListStatusResponseSchema>
1511
+ export type ListTelemetryResponse = z.infer<typeof ListTelemetryResponseSchema>
1512
+ export type ListTelemetryListResponse = z.infer<typeof ListTelemetryListResponseSchema>
1513
+ export type ListExecutionSummaryInput = z.infer<typeof ListExecutionSummarySchema>
1514
+ export type ListExecutionsResponse = z.infer<typeof ListExecutionsResponseSchema>
1515
+ export type ListStageProgress = z.infer<typeof ListStageProgressSchema>
1516
+ export type ListProgress = z.infer<typeof ListProgressResponseSchema>