@elevasis/sdk 1.12.0 → 1.13.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.
@@ -806,3 +806,2615 @@ export interface DeploymentSpec {
806
806
  humanCheckpoints?: HumanCheckpointDefinition[]
807
807
  }
808
808
  ```
809
+
810
+ ## CRM Platform Primitives
811
+
812
+ ### `AcqCompany`
813
+
814
+ ```typescript
815
+ /**
816
+ * Company record in the acquisition database.
817
+ * Contains enriched company data from various sources.
818
+ * Transformed from AcqCompanyRow with camelCase properties.
819
+ */
820
+ export interface AcqCompany {
821
+ id: string
822
+ organizationId: string
823
+ name: string
824
+ domain: string | null
825
+ linkedinUrl: string | null
826
+ website: string | null
827
+ numEmployees: number | null
828
+ foundedYear: number | null
829
+ locationCity: string | null
830
+ locationState: string | null
831
+ category: string | null
832
+ categoryPain: string | null
833
+ segment: string | null
834
+ pipelineStatus: CompanyPipelineStatus | null
835
+ enrichmentData: CompanyEnrichmentData | null
836
+ source: string | null
837
+ batchId: string | null
838
+ status: 'active' | 'invalid'
839
+ verticalResearch: string | null
840
+ /** Track A: flat qualification score (null until a scoring rubric is defined). Added by W1 migration. */
841
+ qualificationScore: number | null
842
+ /** Track A: flat qualification signals jsonb preserving the result payload shape. Added by W1 migration. */
843
+ qualificationSignals: Record<string, unknown> | null
844
+ /** Track A: key identifying the rubric used for qualification. Added by W1 migration. */
845
+ qualificationRubricKey: string | null
846
+ createdAt: Date
847
+ updatedAt: Date
848
+ }
849
+ ```
850
+
851
+ ### `AcqContact`
852
+
853
+ ```typescript
854
+ /**
855
+ * Contact record in the acquisition database.
856
+ * Contains enriched contact data and personalization content.
857
+ * Transformed from AcqContactRow with camelCase properties.
858
+ */
859
+ export interface AcqContact {
860
+ id: string
861
+ organizationId: string
862
+ companyId: string | null
863
+ email: string
864
+ emailValid: 'VALID' | 'INVALID' | 'RISKY' | 'UNKNOWN' | null
865
+ firstName: string | null
866
+ lastName: string | null
867
+ linkedinUrl: string | null
868
+ title: string | null
869
+ headline: string | null
870
+ filterReason: string | null
871
+ openingLine: string | null
872
+ source: string | null
873
+ sourceId: string | null
874
+ pipelineStatus: ContactPipelineStatus | null
875
+ enrichmentData: ContactEnrichmentData | null
876
+ /** Attio Person record ID - set when contact responds and is added to CRM */
877
+ attioPersonId: string | null
878
+ batchId: string | null
879
+ status: 'active' | 'invalid'
880
+ createdAt: Date
881
+ updatedAt: Date
882
+ }
883
+ ```
884
+
885
+ ### `DealStage`
886
+
887
+ ```typescript
888
+ export type DealStage = 'interested' | 'proposal' | 'closing' | 'closed_won' | 'closed_lost' | 'nurturing'
889
+ ```
890
+
891
+ ### `KanbanStageConfig`
892
+
893
+ ```typescript
894
+ export interface KanbanStageConfig {
895
+ color: string // Mantine color token (e.g. 'blue', 'teal')
896
+ label?: string // Optional display label override
897
+ }
898
+ ```
899
+
900
+ ### `KanbanBoardConfig`
901
+
902
+ ```typescript
903
+ export type KanbanBoardConfig = Partial<Record<DealStage, KanbanStageConfig>>
904
+ ```
905
+
906
+ ### `DealContact`
907
+
908
+ ```typescript
909
+ export interface DealContact {
910
+ id: string
911
+ first_name: string | null
912
+ last_name: string | null
913
+ email: string
914
+ title: string | null
915
+ headline: string | null
916
+ linkedin_url: string | null
917
+ pipeline_status: Record<string, unknown> | null
918
+ enrichment_data: Record<string, unknown> | null
919
+ company: {
920
+ id: string
921
+ name: string
922
+ domain: string | null
923
+ website: string | null
924
+ linkedin_url: string | null
925
+ segment: string | null
926
+ category: string | null
927
+ num_employees: number | null
928
+ } | null
929
+ }
930
+ ```
931
+
932
+ ### `DealFilters`
933
+
934
+ ```typescript
935
+ export interface DealFilters {
936
+ stage?: DealStage
937
+ search?: string
938
+ limit?: number
939
+ offset?: number
940
+ }
941
+ ```
942
+
943
+ ### `DealListItem`
944
+
945
+ ```typescript
946
+ /** Deal list item with joined contact and company data */
947
+ export interface DealListItem extends AcqDealRow {
948
+ contact: DealContact | null
949
+ }
950
+ ```
951
+
952
+ ### `DealDetail`
953
+
954
+ ```typescript
955
+ export type DealDetail = DealListItem
956
+ ```
957
+
958
+ ### `AcqDealTaskKind`
959
+
960
+ ```typescript
961
+ /** Task kind options for a deal task (human follow-up action type) */
962
+ export type AcqDealTaskKind = 'call' | 'email' | 'meeting' | 'other'
963
+ ```
964
+
965
+ ### `AcqDealTask`
966
+
967
+ ```typescript
968
+ /**
969
+ * A CRM to-do item attached to a deal representing a human follow-up action.
970
+ * Transformed from AcqDealTaskRow with camelCase properties.
971
+ */
972
+ export interface AcqDealTask {
973
+ id: string
974
+ organizationId: string
975
+ dealId: string
976
+ title: string
977
+ description: string | null
978
+ kind: AcqDealTaskKind
979
+ dueAt: string | null
980
+ assigneeUserId: string | null
981
+ completedAt: string | null
982
+ completedByUserId: string | null
983
+ createdAt: string
984
+ updatedAt: string
985
+ createdByUserId: string | null
986
+ }
987
+ ```
988
+
989
+ ### `DealStageSchema`
990
+
991
+ ```typescript
992
+ export const DealStageSchema = z.enum(['interested', 'proposal', 'closing', 'closed_won', 'closed_lost', 'nurturing'])
993
+ ```
994
+
995
+ ### `AcqDealTaskKindSchema`
996
+
997
+ ```typescript
998
+ export const AcqDealTaskKindSchema = z.enum(['call', 'email', 'meeting', 'other'])
999
+ ```
1000
+
1001
+ ### `DealIdParamsSchema`
1002
+
1003
+ ```typescript
1004
+ export const DealIdParamsSchema = z.object({
1005
+ dealId: UuidSchema
1006
+ })
1007
+ ```
1008
+
1009
+ ### `DealTaskIdParamsSchema`
1010
+
1011
+ ```typescript
1012
+ export const DealTaskIdParamsSchema = z.object({
1013
+ dealId: UuidSchema,
1014
+ taskId: UuidSchema
1015
+ })
1016
+ ```
1017
+
1018
+ ### `ListDealsQuerySchema`
1019
+
1020
+ ```typescript
1021
+ export const ListDealsQuerySchema = z
1022
+ .object({
1023
+ stage: DealStageSchema.optional(),
1024
+ search: z.string().optional(),
1025
+ limit: z.coerce.number().int().positive().default(50),
1026
+ offset: z.coerce.number().int().min(0).default(0)
1027
+ })
1028
+ .strict()
1029
+ ```
1030
+
1031
+ ### `DealLookupQuerySchema`
1032
+
1033
+ ```typescript
1034
+ export const DealLookupQuerySchema = z
1035
+ .object({
1036
+ search: z.string().trim().min(1).max(200).optional(),
1037
+ limit: z.coerce.number().int().min(1).max(25).default(10)
1038
+ })
1039
+ .strict()
1040
+ ```
1041
+
1042
+ ### `ListDealTasksDueQuerySchema`
1043
+
1044
+ ```typescript
1045
+ export const ListDealTasksDueQuerySchema = z
1046
+ .object({
1047
+ window: z.enum(['overdue', 'today', 'today_and_overdue', 'upcoming']).optional(),
1048
+ assigneeUserId: UuidSchema.optional()
1049
+ })
1050
+ .strict()
1051
+ ```
1052
+
1053
+ ### `CreateDealNoteRequestSchema`
1054
+
1055
+ ```typescript
1056
+ export const CreateDealNoteRequestSchema = z
1057
+ .object({
1058
+ body: z.string().trim().min(1).max(10000)
1059
+ })
1060
+ .strict()
1061
+ ```
1062
+
1063
+ ### `CreateDealTaskRequestSchema`
1064
+
1065
+ ```typescript
1066
+ export const CreateDealTaskRequestSchema = z
1067
+ .object({
1068
+ title: z.string().trim().min(1).max(255),
1069
+ description: z.string().nullable().optional(),
1070
+ kind: AcqDealTaskKindSchema.optional(),
1071
+ dueAt: z.string().datetime().nullable().optional(),
1072
+ assigneeUserId: UuidSchema.nullable().optional()
1073
+ })
1074
+ .strict()
1075
+ ```
1076
+
1077
+ ### `TransitionItemRequestSchema`
1078
+
1079
+ ```typescript
1080
+ export const TransitionItemRequestSchema = z
1081
+ .object({
1082
+ pipelineKey: z.string().min(1),
1083
+ stageKey: z.string().min(1),
1084
+ stateKey: z.string().nullable().optional(),
1085
+ reason: z.string().optional(),
1086
+ expectedUpdatedAt: z.string().datetime().optional()
1087
+ })
1088
+ .strict()
1089
+ ```
1090
+
1091
+ ### `ExecuteActionParamsSchema`
1092
+
1093
+ ```typescript
1094
+ export const ExecuteActionParamsSchema = z
1095
+ .object({
1096
+ dealId: UuidSchema,
1097
+ actionKey: NonEmptyStringSchema
1098
+ })
1099
+ .strict()
1100
+ ```
1101
+
1102
+ ### `ExecuteActionRequestSchema`
1103
+
1104
+ ```typescript
1105
+ export const ExecuteActionRequestSchema = z
1106
+ .object({
1107
+ payload: z.record(z.string(), z.unknown()).optional()
1108
+ })
1109
+ .strict()
1110
+ ```
1111
+
1112
+ ### `DealContactSummarySchema`
1113
+
1114
+ ```typescript
1115
+ /**
1116
+ * Contact summary nested inside DealListItem / DealDetailResponse.
1117
+ * Matches the joined shape returned by useDeals / useDealDetail Supabase queries.
1118
+ */
1119
+ export const DealContactSummarySchema = z.object({
1120
+ id: z.string(),
1121
+ first_name: z.string().nullable(),
1122
+ last_name: z.string().nullable(),
1123
+ email: z.string(),
1124
+ title: z.string().nullable(),
1125
+ headline: z.string().nullable(),
1126
+ linkedin_url: z.string().nullable(),
1127
+ pipeline_status: z.record(z.string(), z.unknown()).nullable(),
1128
+ enrichment_data: z.record(z.string(), z.unknown()).nullable(),
1129
+ company: z
1130
+ .object({
1131
+ id: z.string(),
1132
+ name: z.string(),
1133
+ domain: z.string().nullable(),
1134
+ website: z.string().nullable(),
1135
+ linkedin_url: z.string().nullable(),
1136
+ segment: z.string().nullable(),
1137
+ category: z.string().nullable(),
1138
+ num_employees: z.number().nullable()
1139
+ })
1140
+ .nullable()
1141
+ })
1142
+ ```
1143
+
1144
+ ### `DealListItemSchema`
1145
+
1146
+ ```typescript
1147
+ /**
1148
+ * Deal list item with joined contact (and company via contact).
1149
+ * Matches DealListItem from @repo/core types.
1150
+ */
1151
+ export const DealListItemSchema = z.object({
1152
+ // acq_deals columns
1153
+ id: z.string(),
1154
+ organization_id: z.string(),
1155
+ contact_id: z.string().nullable(),
1156
+ contact_email: z.string(),
1157
+ pipeline_key: z.string(),
1158
+ stage_key: z.string().nullable(),
1159
+ state_key: z.string().nullable(),
1160
+ activity_log: z.unknown(),
1161
+ discovery_data: z.unknown().nullable(),
1162
+ discovery_submitted_at: z.string().nullable(),
1163
+ discovery_submitted_by: z.string().nullable(),
1164
+ proposal_data: z.unknown().nullable(),
1165
+ proposal_sent_at: z.string().nullable(),
1166
+ proposal_pdf_url: z.string().nullable(),
1167
+ signature_envelope_id: z.string().nullable(),
1168
+ source_list_id: z.string().nullable(),
1169
+ source_type: z.string().nullable(),
1170
+ initial_fee: z.number().nullable(),
1171
+ monthly_fee: z.number().nullable(),
1172
+ closed_lost_at: z.string().nullable(),
1173
+ closed_lost_reason: z.string().nullable(),
1174
+ created_at: z.string(),
1175
+ updated_at: z.string(),
1176
+ // joined relation
1177
+ contact: DealContactSummarySchema.nullable()
1178
+ })
1179
+ ```
1180
+
1181
+ ### `DealListResponseSchema`
1182
+
1183
+ ```typescript
1184
+ export const DealListResponseSchema = z.object({
1185
+ data: z.array(DealListItemSchema),
1186
+ total: z.number().int(),
1187
+ limit: z.number().int(),
1188
+ offset: z.number().int()
1189
+ })
1190
+ ```
1191
+
1192
+ ### `DealDetailResponseSchema`
1193
+
1194
+ ```typescript
1195
+ /**
1196
+ * Deal detail shape — currently the same as a list item (full joined record).
1197
+ * useDealDetail returns DealDetail which is typed as DealListItem.
1198
+ */
1199
+ export const DealDetailResponseSchema = DealListItemSchema
1200
+ ```
1201
+
1202
+ ### `DealNoteResponseSchema`
1203
+
1204
+ ```typescript
1205
+ /**
1206
+ * Single acq_deal_notes row (camelCase API representation).
1207
+ */
1208
+ export const DealNoteResponseSchema = z.object({
1209
+ id: z.string(),
1210
+ dealId: z.string(),
1211
+ organizationId: z.string(),
1212
+ authorUserId: z.string().nullable(),
1213
+ body: z.string(),
1214
+ createdAt: z.string(),
1215
+ updatedAt: z.string()
1216
+ })
1217
+ ```
1218
+
1219
+ ### `DealNoteListResponseSchema`
1220
+
1221
+ ```typescript
1222
+ export const DealNoteListResponseSchema = z.array(DealNoteResponseSchema)
1223
+ ```
1224
+
1225
+ ### `DealTaskResponseSchema`
1226
+
1227
+ ```typescript
1228
+ /**
1229
+ * Single acq_deal_tasks row (camelCase API representation).
1230
+ * Matches AcqDealTask domain type from types.ts.
1231
+ */
1232
+ export const DealTaskResponseSchema = z.object({
1233
+ id: z.string(),
1234
+ organizationId: z.string(),
1235
+ dealId: z.string(),
1236
+ title: z.string(),
1237
+ description: z.string().nullable(),
1238
+ kind: AcqDealTaskKindSchema,
1239
+ dueAt: z.string().nullable(),
1240
+ assigneeUserId: z.string().nullable(),
1241
+ completedAt: z.string().nullable(),
1242
+ completedByUserId: z.string().nullable(),
1243
+ createdAt: z.string(),
1244
+ updatedAt: z.string(),
1245
+ createdByUserId: z.string().nullable()
1246
+ })
1247
+ ```
1248
+
1249
+ ### `DealTaskListResponseSchema`
1250
+
1251
+ ```typescript
1252
+ export const DealTaskListResponseSchema = z.array(DealTaskResponseSchema)
1253
+ ```
1254
+
1255
+ ### `DealSchemas`
1256
+
1257
+ ```typescript
1258
+ export const DealSchemas = {
1259
+ // Params
1260
+ DealIdParams: DealIdParamsSchema,
1261
+ DealTaskIdParams: DealTaskIdParamsSchema,
1262
+
1263
+ // Queries
1264
+ ListDealsQuery: ListDealsQuerySchema,
1265
+ DealLookupQuery: DealLookupQuerySchema,
1266
+ ListDealTasksDueQuery: ListDealTasksDueQuerySchema,
1267
+
1268
+ // Request bodies
1269
+ CreateDealNoteRequest: CreateDealNoteRequestSchema,
1270
+ CreateDealTaskRequest: CreateDealTaskRequestSchema,
1271
+ TransitionItemRequest: TransitionItemRequestSchema,
1272
+ ExecuteActionParams: ExecuteActionParamsSchema,
1273
+ ExecuteActionRequest: ExecuteActionRequestSchema,
1274
+
1275
+ // Responses
1276
+ DealListResponse: DealListResponseSchema,
1277
+ DealSummaryResponse: DealSummaryResponseSchema,
1278
+ DealLookupResponse: DealLookupResponseSchema,
1279
+ DealDetailResponse: DealDetailResponseSchema,
1280
+ DealNoteResponse: DealNoteResponseSchema,
1281
+ DealNoteListResponse: DealNoteListResponseSchema,
1282
+ DealTaskResponse: DealTaskResponseSchema,
1283
+ DealTaskListResponse: DealTaskListResponseSchema
1284
+ }
1285
+ ```
1286
+
1287
+ ### `Action`
1288
+
1289
+ ```typescript
1290
+ export interface Action {
1291
+ key: string
1292
+ label: string
1293
+ payloadSchema?: z.ZodTypeAny
1294
+ }
1295
+ ```
1296
+
1297
+ ### `ActionDef`
1298
+
1299
+ ```typescript
1300
+ export interface ActionDef {
1301
+ key: string
1302
+ label: string
1303
+ isAvailableFor: (deal: AcqDealRow) => boolean
1304
+ workflowId: string
1305
+ payloadSchema?: z.ZodTypeAny
1306
+ }
1307
+ ```
1308
+
1309
+ ### `DEFAULT_CRM_ACTIONS`
1310
+
1311
+ ```typescript
1312
+ export const DEFAULT_CRM_ACTIONS: ActionDef[] = [
1313
+ {
1314
+ key: 'move_to_proposal',
1315
+ label: 'Move to Proposal',
1316
+ isAvailableFor: (deal) => deal.stage_key === 'interested',
1317
+ workflowId: 'move_to_proposal-workflow'
1318
+ },
1319
+ {
1320
+ key: 'move_to_closing',
1321
+ label: 'Move to Closing',
1322
+ isAvailableFor: (deal) => deal.stage_key === 'proposal',
1323
+ workflowId: 'move_to_closing-workflow'
1324
+ },
1325
+ {
1326
+ key: 'move_to_closed_won',
1327
+ label: 'Close Won',
1328
+ isAvailableFor: (deal) => deal.stage_key === 'closing',
1329
+ workflowId: 'move_to_closed_won-workflow'
1330
+ },
1331
+ {
1332
+ key: 'move_to_closed_lost',
1333
+ label: 'Close Lost',
1334
+ isAvailableFor: (deal) =>
1335
+ deal.stage_key === 'interested' || deal.stage_key === 'proposal' || deal.stage_key === 'closing',
1336
+ workflowId: 'move_to_closed_lost-workflow'
1337
+ },
1338
+ {
1339
+ key: 'move_to_nurturing',
1340
+ label: 'Move to Nurturing',
1341
+ isAvailableFor: (deal) =>
1342
+ deal.stage_key === 'interested' || deal.stage_key === 'proposal' || deal.stage_key === 'closing',
1343
+ workflowId: 'move_to_nurturing-workflow'
1344
+ },
1345
+ {
1346
+ key: 'send_link',
1347
+ label: 'Send Booking Link',
1348
+ isAvailableFor: (deal) => deal.stage_key === 'interested' && deal.state_key === 'discovery_replied',
1349
+ workflowId: 'crm-send-booking-link-workflow'
1350
+ },
1351
+ {
1352
+ key: 'send_nudge',
1353
+ label: 'Send Nudge',
1354
+ isAvailableFor: (deal) =>
1355
+ deal.stage_key === 'interested' &&
1356
+ (deal.state_key === 'discovery_link_sent' || deal.state_key === 'discovery_nudging'),
1357
+ workflowId: 'crm-send-nudge-workflow'
1358
+ },
1359
+ {
1360
+ key: 'mark_no_show',
1361
+ label: 'Mark No-Show',
1362
+ isAvailableFor: (deal) => deal.stage_key === 'interested' && deal.state_key === 'discovery_nudging',
1363
+ // Mirrors the auto-timeout precedent in operations/sales/crm/pipeline/timeout-actions.ts:
1364
+ // both manual-click and timeout move the deal to closed_lost. The action_taken activity
1365
+ // event captures operator intent and distinguishes the manual variant from the timed one.
1366
+ workflowId: 'mark_no_show-workflow'
1367
+ },
1368
+ {
1369
+ key: 'rebook',
1370
+ label: 'Rebook',
1371
+ isAvailableFor: (deal) => deal.stage_key === 'interested' && deal.state_key === 'discovery_booking_cancelled',
1372
+ workflowId: 'crm-rebook-workflow'
1373
+ }
1374
+ ]
1375
+ ```
1376
+
1377
+ ### `CrmToolMap`
1378
+
1379
+ ```typescript
1380
+ export type CrmToolMap = {
1381
+ getRecentActivity: { params: CrmRecentActivityParams; result: RecentActivityEntry[] }
1382
+ listDeals: { params: CrmListDealsParams; result: DealListItem[] }
1383
+ getDeal: { params: CrmGetDealParams; result: DealDetail | null }
1384
+ getDealByEmail: { params: CrmGetDealByEmailParams; result: DealDetail | null }
1385
+ createDealNote: { params: CrmDealNoteParams; result: AcqDealNote }
1386
+ listDealNotes: { params: Omit<ListDealNotesParams, 'organizationId'>; result: AcqDealNote[] }
1387
+ createDealTask: { params: CrmDealTaskParams; result: AcqDealTask }
1388
+ listDealTasks: { params: Omit<ListDealTasksParams, 'organizationId'>; result: AcqDealTask[] }
1389
+ listDealTasksDue: { params: CrmTaskDueParams; result: AcqDealTask[] }
1390
+ completeDealTask: { params: Omit<CompleteDealTaskParams, 'organizationId'>; result: AcqDealTask }
1391
+ recordActivity: { params: CrmRecordActivityParams; result: void }
1392
+ deleteDeal: { params: CrmDeleteDealParams; result: void }
1393
+ }
1394
+ ```
1395
+
1396
+ ## Lead Gen Platform Primitives
1397
+
1398
+ ### `WebPost`
1399
+
1400
+ ```typescript
1401
+ /**
1402
+ * Represents a web post from company website scraping.
1403
+ * Used for recent blog posts, news, or announcements.
1404
+ */
1405
+ export interface WebPost {
1406
+ /** ISO date string of when the post was published */
1407
+ date: string
1408
+ /** Title of the web post */
1409
+ title: string
1410
+ /** Brief summary of the post content */
1411
+ summary: string
1412
+ /** AI-generated insight about the post's relevance */
1413
+ aiInsight?: string
1414
+ }
1415
+ ```
1416
+
1417
+ ### `CompanyPipelineStatus`
1418
+
1419
+ ```typescript
1420
+ /**
1421
+ * Tracks pipeline status for a company across all processing stages.
1422
+ */
1423
+ export interface CompanyPipelineStatus {
1424
+ acquired: boolean
1425
+ enrichment: {
1426
+ [source: string]: {
1427
+ status: 'pending' | 'complete' | 'failed' | 'skipped'
1428
+ completedAt?: string
1429
+ error?: string
1430
+ }
1431
+ }
1432
+ }
1433
+ ```
1434
+
1435
+ ### `ContactPipelineStatus`
1436
+
1437
+ ```typescript
1438
+ /**
1439
+ * Tracks pipeline status for a contact across all processing stages.
1440
+ */
1441
+ export interface ContactPipelineStatus {
1442
+ enrichment: {
1443
+ [source: string]: {
1444
+ status: 'pending' | 'complete' | 'failed' | 'skipped'
1445
+ completedAt?: string
1446
+ error?: string
1447
+ }
1448
+ }
1449
+ personalization: {
1450
+ status: 'pending' | 'complete' | 'failed' | 'skipped'
1451
+ completedAt?: string
1452
+ }
1453
+ outreach: {
1454
+ status: 'pending' | 'sent' | 'replied' | 'bounced' | 'opted-out'
1455
+ sentAt?: string
1456
+ channel?: string
1457
+ campaignId?: string
1458
+ }
1459
+ }
1460
+ ```
1461
+
1462
+ ### `CompanyEnrichmentData`
1463
+
1464
+ ```typescript
1465
+ /**
1466
+ * Enrichment data collected for a company from various sources.
1467
+ */
1468
+ export interface CompanyEnrichmentData {
1469
+ googleMaps?: {
1470
+ placeId?: string
1471
+ totalScore?: number
1472
+ reviewsCount?: number
1473
+ address?: string
1474
+ phone?: string
1475
+ categoryName?: string
1476
+ googleMapsUrl?: string
1477
+ scrapedAt?: string
1478
+ }
1479
+ websiteCrawl?: {
1480
+ companyDescription?: string
1481
+ services?: string[]
1482
+ specialties?: string[]
1483
+ staff?: Array<{ name: string; title?: string; email?: string }>
1484
+ automationGaps?: string[]
1485
+ targetAudience?: string
1486
+ category?: string
1487
+ segment?: string
1488
+ recentWin?: string
1489
+ emailCount?: number
1490
+ pageCount?: number
1491
+ totalChars?: number
1492
+ crawledAt?: string
1493
+ extractedAt?: string
1494
+ }
1495
+ website?: {
1496
+ missionVision?: string
1497
+ uniqueAttributes?: string
1498
+ coreOfferings?: string
1499
+ targetAudience?: string
1500
+ companyValues?: string
1501
+ businessDescription?: string
1502
+ recentPosts?: Array<{ date?: string; title?: string; summary?: string; aiInsight?: string }>
1503
+ }
1504
+ tomba?: {
1505
+ waterfallEmail?: {
1506
+ email: string
1507
+ name?: string
1508
+ title?: string
1509
+ department?: string
1510
+ } | null
1511
+ genericEmail?: string | null
1512
+ totalFound?: number
1513
+ searchedAt?: string
1514
+ }
1515
+ }
1516
+ ```
1517
+
1518
+ ### `ContactEnrichmentData`
1519
+
1520
+ ```typescript
1521
+ /**
1522
+ * Enrichment data collected for a contact from various sources.
1523
+ */
1524
+ export interface ContactEnrichmentData {
1525
+ linkedin?: {
1526
+ summary?: string
1527
+ pastExperience?: string
1528
+ education?: string
1529
+ activity?: Array<{ date?: string; content?: string }>
1530
+ }
1531
+ }
1532
+ ```
1533
+
1534
+ ### `AcqList`
1535
+
1536
+ ```typescript
1537
+ /**
1538
+ * Acquisition list for organizing contacts and companies.
1539
+ * Transformed from AcqListRow with camelCase properties.
1540
+ *
1541
+ * Track B: acq_lists adopts the Stateful trait (pipeline_key / stage_key / state_key / activity_log).
1542
+ * `status` is preserved as a convenience alias for `state_key` so existing consumers continue to
1543
+ * compile until they are migrated to read the trait fields directly.
1544
+ */
1545
+ export interface AcqList {
1546
+ id: string
1547
+ organizationId: string
1548
+ name: string
1549
+ description: string | null
1550
+ type: string
1551
+ batchIds: string[]
1552
+ instantlyCampaignId: string | null
1553
+ /** @deprecated Use state_key. Retained as alias mapped from state_key for backward-compatibility. */
1554
+ status: string
1555
+ /** Stateful trait: pipeline identifier (always 'lead-gen' for acq_lists). Added by W2 migration. */
1556
+ pipelineKey: string
1557
+ /** Stateful trait: stage within the pipeline (always 'lifecycle' for acq_lists). Added by W2 migration. */
1558
+ stageKey: string
1559
+ /** Stateful trait: lifecycle state (draft | enriching | launched | closing | archived). Added by W2 migration. */
1560
+ stateKey: string
1561
+ metadata: Record<string, unknown>
1562
+ launchedAt: Date | null
1563
+ completedAt: Date | null
1564
+ createdAt: Date
1565
+ config: ListConfig
1566
+ }
1567
+ ```
1568
+
1569
+ ### `AcqCompany`
1570
+
1571
+ ```typescript
1572
+ /**
1573
+ * Company record in the acquisition database.
1574
+ * Contains enriched company data from various sources.
1575
+ * Transformed from AcqCompanyRow with camelCase properties.
1576
+ */
1577
+ export interface AcqCompany {
1578
+ id: string
1579
+ organizationId: string
1580
+ name: string
1581
+ domain: string | null
1582
+ linkedinUrl: string | null
1583
+ website: string | null
1584
+ numEmployees: number | null
1585
+ foundedYear: number | null
1586
+ locationCity: string | null
1587
+ locationState: string | null
1588
+ category: string | null
1589
+ categoryPain: string | null
1590
+ segment: string | null
1591
+ pipelineStatus: CompanyPipelineStatus | null
1592
+ enrichmentData: CompanyEnrichmentData | null
1593
+ source: string | null
1594
+ batchId: string | null
1595
+ status: 'active' | 'invalid'
1596
+ verticalResearch: string | null
1597
+ /** Track A: flat qualification score (null until a scoring rubric is defined). Added by W1 migration. */
1598
+ qualificationScore: number | null
1599
+ /** Track A: flat qualification signals jsonb preserving the result payload shape. Added by W1 migration. */
1600
+ qualificationSignals: Record<string, unknown> | null
1601
+ /** Track A: key identifying the rubric used for qualification. Added by W1 migration. */
1602
+ qualificationRubricKey: string | null
1603
+ createdAt: Date
1604
+ updatedAt: Date
1605
+ }
1606
+ ```
1607
+
1608
+ ### `AcqContact`
1609
+
1610
+ ```typescript
1611
+ /**
1612
+ * Contact record in the acquisition database.
1613
+ * Contains enriched contact data and personalization content.
1614
+ * Transformed from AcqContactRow with camelCase properties.
1615
+ */
1616
+ export interface AcqContact {
1617
+ id: string
1618
+ organizationId: string
1619
+ companyId: string | null
1620
+ email: string
1621
+ emailValid: 'VALID' | 'INVALID' | 'RISKY' | 'UNKNOWN' | null
1622
+ firstName: string | null
1623
+ lastName: string | null
1624
+ linkedinUrl: string | null
1625
+ title: string | null
1626
+ headline: string | null
1627
+ filterReason: string | null
1628
+ openingLine: string | null
1629
+ source: string | null
1630
+ sourceId: string | null
1631
+ pipelineStatus: ContactPipelineStatus | null
1632
+ enrichmentData: ContactEnrichmentData | null
1633
+ /** Attio Person record ID - set when contact responds and is added to CRM */
1634
+ attioPersonId: string | null
1635
+ batchId: string | null
1636
+ status: 'active' | 'invalid'
1637
+ createdAt: Date
1638
+ updatedAt: Date
1639
+ }
1640
+ ```
1641
+
1642
+ ### `PipelineStep`
1643
+
1644
+ ```typescript
1645
+ /**
1646
+ * One ordered step in a list's pipeline. Maps to a deployed workflow
1647
+ * (e.g. 'lgn-03-company-qualification-workflow'). The `inputTemplate`
1648
+ * is merged with `{ listId }` at run time to form the workflow input.
1649
+ */
1650
+ export interface PipelineStep {
1651
+ /** Stable key, e.g. 'scrape' | 'extract' | 'qualify' | 'discover' | 'verify' | 'personalize'. */
1652
+ key: string
1653
+ /** Human label rendered in the UI stepper. */
1654
+ label: string
1655
+ /** Deployed workflow resourceId (e.g. 'lgn-03-company-qualification-workflow'). */
1656
+ resourceId: string
1657
+ /** Input defaults merged with `{ listId }` at dispatch. */
1658
+ inputTemplate: Record<string, unknown>
1659
+ /** Whether the UI shows the Run button. */
1660
+ enabled: boolean
1661
+ /** Display order (ascending). */
1662
+ order: number
1663
+ }
1664
+ ```
1665
+
1666
+ ### `CompanyListStage`
1667
+
1668
+ ```typescript
1669
+ export type CompanyListStage = 'populated' | 'extracted' | 'qualified'
1670
+ ```
1671
+
1672
+ ### `ContactListStage`
1673
+
1674
+ ```typescript
1675
+ export type ContactListStage = 'discovered' | 'verified' | 'personalized' | 'uploaded'
1676
+ ```
1677
+
1678
+ ### `ListConfig`
1679
+
1680
+ ```typescript
1681
+ /**
1682
+ * Per-list pipeline configuration stored as jsonb in `acq_lists.config`.
1683
+ *
1684
+ * `qualification` is the only required subtree. Every other subtree is optional
1685
+ * and inherits global defaults when omitted: workflows resolve values as
1686
+ * `list.config.foo ?? globalDefaults.foo`. Seeded rows from
1687
+ * `20260413000100_backfill_list_configs.sql` only populate `qualification`
1688
+ * and `scraping`; the rest was intentionally omitted.
1689
+ */
1690
+ export interface ListConfig {
1691
+ qualification: {
1692
+ /** One-line description of the target vertical/segment. */
1693
+ targetDescription: string
1694
+ /** Minimum Google review count to qualify. */
1695
+ minReviewCount: number
1696
+ /** Minimum Google star rating to qualify (e.g. 3.0). */
1697
+ minRating: number
1698
+ /** Whether to exclude franchises/chains during qualification. */
1699
+ excludeFranchises: boolean
1700
+ /** Free-form LLM rules layered on top of the structured criteria. */
1701
+ customRules: string
1702
+ /**
1703
+ * Free-form text key identifying the ICP qualification rubric applied to this list.
1704
+ * Decision C4: stays as free-form text until an Org OS rubric registry lands.
1705
+ */
1706
+ qualificationRubricKey?: string | null
1707
+ }
1708
+ enrichment?: {
1709
+ emailDiscovery?: {
1710
+ primary: 'tomba' | 'anymailfinder'
1711
+ credentialName?: string
1712
+ }
1713
+ emailVerification?: {
1714
+ provider: 'millionverifier'
1715
+ threshold?: 'ok' | 'ok+catch_all'
1716
+ }
1717
+ }
1718
+ personalization?: {
1719
+ industryContext?: string
1720
+ /** Email body template with tags like {{opening_line}} / {{category_pain}}. */
1721
+ emailBody?: string
1722
+ creativeDirection?: string
1723
+ /** Contradiction-prevention rules layered into the personalization prompt. */
1724
+ exclusionRules?: string[]
1725
+ }
1726
+ pipeline?: {
1727
+ steps: PipelineStep[]
1728
+ }
1729
+ }
1730
+ ```
1731
+
1732
+ ### `ListTelemetry`
1733
+
1734
+ ```typescript
1735
+ /**
1736
+ * Live-scan aggregate telemetry for a single list, computed on demand from
1737
+ * the list junction tables and current contact deliverability state.
1738
+ */
1739
+ export interface ListTelemetry {
1740
+ listId: string
1741
+ totalCompanies: number
1742
+ totalContacts: number
1743
+ stageCounts: {
1744
+ populated: number
1745
+ extracted: number
1746
+ qualified: number
1747
+ discovered: number
1748
+ verified: number
1749
+ personalized: number
1750
+ uploaded: number
1751
+ }
1752
+ deliverability: {
1753
+ valid: number
1754
+ risky: number
1755
+ invalid: number
1756
+ unknown: number
1757
+ bounced: number
1758
+ }
1759
+ /** Reserved -- active workflow IDs associated with this list. */
1760
+ activeWorkflows?: string[]
1761
+ }
1762
+ ```
1763
+
1764
+ ### `ListQualificationSchema`
1765
+
1766
+ ```typescript
1767
+ export const ListQualificationSchema = z.object({
1768
+ targetDescription: z.string(),
1769
+ minReviewCount: z.number().int().min(0),
1770
+ minRating: z.number().min(0).max(5),
1771
+ excludeFranchises: z.boolean(),
1772
+ customRules: z.string(),
1773
+ /**
1774
+ * Free-form text key identifying the ICP rubric applied to this list.
1775
+ * Decision C4: stays free-form until an Org OS rubric registry lands.
1776
+ */
1777
+ qualificationRubricKey: z.string().trim().max(255).nullish()
1778
+ })
1779
+ ```
1780
+
1781
+ ### `ListEnrichmentSchema`
1782
+
1783
+ ```typescript
1784
+ export const ListEnrichmentSchema = z.object({
1785
+ emailDiscovery: z
1786
+ .object({
1787
+ primary: z.enum(['tomba', 'anymailfinder']),
1788
+ credentialName: z.string().optional()
1789
+ })
1790
+ .optional(),
1791
+ emailVerification: z
1792
+ .object({
1793
+ provider: z.literal('millionverifier'),
1794
+ threshold: z.enum(['ok', 'ok+catch_all']).optional()
1795
+ })
1796
+ .optional()
1797
+ })
1798
+ ```
1799
+
1800
+ ### `ListPersonalizationSchema`
1801
+
1802
+ ```typescript
1803
+ export const ListPersonalizationSchema = z.object({
1804
+ industryContext: z.string().optional(),
1805
+ emailBody: z.string().optional(),
1806
+ creativeDirection: z.string().optional(),
1807
+ exclusionRules: z.array(z.string()).optional()
1808
+ })
1809
+ ```
1810
+
1811
+ ### `PipelineStepSchema`
1812
+
1813
+ ```typescript
1814
+ export const PipelineStepSchema = z.object({
1815
+ key: z.string(),
1816
+ label: z.string(),
1817
+ resourceId: z.string(),
1818
+ inputTemplate: z.record(z.string(), z.unknown()),
1819
+ enabled: z.boolean(),
1820
+ order: z.number().int()
1821
+ })
1822
+ ```
1823
+
1824
+ ### `ListPipelineSchema`
1825
+
1826
+ ```typescript
1827
+ export const ListPipelineSchema = z.object({
1828
+ steps: z.array(PipelineStepSchema)
1829
+ })
1830
+ ```
1831
+
1832
+ ### `ListConfigSchema`
1833
+
1834
+ ```typescript
1835
+ /**
1836
+ * Full ListConfig shape. `qualification` is required; everything else optional.
1837
+ * Matches `acq_lists.config` jsonb and ListConfig type in types.ts.
1838
+ */
1839
+ export const ListConfigSchema = z.object({
1840
+ qualification: ListQualificationSchema,
1841
+ enrichment: ListEnrichmentSchema.optional(),
1842
+ personalization: ListPersonalizationSchema.optional(),
1843
+ pipeline: ListPipelineSchema.optional()
1844
+ })
1845
+ ```
1846
+
1847
+ ### `ListStageCountsSchema`
1848
+
1849
+ ```typescript
1850
+ export const ListStageCountsSchema = z.object({
1851
+ stageCounts: z.object({
1852
+ populated: z.number().int(),
1853
+ extracted: z.number().int(),
1854
+ qualified: z.number().int(),
1855
+ discovered: z.number().int(),
1856
+ verified: z.number().int(),
1857
+ personalized: z.number().int(),
1858
+ uploaded: z.number().int()
1859
+ }),
1860
+ deliverability: z.object({
1861
+ valid: z.number().int(),
1862
+ risky: z.number().int(),
1863
+ invalid: z.number().int(),
1864
+ unknown: z.number().int(),
1865
+ bounced: z.number().int()
1866
+ })
1867
+ })
1868
+ ```
1869
+
1870
+ ### `ListTelemetrySchema`
1871
+
1872
+ ```typescript
1873
+ export const ListTelemetrySchema = z.object({
1874
+ listId: UuidSchema,
1875
+ totalCompanies: z.number().int(),
1876
+ totalContacts: z.number().int(),
1877
+ stageCounts: ListStageCountsSchema.shape.stageCounts,
1878
+ deliverability: ListStageCountsSchema.shape.deliverability,
1879
+ activeWorkflows: z.array(z.string()).optional()
1880
+ })
1881
+ ```
1882
+
1883
+ ### `ListIdParamsSchema`
1884
+
1885
+ ```typescript
1886
+ export const ListIdParamsSchema = z.object({
1887
+ listId: UuidSchema
1888
+ })
1889
+ ```
1890
+
1891
+ ### `CreateListRequestSchema`
1892
+
1893
+ ```typescript
1894
+ export const CreateListRequestSchema = z
1895
+ .object({
1896
+ name: z.string().trim().min(1).max(255),
1897
+ description: z.string().trim().nullable().optional(),
1898
+ type: z.string().default('manual'),
1899
+ /** Stateful trait pipeline key. Defaults to 'lead-gen'. */
1900
+ pipelineKey: z.string().trim().min(1).max(100).optional(),
1901
+ config: ListConfigSchema.optional()
1902
+ })
1903
+ .strict()
1904
+ ```
1905
+
1906
+ ### `UpdateListRequestSchema`
1907
+
1908
+ ```typescript
1909
+ export const UpdateListRequestSchema = z
1910
+ .object({
1911
+ name: z.string().trim().min(1).max(255).optional(),
1912
+ description: z.string().trim().nullable().optional(),
1913
+ status: z.string().optional(),
1914
+ batchIds: z.array(z.string()).optional()
1915
+ })
1916
+ .strict()
1917
+ .refine(
1918
+ (data) =>
1919
+ data.name !== undefined ||
1920
+ data.description !== undefined ||
1921
+ data.status !== undefined ||
1922
+ data.batchIds !== undefined,
1923
+ {
1924
+ message: 'At least one field (name, description, status, or batchIds) must be provided'
1925
+ }
1926
+ )
1927
+ ```
1928
+
1929
+ ### `UpdateListConfigRequestSchema`
1930
+
1931
+ ```typescript
1932
+ /**
1933
+ * Partial patch for list.config — UI sends only the edited tab's subtree.
1934
+ * Zod v4: use .partial() on each subtree and remake the root schema.
1935
+ * Since the root ListConfigSchema marks `qualification` as required, we must
1936
+ * produce a manually-built deep-partial that makes qualification optional too.
1937
+ */
1938
+ export const UpdateListConfigRequestSchema = z
1939
+ .object({
1940
+ qualification: ListQualificationSchema.partial().optional(),
1941
+ enrichment: ListEnrichmentSchema.partial().optional(),
1942
+ personalization: ListPersonalizationSchema.partial().optional(),
1943
+ pipeline: ListPipelineSchema.partial().optional()
1944
+ })
1945
+ .strict()
1946
+ ```
1947
+
1948
+ ### `AddCompaniesToListRequestSchema`
1949
+
1950
+ ```typescript
1951
+ export const AddCompaniesToListRequestSchema = z
1952
+ .object({
1953
+ companyIds: z.array(UuidSchema).min(1).max(1000)
1954
+ })
1955
+ .strict()
1956
+ ```
1957
+
1958
+ ### `RemoveCompaniesFromListRequestSchema`
1959
+
1960
+ ```typescript
1961
+ export const RemoveCompaniesFromListRequestSchema = z
1962
+ .object({
1963
+ companyIds: z.array(UuidSchema).min(1).max(1000)
1964
+ })
1965
+ .strict()
1966
+ ```
1967
+
1968
+ ### `AddContactsToListRequestSchema`
1969
+
1970
+ ```typescript
1971
+ export const AddContactsToListRequestSchema = z
1972
+ .object({
1973
+ contactIds: z.array(UuidSchema).min(1).max(1000)
1974
+ })
1975
+ .strict()
1976
+ ```
1977
+
1978
+ ### `RecordListExecutionRequestSchema`
1979
+
1980
+ ```typescript
1981
+ export const RecordListExecutionRequestSchema = z
1982
+ .object({
1983
+ executionId: UuidSchema,
1984
+ configSnapshot: z.record(z.string(), z.unknown()).optional()
1985
+ })
1986
+ .strict()
1987
+ ```
1988
+
1989
+ ### `AcqListResponseSchema`
1990
+
1991
+ ```typescript
1992
+ /**
1993
+ * Single list as returned by /api/acquisition/lists/:id etc.
1994
+ * Camel-cased domain shape matching AcqList in types.ts.
1995
+ */
1996
+ export const AcqListResponseSchema = z.object({
1997
+ id: z.string(),
1998
+ organizationId: z.string(),
1999
+ name: z.string(),
2000
+ description: z.string().nullable(),
2001
+ type: z.string(),
2002
+ batchIds: z.array(z.string()),
2003
+ instantlyCampaignId: z.string().nullable(),
2004
+ /** @deprecated Alias for stateKey — retained for backward compatibility. */
2005
+ status: z.string(),
2006
+ /** Stateful trait: pipeline identifier (always 'lead-gen'). Added by W2 migration. */
2007
+ pipelineKey: z.string(),
2008
+ /** Stateful trait: stage within the pipeline (always 'lifecycle'). Added by W2 migration. */
2009
+ stageKey: z.string(),
2010
+ /** Stateful trait: lifecycle state (draft | enriching | launched | closing | archived). */
2011
+ stateKey: z.string(),
2012
+ metadata: z.record(z.string(), z.unknown()),
2013
+ launchedAt: z.string().nullable(),
2014
+ completedAt: z.string().nullable(),
2015
+ createdAt: z.string(),
2016
+ config: ListConfigSchema
2017
+ })
2018
+ ```
2019
+
2020
+ ### `AcqListListResponseSchema`
2021
+
2022
+ ```typescript
2023
+ export const AcqListListResponseSchema = z.array(AcqListResponseSchema)
2024
+ ```
2025
+
2026
+ ### `ListTelemetryResponseSchema`
2027
+
2028
+ ```typescript
2029
+ export const ListTelemetryResponseSchema = ListTelemetrySchema
2030
+ ```
2031
+
2032
+ ### `ListTelemetryListResponseSchema`
2033
+
2034
+ ```typescript
2035
+ export const ListTelemetryListResponseSchema = z.array(ListTelemetrySchema)
2036
+ ```
2037
+
2038
+ ### `ListExecutionSummarySchema`
2039
+
2040
+ ```typescript
2041
+ /**
2042
+ * Row from acq_list_executions joined with the execution summary,
2043
+ * shaped for the /lists/:id/executions response.
2044
+ */
2045
+ export const ListExecutionSummarySchema = z.object({
2046
+ executionId: z.string(),
2047
+ resourceId: z.string(),
2048
+ status: z.string(),
2049
+ createdAt: z.string(),
2050
+ completedAt: z.string().nullable(),
2051
+ durationMs: z.number().int().nullable()
2052
+ })
2053
+ ```
2054
+
2055
+ ### `ListExecutionsResponseSchema`
2056
+
2057
+ ```typescript
2058
+ export const ListExecutionsResponseSchema = z.array(ListExecutionSummarySchema)
2059
+ ```
2060
+
2061
+ ### `AcqCompanyStatusSchema`
2062
+
2063
+ ```typescript
2064
+ export const AcqCompanyStatusSchema = z.enum(['active', 'invalid'])
2065
+ ```
2066
+
2067
+ ### `AcqContactStatusSchema`
2068
+
2069
+ ```typescript
2070
+ export const AcqContactStatusSchema = z.enum(['active', 'invalid'])
2071
+ ```
2072
+
2073
+ ### `AcqEmailValidSchema`
2074
+
2075
+ ```typescript
2076
+ export const AcqEmailValidSchema = z.enum(['VALID', 'INVALID', 'RISKY', 'UNKNOWN'])
2077
+ ```
2078
+
2079
+ ### `CompanyIdParamsSchema`
2080
+
2081
+ ```typescript
2082
+ export const CompanyIdParamsSchema = z.object({
2083
+ companyId: UuidSchema
2084
+ })
2085
+ ```
2086
+
2087
+ ### `ContactIdParamsSchema`
2088
+
2089
+ ```typescript
2090
+ export const ContactIdParamsSchema = z.object({
2091
+ contactId: UuidSchema
2092
+ })
2093
+ ```
2094
+
2095
+ ### `ListCompaniesQuerySchema`
2096
+
2097
+ ```typescript
2098
+ export const ListCompaniesQuerySchema = z
2099
+ .object({
2100
+ search: z.string().trim().min(1).max(200).optional(),
2101
+ listId: UuidSchema.optional(),
2102
+ domain: z.string().trim().min(1).max(255).optional(),
2103
+ website: z.string().trim().min(1).max(2048).optional(),
2104
+ segment: z.string().trim().min(1).max(255).optional(),
2105
+ category: z.string().trim().min(1).max(255).optional(),
2106
+ batchId: z.string().trim().min(1).max(255).optional(),
2107
+ status: AcqCompanyStatusSchema.optional(),
2108
+ includeAll: QueryBooleanSchema.optional(),
2109
+ limit: z.coerce.number().int().min(1).max(5000).default(50),
2110
+ offset: z.coerce.number().int().min(0).default(0)
2111
+ })
2112
+ .strict()
2113
+ ```
2114
+
2115
+ ### `ListContactsQuerySchema`
2116
+
2117
+ ```typescript
2118
+ export const ListContactsQuerySchema = z
2119
+ .object({
2120
+ search: z.string().trim().min(1).max(200).optional(),
2121
+ listId: UuidSchema.optional(),
2122
+ openingLineIsNull: QueryBooleanSchema.optional(),
2123
+ batchId: z.string().trim().min(1).max(255).optional(),
2124
+ contactStatus: AcqContactStatusSchema.optional(),
2125
+ limit: z.coerce.number().int().min(1).max(5000).default(5000),
2126
+ offset: z.coerce.number().int().min(0).default(0)
2127
+ })
2128
+ .strict()
2129
+ ```
2130
+
2131
+ ### `CreateCompanyRequestSchema`
2132
+
2133
+ ```typescript
2134
+ export const CreateCompanyRequestSchema = z
2135
+ .object({
2136
+ name: z.string().trim().min(1).max(255),
2137
+ domain: z.string().trim().min(1).max(255).optional(),
2138
+ linkedinUrl: z.string().trim().url().optional(),
2139
+ website: z.string().trim().url().optional(),
2140
+ numEmployees: z.number().int().min(0).optional(),
2141
+ foundedYear: z.number().int().optional(),
2142
+ locationCity: z.string().trim().min(1).max(255).optional(),
2143
+ locationState: z.string().trim().min(1).max(255).optional(),
2144
+ category: z.string().trim().min(1).max(255).optional(),
2145
+ source: z.string().trim().min(1).max(255).optional(),
2146
+ batchId: z.string().trim().min(1).max(255).optional(),
2147
+ verticalResearch: z.string().trim().min(1).max(5000).optional()
2148
+ })
2149
+ .strict()
2150
+ ```
2151
+
2152
+ ### `UpdateCompanyRequestSchema`
2153
+
2154
+ ```typescript
2155
+ export const UpdateCompanyRequestSchema = z
2156
+ .object({
2157
+ name: z.string().trim().min(1).max(255).optional(),
2158
+ domain: z.string().trim().min(1).max(255).optional(),
2159
+ linkedinUrl: z.string().trim().url().optional(),
2160
+ website: z.string().trim().url().optional(),
2161
+ numEmployees: z.number().int().min(0).optional(),
2162
+ foundedYear: z.number().int().optional(),
2163
+ locationCity: z.string().trim().min(1).max(255).optional(),
2164
+ locationState: z.string().trim().min(1).max(255).optional(),
2165
+ category: z.string().trim().min(1).max(255).optional(),
2166
+ segment: z.string().trim().min(1).max(255).optional(),
2167
+ pipelineStatus: z.record(z.string(), z.unknown()).optional(),
2168
+ enrichmentData: z.record(z.string(), z.unknown()).optional(),
2169
+ source: z.string().trim().min(1).max(255).optional(),
2170
+ batchId: z.string().trim().min(1).max(255).optional(),
2171
+ status: AcqCompanyStatusSchema.optional(),
2172
+ verticalResearch: z.string().trim().min(1).max(5000).nullable().optional()
2173
+ })
2174
+ .strict()
2175
+ .refine(
2176
+ (data) =>
2177
+ data.name !== undefined ||
2178
+ data.domain !== undefined ||
2179
+ data.linkedinUrl !== undefined ||
2180
+ data.website !== undefined ||
2181
+ data.numEmployees !== undefined ||
2182
+ data.foundedYear !== undefined ||
2183
+ data.locationCity !== undefined ||
2184
+ data.locationState !== undefined ||
2185
+ data.category !== undefined ||
2186
+ data.segment !== undefined ||
2187
+ data.pipelineStatus !== undefined ||
2188
+ data.enrichmentData !== undefined ||
2189
+ data.source !== undefined ||
2190
+ data.batchId !== undefined ||
2191
+ data.status !== undefined ||
2192
+ data.verticalResearch !== undefined,
2193
+ {
2194
+ message: 'At least one field must be provided'
2195
+ }
2196
+ )
2197
+ ```
2198
+
2199
+ ### `CreateContactRequestSchema`
2200
+
2201
+ ```typescript
2202
+ export const CreateContactRequestSchema = z
2203
+ .object({
2204
+ email: z.string().trim().email(),
2205
+ companyId: UuidSchema.optional(),
2206
+ firstName: z.string().trim().min(1).max(255).optional(),
2207
+ lastName: z.string().trim().min(1).max(255).optional(),
2208
+ linkedinUrl: z.string().trim().url().optional(),
2209
+ title: z.string().trim().min(1).max(255).optional(),
2210
+ source: z.string().trim().min(1).max(255).optional(),
2211
+ sourceId: z.string().trim().min(1).max(255).optional(),
2212
+ batchId: z.string().trim().min(1).max(255).optional()
2213
+ })
2214
+ .strict()
2215
+ ```
2216
+
2217
+ ### `UpdateContactRequestSchema`
2218
+
2219
+ ```typescript
2220
+ export const UpdateContactRequestSchema = z
2221
+ .object({
2222
+ companyId: UuidSchema.optional(),
2223
+ emailValid: AcqEmailValidSchema.optional(),
2224
+ firstName: z.string().trim().min(1).max(255).optional(),
2225
+ lastName: z.string().trim().min(1).max(255).optional(),
2226
+ linkedinUrl: z.string().trim().url().optional(),
2227
+ title: z.string().trim().min(1).max(255).optional(),
2228
+ headline: z.string().trim().min(1).max(5000).optional(),
2229
+ filterReason: z.string().trim().min(1).max(5000).optional(),
2230
+ openingLine: z.string().trim().min(1).max(5000).optional(),
2231
+ pipelineStatus: z.record(z.string(), z.unknown()).optional(),
2232
+ enrichmentData: z.record(z.string(), z.unknown()).optional(),
2233
+ status: AcqContactStatusSchema.optional()
2234
+ })
2235
+ .strict()
2236
+ .refine(
2237
+ (data) =>
2238
+ data.companyId !== undefined ||
2239
+ data.emailValid !== undefined ||
2240
+ data.firstName !== undefined ||
2241
+ data.lastName !== undefined ||
2242
+ data.linkedinUrl !== undefined ||
2243
+ data.title !== undefined ||
2244
+ data.headline !== undefined ||
2245
+ data.filterReason !== undefined ||
2246
+ data.openingLine !== undefined ||
2247
+ data.pipelineStatus !== undefined ||
2248
+ data.enrichmentData !== undefined ||
2249
+ data.status !== undefined,
2250
+ {
2251
+ message: 'At least one field must be provided'
2252
+ }
2253
+ )
2254
+ ```
2255
+
2256
+ ### `AcqCompanyResponseSchema`
2257
+
2258
+ ```typescript
2259
+ export const AcqCompanyResponseSchema = z.object({
2260
+ id: z.string(),
2261
+ organizationId: z.string(),
2262
+ name: z.string(),
2263
+ domain: z.string().nullable(),
2264
+ linkedinUrl: z.string().nullable(),
2265
+ website: z.string().nullable(),
2266
+ numEmployees: z.number().nullable(),
2267
+ foundedYear: z.number().nullable(),
2268
+ locationCity: z.string().nullable(),
2269
+ locationState: z.string().nullable(),
2270
+ category: z.string().nullable(),
2271
+ categoryPain: z.string().nullable(),
2272
+ segment: z.string().nullable(),
2273
+ pipelineStatus: z.record(z.string(), z.unknown()).nullable(),
2274
+ enrichmentData: z.record(z.string(), z.unknown()).nullable(),
2275
+ source: z.string().nullable(),
2276
+ batchId: z.string().nullable(),
2277
+ status: AcqCompanyStatusSchema,
2278
+ contactCount: z.number().int().min(0),
2279
+ verticalResearch: z.string().nullable(),
2280
+ createdAt: z.string(),
2281
+ updatedAt: z.string()
2282
+ })
2283
+ ```
2284
+
2285
+ ### `AcqCompanyListResponseSchema`
2286
+
2287
+ ```typescript
2288
+ export const AcqCompanyListResponseSchema = z.object({
2289
+ data: z.array(AcqCompanyResponseSchema),
2290
+ total: z.number().int(),
2291
+ limit: z.number().int(),
2292
+ offset: z.number().int()
2293
+ })
2294
+ ```
2295
+
2296
+ ### `AcqCompanyFacetsResponseSchema`
2297
+
2298
+ ```typescript
2299
+ export const AcqCompanyFacetsResponseSchema = z.object({
2300
+ segments: z.array(z.string()),
2301
+ categories: z.array(z.string()),
2302
+ statuses: z.array(AcqCompanyStatusSchema)
2303
+ })
2304
+ ```
2305
+
2306
+ ### `AcqContactCompanySummarySchema`
2307
+
2308
+ ```typescript
2309
+ export const AcqContactCompanySummarySchema = z.object({
2310
+ id: z.string(),
2311
+ name: z.string(),
2312
+ domain: z.string().nullable(),
2313
+ website: z.string().nullable(),
2314
+ linkedinUrl: z.string().nullable(),
2315
+ segment: z.string().nullable(),
2316
+ category: z.string().nullable(),
2317
+ status: AcqCompanyStatusSchema
2318
+ })
2319
+ ```
2320
+
2321
+ ### `AcqContactResponseSchema`
2322
+
2323
+ ```typescript
2324
+ export const AcqContactResponseSchema = z.object({
2325
+ id: z.string(),
2326
+ organizationId: z.string(),
2327
+ companyId: z.string().nullable(),
2328
+ email: z.string(),
2329
+ emailValid: AcqEmailValidSchema.nullable(),
2330
+ firstName: z.string().nullable(),
2331
+ lastName: z.string().nullable(),
2332
+ linkedinUrl: z.string().nullable(),
2333
+ title: z.string().nullable(),
2334
+ headline: z.string().nullable(),
2335
+ filterReason: z.string().nullable(),
2336
+ openingLine: z.string().nullable(),
2337
+ source: z.string().nullable(),
2338
+ sourceId: z.string().nullable(),
2339
+ pipelineStatus: z.record(z.string(), z.unknown()).nullable(),
2340
+ enrichmentData: z.record(z.string(), z.unknown()).nullable(),
2341
+ attioPersonId: z.string().nullable(),
2342
+ batchId: z.string().nullable(),
2343
+ status: AcqContactStatusSchema,
2344
+ company: AcqContactCompanySummarySchema.nullable().optional(),
2345
+ createdAt: z.string(),
2346
+ updatedAt: z.string()
2347
+ })
2348
+ ```
2349
+
2350
+ ### `AcqContactListResponseSchema`
2351
+
2352
+ ```typescript
2353
+ export const AcqContactListResponseSchema = z.object({
2354
+ data: z.array(AcqContactResponseSchema),
2355
+ total: z.number().int(),
2356
+ limit: z.number().int(),
2357
+ offset: z.number().int()
2358
+ })
2359
+ ```
2360
+
2361
+ ### `AcqArtifactOwnerKindSchema`
2362
+
2363
+ ```typescript
2364
+ export const AcqArtifactOwnerKindSchema = z.enum(['company', 'contact', 'deal', 'list', 'list_member'])
2365
+ ```
2366
+
2367
+ ### `ListArtifactsQuerySchema`
2368
+
2369
+ ```typescript
2370
+ export const ListArtifactsQuerySchema = z
2371
+ .object({
2372
+ ownerKind: AcqArtifactOwnerKindSchema,
2373
+ ownerId: UuidSchema
2374
+ })
2375
+ .strict()
2376
+ ```
2377
+
2378
+ ### `CreateArtifactRequestSchema`
2379
+
2380
+ ```typescript
2381
+ export const CreateArtifactRequestSchema = z
2382
+ .object({
2383
+ ownerKind: AcqArtifactOwnerKindSchema,
2384
+ ownerId: UuidSchema,
2385
+ kind: z.string().trim().min(1).max(255),
2386
+ content: z.record(z.string(), z.unknown()),
2387
+ sourceExecutionId: UuidSchema.optional()
2388
+ })
2389
+ .strict()
2390
+ ```
2391
+
2392
+ ### `AcqArtifactResponseSchema`
2393
+
2394
+ ```typescript
2395
+ export const AcqArtifactResponseSchema = z.object({
2396
+ id: z.string(),
2397
+ organizationId: z.string(),
2398
+ ownerKind: z.string(),
2399
+ ownerId: z.string(),
2400
+ kind: z.string(),
2401
+ content: z.record(z.string(), z.unknown()),
2402
+ sourceExecutionId: z.string().nullable(),
2403
+ createdBy: z.string().nullable(),
2404
+ createdAt: z.string(),
2405
+ version: z.number().int()
2406
+ })
2407
+ ```
2408
+
2409
+ ### `AcqArtifactListResponseSchema`
2410
+
2411
+ ```typescript
2412
+ export const AcqArtifactListResponseSchema = z.object({
2413
+ artifacts: z.array(AcqArtifactResponseSchema)
2414
+ })
2415
+ ```
2416
+
2417
+ ### `ListTouchpointsQuerySchema`
2418
+
2419
+ ```typescript
2420
+ export const ListTouchpointsQuerySchema = z
2421
+ .object({
2422
+ listId: UuidSchema.optional(),
2423
+ listMemberId: UuidSchema.optional(),
2424
+ contactId: UuidSchema.optional()
2425
+ })
2426
+ .strict()
2427
+ .refine((data) => data.listId !== undefined || data.listMemberId !== undefined || data.contactId !== undefined, {
2428
+ message: 'At least one filter (listId, listMemberId, or contactId) must be provided'
2429
+ })
2430
+ ```
2431
+
2432
+ ### `AcqTouchpointResponseSchema`
2433
+
2434
+ ```typescript
2435
+ export const AcqTouchpointResponseSchema = z.object({
2436
+ id: z.string(),
2437
+ organizationId: z.string(),
2438
+ listId: z.string().nullable(),
2439
+ listMemberId: z.string().nullable(),
2440
+ contactId: z.string().nullable(),
2441
+ artifactId: z.string().nullable(),
2442
+ channel: z.string(),
2443
+ kind: z.string(),
2444
+ direction: z.string(),
2445
+ occurredAt: z.string(),
2446
+ payload: z.record(z.string(), z.unknown()).nullable(),
2447
+ sourceExecutionId: z.string().nullable(),
2448
+ createdAt: z.string()
2449
+ })
2450
+ ```
2451
+
2452
+ ### `AcqTouchpointListResponseSchema`
2453
+
2454
+ ```typescript
2455
+ export const AcqTouchpointListResponseSchema = z.object({
2456
+ touchpoints: z.array(AcqTouchpointResponseSchema)
2457
+ })
2458
+ ```
2459
+
2460
+ ### `ListMembersQuerySchema`
2461
+
2462
+ ```typescript
2463
+ export const ListMembersQuerySchema = z
2464
+ .object({
2465
+ limit: z.coerce.number().int().min(1).max(500).default(50),
2466
+ offset: z.coerce.number().int().min(0).default(0)
2467
+ })
2468
+ .strict()
2469
+ ```
2470
+
2471
+ ### `MemberIdParamsSchema`
2472
+
2473
+ ```typescript
2474
+ export const MemberIdParamsSchema = z.object({
2475
+ memberId: UuidSchema
2476
+ })
2477
+ ```
2478
+
2479
+ ### `AcqListMemberContactSummarySchema`
2480
+
2481
+ ```typescript
2482
+ export const AcqListMemberContactSummarySchema = z.object({
2483
+ id: z.string(),
2484
+ email: z.string(),
2485
+ firstName: z.string().nullable(),
2486
+ lastName: z.string().nullable(),
2487
+ title: z.string().nullable(),
2488
+ linkedinUrl: z.string().nullable(),
2489
+ companyId: z.string().nullable()
2490
+ })
2491
+ ```
2492
+
2493
+ ### `AcqListMemberResponseSchema`
2494
+
2495
+ ```typescript
2496
+ export const AcqListMemberResponseSchema = z.object({
2497
+ id: z.string(),
2498
+ listId: z.string(),
2499
+ contactId: z.string(),
2500
+ pipelineKey: z.string(),
2501
+ stageKey: z.string(),
2502
+ stateKey: z.string(),
2503
+ activityLog: z.unknown(),
2504
+ addedAt: z.string(),
2505
+ addedBy: z.string().nullable(),
2506
+ sourceExecutionId: z.string().nullable(),
2507
+ contact: AcqListMemberContactSummarySchema.nullable()
2508
+ })
2509
+ ```
2510
+
2511
+ ### `AcqListMembersResponseSchema`
2512
+
2513
+ ```typescript
2514
+ export const AcqListMembersResponseSchema = z.object({
2515
+ members: z.array(AcqListMemberResponseSchema)
2516
+ })
2517
+ ```
2518
+
2519
+ ### `ListCompanyIdParamsSchema`
2520
+
2521
+ ```typescript
2522
+ export const ListCompanyIdParamsSchema = z.object({
2523
+ listCompanyId: UuidSchema
2524
+ })
2525
+ ```
2526
+
2527
+ ### `AcqListCompanyResponseSchema`
2528
+
2529
+ ```typescript
2530
+ export const AcqListCompanyResponseSchema = z.object({
2531
+ id: z.string(),
2532
+ listId: z.string(),
2533
+ companyId: z.string(),
2534
+ pipelineKey: z.string(),
2535
+ stageKey: z.string(),
2536
+ stateKey: z.string(),
2537
+ activityLog: z.unknown(),
2538
+ addedAt: z.string(),
2539
+ addedBy: z.string().nullable(),
2540
+ sourceExecutionId: z.string().nullable()
2541
+ })
2542
+ ```
2543
+
2544
+ ### `AcqCompanySchemas`
2545
+
2546
+ ```typescript
2547
+ export const AcqCompanySchemas = {
2548
+ CompanyIdParams: CompanyIdParamsSchema,
2549
+ ListCompaniesQuery: ListCompaniesQuerySchema,
2550
+ CreateCompanyRequest: CreateCompanyRequestSchema,
2551
+ UpdateCompanyRequest: UpdateCompanyRequestSchema,
2552
+ AcqCompanyResponse: AcqCompanyResponseSchema,
2553
+ AcqCompanyListResponse: AcqCompanyListResponseSchema,
2554
+ AcqCompanyFacetsResponse: AcqCompanyFacetsResponseSchema
2555
+ }
2556
+ ```
2557
+
2558
+ ### `AcqContactSchemas`
2559
+
2560
+ ```typescript
2561
+ export const AcqContactSchemas = {
2562
+ ContactIdParams: ContactIdParamsSchema,
2563
+ ListContactsQuery: ListContactsQuerySchema,
2564
+ CreateContactRequest: CreateContactRequestSchema,
2565
+ UpdateContactRequest: UpdateContactRequestSchema,
2566
+ AcqContactResponse: AcqContactResponseSchema,
2567
+ AcqContactListResponse: AcqContactListResponseSchema
2568
+ }
2569
+ ```
2570
+
2571
+ ### `AcqListSchemas`
2572
+
2573
+ ```typescript
2574
+ export const AcqListSchemas = {
2575
+ // Params
2576
+ ListIdParams: ListIdParamsSchema,
2577
+
2578
+ // Primitives (for UI / tests)
2579
+ ListConfig: ListConfigSchema,
2580
+ ListStageCounts: ListStageCountsSchema,
2581
+ ListTelemetry: ListTelemetrySchema,
2582
+ PipelineStep: PipelineStepSchema,
2583
+
2584
+ // Requests
2585
+ CreateListRequest: CreateListRequestSchema,
2586
+ UpdateListRequest: UpdateListRequestSchema,
2587
+ UpdateListConfigRequest: UpdateListConfigRequestSchema,
2588
+ AddCompaniesToListRequest: AddCompaniesToListRequestSchema,
2589
+ RemoveCompaniesFromListRequest: RemoveCompaniesFromListRequestSchema,
2590
+ AddContactsToListRequest: AddContactsToListRequestSchema,
2591
+ RecordListExecutionRequest: RecordListExecutionRequestSchema,
2592
+
2593
+ // Responses
2594
+ AcqListResponse: AcqListResponseSchema,
2595
+ AcqListListResponse: AcqListListResponseSchema,
2596
+ ListTelemetryResponse: ListTelemetryResponseSchema,
2597
+ ListTelemetryListResponse: ListTelemetryListResponseSchema,
2598
+ ListExecutionsResponse: ListExecutionsResponseSchema
2599
+ }
2600
+ ```
2601
+
2602
+ ### `AcqSubstrateSchemas`
2603
+
2604
+ ```typescript
2605
+ export const AcqSubstrateSchemas = {
2606
+ // Artifacts
2607
+ ListArtifactsQuery: ListArtifactsQuerySchema,
2608
+ CreateArtifactRequest: CreateArtifactRequestSchema,
2609
+ AcqArtifactResponse: AcqArtifactResponseSchema,
2610
+ AcqArtifactListResponse: AcqArtifactListResponseSchema,
2611
+
2612
+ // Touchpoints
2613
+ ListTouchpointsQuery: ListTouchpointsQuerySchema,
2614
+ AcqTouchpointResponse: AcqTouchpointResponseSchema,
2615
+ AcqTouchpointListResponse: AcqTouchpointListResponseSchema,
2616
+
2617
+ // List members
2618
+ ListMembersQuery: ListMembersQuerySchema,
2619
+ MemberIdParams: MemberIdParamsSchema,
2620
+ AcqListMemberResponse: AcqListMemberResponseSchema,
2621
+ AcqListMembersResponse: AcqListMembersResponseSchema,
2622
+
2623
+ // List companies
2624
+ ListCompanyIdParams: ListCompanyIdParamsSchema,
2625
+ AcqListCompanyResponse: AcqListCompanyResponseSchema,
2626
+
2627
+ // Transition (shared with deals — TransitionItemRequestSchema)
2628
+ TransitionItemRequest: TransitionItemRequestSchema
2629
+ }
2630
+ ```
2631
+
2632
+ ### `Stateful`
2633
+
2634
+ ```typescript
2635
+ /**
2636
+ * Stateful trait — the (pipeline_key, stage_key, state_key, activity_log) quartet
2637
+ * applied to acq_deals (CRM HITL, shipped 2026-04-27) and being generalized to
2638
+ * acq_lists / acq_list_members / acq_list_companies via Track B.
2639
+ */
2640
+ export interface Stateful {
2641
+ pipeline_key: string
2642
+ stage_key: string
2643
+ state_key: string
2644
+ activity_log: ActivityEvent[]
2645
+ }
2646
+ ```
2647
+
2648
+ ### `TransitionItem`
2649
+
2650
+ ```typescript
2651
+ /** Generic transition shape — concrete per-entity transitionItem implementations satisfy this. */
2652
+ export type TransitionItem<T extends Stateful, TEvent extends ActivityEvent> = (
2653
+ item: T,
2654
+ transition: { stage_key?: string; state_key?: string; event: TEvent }
2655
+ ) => T
2656
+ ```
2657
+
2658
+ ### `DeriveActions`
2659
+
2660
+ ```typescript
2661
+ /** Generic action-derivation shape — concrete per-entity deriveActions implementations satisfy this. */
2662
+ export type DeriveActions<T extends Stateful, TAction> = (item: T) => TAction[]
2663
+ ```
2664
+
2665
+ ### `StatefulSchema`
2666
+
2667
+ ```typescript
2668
+ export const StatefulSchema = z.object({
2669
+ pipeline_key: z.string(),
2670
+ stage_key: z.string(),
2671
+ state_key: z.string(),
2672
+ activity_log: z.array(ActivityEventSchema)
2673
+ })
2674
+ ```
2675
+
2676
+ ### `StatefulStateDefinition`
2677
+
2678
+ ```typescript
2679
+ /** One state within a stage — minimal shape: key + display label. */
2680
+ export interface StatefulStateDefinition {
2681
+ /** Matches state_key values written by workflow steps. */
2682
+ stateKey: string
2683
+ label: string
2684
+ }
2685
+ ```
2686
+
2687
+ ### `StatefulStageDefinition`
2688
+
2689
+ ```typescript
2690
+ /** One stage within a pipeline — has a stage_key and an ordered list of valid states. */
2691
+ export interface StatefulStageDefinition {
2692
+ /** Matches stage_key values written by workflow steps. */
2693
+ stageKey: string
2694
+ label: string
2695
+ states: StatefulStateDefinition[]
2696
+ }
2697
+ ```
2698
+
2699
+ ### `StatefulPipelineDefinition`
2700
+
2701
+ ```typescript
2702
+ /**
2703
+ * Pipeline definition for a single entity participating in the Stateful trait.
2704
+ * Parallel to acq_deals' pipeline_key concept but structured for lead-gen entities.
2705
+ */
2706
+ export interface StatefulPipelineDefinition {
2707
+ /** Matches pipeline_key values in the database (e.g. 'lead-gen'). */
2708
+ pipelineKey: string
2709
+ label: string
2710
+ /** Entity this pipeline applies to (e.g. 'acq.list', 'acq.list-member', 'acq.list-company'). */
2711
+ entityKey: string
2712
+ stages: StatefulStageDefinition[]
2713
+ }
2714
+ ```
2715
+
2716
+ ### `ACQ_LISTS_LEAD_GEN_PIPELINE`
2717
+
2718
+ ```typescript
2719
+ /**
2720
+ * Lead-gen pipeline definition for acq_lists.
2721
+ * Single stage: 'lifecycle' with five ordered states (Decision B9).
2722
+ */
2723
+ export const ACQ_LISTS_LEAD_GEN_PIPELINE: StatefulPipelineDefinition = {
2724
+ pipelineKey: 'lead-gen',
2725
+ label: 'Lead Generation',
2726
+ entityKey: 'acq.list',
2727
+ stages: [
2728
+ {
2729
+ stageKey: 'lifecycle',
2730
+ label: 'Lifecycle',
2731
+ states: [
2732
+ { stateKey: 'draft', label: 'Draft' },
2733
+ { stateKey: 'enriching', label: 'Enriching' },
2734
+ { stateKey: 'launched', label: 'Launched' },
2735
+ { stateKey: 'closing', label: 'Closing' },
2736
+ { stateKey: 'archived', label: 'Archived' }
2737
+ ]
2738
+ }
2739
+ ]
2740
+ }
2741
+ ```
2742
+
2743
+ ### `ACQ_LIST_MEMBERS_LEAD_GEN_PIPELINE`
2744
+
2745
+ ```typescript
2746
+ /**
2747
+ * Lead-gen pipeline definition for acq_list_members (contacts).
2748
+ * Three stages matching the post-restructure sales subdomain tree.
2749
+ *
2750
+ * Note: members visit outreach and prospecting states depending on which
2751
+ * workflow last processed them. stage_key is set per-transition by the workflow.
2752
+ */
2753
+ export const ACQ_LIST_MEMBERS_LEAD_GEN_PIPELINE: StatefulPipelineDefinition = {
2754
+ pipelineKey: 'lead-gen',
2755
+ label: 'Lead Generation',
2756
+ entityKey: 'acq.list-member',
2757
+ stages: [
2758
+ {
2759
+ stageKey: 'outreach',
2760
+ label: 'Outreach',
2761
+ states: [
2762
+ PENDING_STATE,
2763
+ { stateKey: 'personalized', label: 'Personalized' },
2764
+ { stateKey: 'uploaded', label: 'Uploaded' },
2765
+ { stateKey: 'interested', label: 'Interested' }
2766
+ ]
2767
+ },
2768
+ {
2769
+ stageKey: 'prospecting',
2770
+ label: 'Prospecting',
2771
+ states: [
2772
+ PENDING_STATE,
2773
+ { stateKey: 'discovered', label: 'Discovered' },
2774
+ { stateKey: 'verified', label: 'Verified' }
2775
+ ]
2776
+ },
2777
+ {
2778
+ stageKey: 'qualification',
2779
+ label: 'Qualification',
2780
+ states: [PENDING_STATE]
2781
+ }
2782
+ ]
2783
+ }
2784
+ ```
2785
+
2786
+ ### `ACQ_LIST_COMPANIES_LEAD_GEN_PIPELINE`
2787
+
2788
+ ```typescript
2789
+ /**
2790
+ * Lead-gen pipeline definition for acq_list_companies.
2791
+ * Three stages matching the post-restructure sales subdomain tree.
2792
+ *
2793
+ * Note: companies visit prospecting and qualification states depending on which
2794
+ * workflow last processed them. stage_key is set per-transition by the workflow.
2795
+ */
2796
+ export const ACQ_LIST_COMPANIES_LEAD_GEN_PIPELINE: StatefulPipelineDefinition = {
2797
+ pipelineKey: 'lead-gen',
2798
+ label: 'Lead Generation',
2799
+ entityKey: 'acq.list-company',
2800
+ stages: [
2801
+ {
2802
+ stageKey: 'outreach',
2803
+ label: 'Outreach',
2804
+ states: [PENDING_STATE]
2805
+ },
2806
+ {
2807
+ stageKey: 'prospecting',
2808
+ label: 'Prospecting',
2809
+ states: [
2810
+ PENDING_STATE,
2811
+ { stateKey: 'populated', label: 'Populated' },
2812
+ { stateKey: 'extracted', label: 'Extracted' }
2813
+ ]
2814
+ },
2815
+ {
2816
+ stageKey: 'qualification',
2817
+ label: 'Qualification',
2818
+ states: [PENDING_STATE, { stateKey: 'qualified', label: 'Qualified' }]
2819
+ }
2820
+ ]
2821
+ }
2822
+ ```
2823
+
2824
+ ### `LEAD_GEN_PIPELINE_DEFINITIONS`
2825
+
2826
+ ```typescript
2827
+ /**
2828
+ * All lead-gen pipeline definitions indexed by entity key.
2829
+ * Use findPipeline() to locate a definition by pipeline_key within any of these arrays.
2830
+ */
2831
+ export const LEAD_GEN_PIPELINE_DEFINITIONS: Record<string, StatefulPipelineDefinition[]> = {
2832
+ 'acq.list': [ACQ_LISTS_LEAD_GEN_PIPELINE],
2833
+ 'acq.list-member': [ACQ_LIST_MEMBERS_LEAD_GEN_PIPELINE],
2834
+ 'acq.list-company': [ACQ_LIST_COMPANIES_LEAD_GEN_PIPELINE]
2835
+ }
2836
+ ```
2837
+
2838
+ ### `PaginationParams`
2839
+
2840
+ ```typescript
2841
+ export interface PaginationParams {
2842
+ limit: number
2843
+ offset: number
2844
+ }
2845
+ ```
2846
+
2847
+ ### `PaginatedResult`
2848
+
2849
+ ```typescript
2850
+ export interface PaginatedResult<T> {
2851
+ data: T[]
2852
+ total: number
2853
+ limit: number
2854
+ offset: number
2855
+ }
2856
+ ```
2857
+
2858
+ ### `CreateListParams`
2859
+
2860
+ ```typescript
2861
+ export interface CreateListParams {
2862
+ organizationId: string
2863
+ name: string
2864
+ description?: string
2865
+ type?: string
2866
+ batchIds?: string[]
2867
+ instantlyCampaignId?: string
2868
+ status?: string
2869
+ metadata?: Record<string, unknown>
2870
+ config?: ListConfig
2871
+ }
2872
+ ```
2873
+
2874
+ ### `UpdateListParams`
2875
+
2876
+ ```typescript
2877
+ export interface UpdateListParams {
2878
+ name?: string
2879
+ description?: string
2880
+ status?: string
2881
+ batchIds?: string[]
2882
+ }
2883
+ ```
2884
+
2885
+ ### `CreateCompanyParams`
2886
+
2887
+ ```typescript
2888
+ export interface CreateCompanyParams {
2889
+ organizationId: string
2890
+ name: string
2891
+ domain?: string
2892
+ linkedinUrl?: string
2893
+ website?: string
2894
+ numEmployees?: number
2895
+ foundedYear?: number
2896
+ locationCity?: string
2897
+ locationState?: string
2898
+ category?: string
2899
+ source?: string
2900
+ batchId?: string
2901
+ verticalResearch?: string
2902
+ }
2903
+ ```
2904
+
2905
+ ### `UpdateCompanyParams`
2906
+
2907
+ ```typescript
2908
+ export interface UpdateCompanyParams {
2909
+ name?: string
2910
+ domain?: string
2911
+ linkedinUrl?: string
2912
+ website?: string
2913
+ numEmployees?: number
2914
+ foundedYear?: number
2915
+ locationCity?: string
2916
+ locationState?: string
2917
+ category?: string
2918
+ segment?: string
2919
+ pipelineStatus?: Record<string, unknown>
2920
+ enrichmentData?: Record<string, unknown>
2921
+ source?: string
2922
+ batchId?: string
2923
+ status?: 'active' | 'invalid'
2924
+ verticalResearch?: string | null
2925
+ /** Track A: flat qualification score column (null until a scoring rubric is defined) */
2926
+ qualificationScore?: number | null
2927
+ /** Track A: flat qualification signals jsonb — mirrors the former pipeline_status.qualification shape */
2928
+ qualificationSignals?: Record<string, unknown> | null
2929
+ /** Track A: key identifying the rubric used for qualification */
2930
+ qualificationRubricKey?: string | null
2931
+ }
2932
+ ```
2933
+
2934
+ ### `UpsertCompanyParams`
2935
+
2936
+ ```typescript
2937
+ export type UpsertCompanyParams = CreateCompanyParams
2938
+ ```
2939
+
2940
+ ### `CompanyFilters`
2941
+
2942
+ ```typescript
2943
+ export interface CompanyFilters {
2944
+ listId?: string // Filter to companies in a specific list (via acq_list_companies)
2945
+ search?: string
2946
+ domain?: string
2947
+ website?: string
2948
+ segment?: string
2949
+ category?: string
2950
+ pipelineStatus?: Record<string, unknown>
2951
+ /** Exclude companies whose pipeline_status contains this value (PostgREST NOT contains) */
2952
+ pipelineStatusNot?: Record<string, unknown>
2953
+ batchId?: string
2954
+ status?: 'active' | 'invalid'
2955
+ includeAll?: boolean
2956
+ excludeColumns?: Array<'enrichmentData' | 'pipelineStatus'>
2957
+ }
2958
+ ```
2959
+
2960
+ ### `CreateContactParams`
2961
+
2962
+ ```typescript
2963
+ export interface CreateContactParams {
2964
+ organizationId: string
2965
+ email: string
2966
+ companyId?: string
2967
+ firstName?: string
2968
+ lastName?: string
2969
+ linkedinUrl?: string
2970
+ title?: string
2971
+ source?: string
2972
+ sourceId?: string
2973
+ batchId?: string
2974
+ }
2975
+ ```
2976
+
2977
+ ### `UpdateContactParams`
2978
+
2979
+ ```typescript
2980
+ export interface UpdateContactParams {
2981
+ companyId?: string
2982
+ emailValid?: 'VALID' | 'INVALID' | 'RISKY' | 'UNKNOWN'
2983
+ firstName?: string
2984
+ lastName?: string
2985
+ linkedinUrl?: string
2986
+ title?: string
2987
+ headline?: string
2988
+ filterReason?: string
2989
+ openingLine?: string
2990
+ pipelineStatus?: Record<string, unknown>
2991
+ enrichmentData?: Record<string, unknown>
2992
+ status?: 'active' | 'invalid'
2993
+ }
2994
+ ```
2995
+
2996
+ ### `UpsertContactParams`
2997
+
2998
+ ```typescript
2999
+ export type UpsertContactParams = CreateContactParams
3000
+ ```
3001
+
3002
+ ### `ContactFilters`
3003
+
3004
+ ```typescript
3005
+ export interface ContactFilters {
3006
+ listId?: string // Filter to contacts in a specific list (via acq_list_members)
3007
+ search?: string
3008
+ openingLineIsNull?: boolean // Filter to contacts without personalization
3009
+ pipelineStatus?: Record<string, unknown>
3010
+ batchId?: string
3011
+ contactStatus?: 'active' | 'invalid' // Filter by contact status (soft-delete flag)
3012
+ }
3013
+ ```
3014
+
3015
+ ### `UpsertSocialPostParams`
3016
+
3017
+ ```typescript
3018
+ export interface UpsertSocialPostParams {
3019
+ organizationId: string
3020
+ platform: string
3021
+ platformPostId: string
3022
+ authorName: string
3023
+ authorUrl?: string | null
3024
+ postTitle: string
3025
+ postText: string
3026
+ postUrl: string
3027
+ engagementCount?: number
3028
+ commentsCount?: number
3029
+ postedAt: string
3030
+ metadata?: Record<string, unknown>
3031
+ relevanceScore?: number
3032
+ matchedKeywords?: string[]
3033
+ matchedQuery?: string | null
3034
+ initialDraft?: string | null
3035
+ finalResponse?: string | null
3036
+ sourceCategory?: string | null
3037
+ }
3038
+ ```
3039
+
3040
+ ### `UpsertSocialPostsParams`
3041
+
3042
+ ```typescript
3043
+ export interface UpsertSocialPostsParams {
3044
+ organizationId: string
3045
+ posts: Omit<UpsertSocialPostParams, 'organizationId'>[]
3046
+ }
3047
+ ```
3048
+
3049
+ ### `UpsertSocialPostsResult`
3050
+
3051
+ ```typescript
3052
+ export interface UpsertSocialPostsResult {
3053
+ inserted: number
3054
+ duplicatesSkipped: number
3055
+ }
3056
+ ```
3057
+
3058
+ ### `AddContactsToListParams`
3059
+
3060
+ ```typescript
3061
+ export interface AddContactsToListParams {
3062
+ organizationId: string
3063
+ listId: string
3064
+ contactIds: string[]
3065
+ }
3066
+ ```
3067
+
3068
+ ### `AddContactsToListResult`
3069
+
3070
+ ```typescript
3071
+ export interface AddContactsToListResult {
3072
+ added: number
3073
+ alreadyExisted: number
3074
+ }
3075
+ ```
3076
+
3077
+ ### `UpdateListConfigParams`
3078
+
3079
+ ```typescript
3080
+ export interface UpdateListConfigParams {
3081
+ organizationId: string
3082
+ listId: string
3083
+ /** Deep-partial patch — any subtree that is present is replaced at that level. */
3084
+ configPatch: Record<string, unknown>
3085
+ }
3086
+ ```
3087
+
3088
+ ### `UpdateCompanyStageParams`
3089
+
3090
+ ```typescript
3091
+ export interface UpdateCompanyStageParams {
3092
+ organizationId: string
3093
+ listId: string
3094
+ companyId: string
3095
+ stage: CompanyListStage
3096
+ executionId?: string
3097
+ }
3098
+ ```
3099
+
3100
+ ### `UpdateContactStageParams`
3101
+
3102
+ ```typescript
3103
+ export interface UpdateContactStageParams {
3104
+ organizationId: string
3105
+ listId: string
3106
+ contactId: string
3107
+ stage: ContactListStage
3108
+ executionId?: string
3109
+ }
3110
+ ```
3111
+
3112
+ ### `AddCompaniesToListParams`
3113
+
3114
+ ```typescript
3115
+ export interface AddCompaniesToListParams {
3116
+ organizationId: string
3117
+ listId: string
3118
+ companyIds: string[]
3119
+ }
3120
+ ```
3121
+
3122
+ ### `AddCompaniesToListResult`
3123
+
3124
+ ```typescript
3125
+ export interface AddCompaniesToListResult {
3126
+ added: number
3127
+ alreadyExisted: number
3128
+ }
3129
+ ```
3130
+
3131
+ ### `RemoveCompaniesFromListParams`
3132
+
3133
+ ```typescript
3134
+ export interface RemoveCompaniesFromListParams {
3135
+ organizationId: string
3136
+ listId: string
3137
+ companyIds: string[]
3138
+ }
3139
+ ```
3140
+
3141
+ ### `RemoveCompaniesFromListResult`
3142
+
3143
+ ```typescript
3144
+ export interface RemoveCompaniesFromListResult {
3145
+ removed: number
3146
+ }
3147
+ ```
3148
+
3149
+ ### `RecordListExecutionParams`
3150
+
3151
+ ```typescript
3152
+ export interface RecordListExecutionParams {
3153
+ organizationId: string
3154
+ listId: string
3155
+ executionId: string
3156
+ configSnapshot?: Record<string, unknown>
3157
+ }
3158
+ ```
3159
+
3160
+ ### `ListExecutionSummary`
3161
+
3162
+ ```typescript
3163
+ export interface ListExecutionSummary {
3164
+ executionId: string
3165
+ resourceId: string
3166
+ status: string
3167
+ createdAt: string
3168
+ completedAt: string | null
3169
+ durationMs: number | null
3170
+ }
3171
+ ```
3172
+
3173
+ ### `BulkImportParams`
3174
+
3175
+ ```typescript
3176
+ export interface BulkImportParams {
3177
+ organizationId: string
3178
+ contacts: CreateContactParams[]
3179
+ listId?: string
3180
+ }
3181
+ ```
3182
+
3183
+ ### `BulkImportResult`
3184
+
3185
+ ```typescript
3186
+ export interface BulkImportResult {
3187
+ created: number
3188
+ updated: number
3189
+ errors: Array<{ email: string; error: string }>
3190
+ }
3191
+ ```
3192
+
3193
+ ### `BulkImportCompanyEntry`
3194
+
3195
+ ```typescript
3196
+ export interface BulkImportCompanyEntry {
3197
+ name: string
3198
+ domain: string
3199
+ website?: string
3200
+ locationCity?: string
3201
+ locationState?: string
3202
+ category?: string
3203
+ source?: string
3204
+ enrichmentData?: Record<string, unknown>
3205
+ pipelineStatus?: Record<string, unknown>
3206
+ }
3207
+ ```
3208
+
3209
+ ### `BulkImportCompaniesParams`
3210
+
3211
+ ```typescript
3212
+ export interface BulkImportCompaniesParams {
3213
+ organizationId: string
3214
+ batchId: string
3215
+ companies: BulkImportCompanyEntry[]
3216
+ }
3217
+ ```
3218
+
3219
+ ### `BulkImportCompaniesResult`
3220
+
3221
+ ```typescript
3222
+ export interface BulkImportCompaniesResult {
3223
+ created: number
3224
+ skipped: number
3225
+ errors: Array<{ companyName: string; error: string }>
3226
+ }
3227
+ ```
3228
+
3229
+ ### `LeadToolMap`
3230
+
3231
+ ```typescript
3232
+ export type LeadToolMap = {
3233
+ // List operations
3234
+ listLists: { params: Record<string, never>; result: AcqList[] }
3235
+ createList: { params: Omit<CreateListParams, 'organizationId'>; result: AcqList }
3236
+ updateList: { params: { id: string } & UpdateListParams; result: AcqList }
3237
+ deleteList: { params: { id: string }; result: void }
3238
+ addContactsToList: { params: Omit<AddContactsToListParams, 'organizationId'>; result: AddContactsToListResult }
3239
+ updateCompanyStage: {
3240
+ params: Omit<UpdateCompanyStageParams, 'organizationId'>
3241
+ result: void
3242
+ }
3243
+ updateContactStage: {
3244
+ params: Omit<UpdateContactStageParams, 'organizationId'>
3245
+ result: void
3246
+ }
3247
+ // Company operations
3248
+ createCompany: { params: Omit<CreateCompanyParams, 'organizationId'>; result: AcqCompany }
3249
+ upsertCompany: { params: Omit<UpsertCompanyParams, 'organizationId'>; result: AcqCompany }
3250
+ updateCompany: { params: { id: string } & UpdateCompanyParams; result: AcqCompany }
3251
+ getCompany: { params: { id: string }; result: AcqCompany | null }
3252
+ listCompanies: { params: CompanyFilters; result: AcqCompany[] }
3253
+ deleteCompany: { params: { id: string }; result: void }
3254
+ // Contact operations
3255
+ createContact: { params: Omit<CreateContactParams, 'organizationId'>; result: AcqContact }
3256
+ upsertContact: { params: Omit<UpsertContactParams, 'organizationId'>; result: AcqContact }
3257
+ updateContact: { params: { id: string } & UpdateContactParams; result: AcqContact }
3258
+ getContact: { params: { id: string }; result: AcqContact | null }
3259
+ getContactByEmail: { params: { email: string }; result: AcqContact | null }
3260
+ listContacts: {
3261
+ params: ContactFilters & { limit?: number; offset?: number }
3262
+ result: PaginatedResult<AcqContact>
3263
+ }
3264
+ deleteContact: { params: { id: string }; result: void }
3265
+ bulkImportContacts: { params: Omit<BulkImportParams, 'organizationId'>; result: BulkImportResult }
3266
+ bulkImportCompanies: {
3267
+ params: Omit<BulkImportCompaniesParams, 'organizationId'>
3268
+ result: BulkImportCompaniesResult
3269
+ }
3270
+ deactivateContactsByCompany: {
3271
+ params: { companyId: string }
3272
+ result: { deactivated: number }
3273
+ }
3274
+ // Deal operations
3275
+ upsertDeal: { params: Omit<UpsertDealParams, 'organizationId'>; result: AcqDeal }
3276
+ getDealByEmail: { params: { email: string }; result: AcqDeal | null }
3277
+ getDealByEnvelopeId: { params: { envelopeId: string }; result: AcqDeal | null }
3278
+ updateDealEnvelopeId: { params: { dealId: string; envelopeId: string }; result: AcqDeal | null }
3279
+ getDealById: { params: Omit<GetDealByIdParams, 'organizationId'>; result: AcqDeal | null }
3280
+ getContactById: { params: Omit<GetContactByIdParams, 'organizationId'>; result: AcqContact | null }
3281
+ getCompanyById: { params: Omit<GetCompanyByIdParams, 'organizationId'>; result: AcqCompany | null }
3282
+ // Deal-sync operations
3283
+ updateDiscoveryData: { params: Omit<UpdateDiscoveryDataParams, 'organizationId'>; result: void }
3284
+ updateProposalData: { params: Omit<UpdateProposalDataParams, 'organizationId'>; result: void }
3285
+ markProposalSent: { params: Omit<MarkProposalSentParams, 'organizationId'>; result: void }
3286
+ markProposalReviewed: { params: Omit<MarkProposalReviewedParams, 'organizationId'>; result: void }
3287
+ updateCloseLostReason: { params: Omit<UpdateCloseLostReasonParams, 'organizationId'>; result: void }
3288
+ updateFees: { params: Omit<UpdateFeesParams, 'organizationId'>; result: void }
3289
+ transitionItem: { params: Omit<TransitionItemParams, 'organizationId'>; result: void }
3290
+ setContactNurture: { params: Omit<SetContactNurtureParams, 'organizationId'>; result: void }
3291
+ cancelSchedulesAndHitlByEmail: {
3292
+ params: Omit<CancelSchedulesAndHitlByEmailParams, 'organizationId'>
3293
+ result: { schedulesCancelled: number; hitlDeleted: number }
3294
+ }
3295
+ cancelHitlByDealId: { params: Omit<CancelHitlByDealIdParams, 'organizationId'>; result: { hitlDeleted: number } }
3296
+ clearDealFields: { params: Omit<ClearDealFieldsParams, 'organizationId'>; result: void }
3297
+ deleteDeal: { params: Omit<DeleteDealParams, 'organizationId'>; result: void }
3298
+ recordDealActivity: {
3299
+ params: Omit<RecordDealActivityParams, 'organizationId'>
3300
+ result: void
3301
+ }
3302
+ // Deal note operations
3303
+ createDealNote: {
3304
+ params: Omit<CreateDealNoteParams, 'organizationId'>
3305
+ result: AcqDealNote
3306
+ }
3307
+ listDealNotes: {
3308
+ params: Omit<ListDealNotesParams, 'organizationId'>
3309
+ result: AcqDealNote[]
3310
+ }
3311
+ // Deal task operations
3312
+ createDealTask: {
3313
+ params: Omit<CreateDealTaskParams, 'organizationId'>
3314
+ result: AcqDealTask
3315
+ }
3316
+ listDealTasks: {
3317
+ params: Omit<ListDealTasksParams, 'organizationId'>
3318
+ result: AcqDealTask[]
3319
+ }
3320
+ listDealTasksDue: {
3321
+ params: Omit<ListDealTasksDueParams, 'organizationId'>
3322
+ result: AcqDealTask[]
3323
+ }
3324
+ completeDealTask: {
3325
+ params: Omit<CompleteDealTaskParams, 'organizationId'>
3326
+ result: AcqDealTask
3327
+ }
3328
+ // Deal query & analytics operations
3329
+ listDeals: { params: DealFilters; result: AcqDeal[] }
3330
+ getDealPipelineAnalytics: { params: { recentLimit?: number }; result: DealPipelineAnalytics }
3331
+ // Enrichment data operations
3332
+ mergeEnrichmentData: {
3333
+ params: { id: string; table: 'acq_companies' | 'acq_contacts'; data: Record<string, unknown> }
3334
+ result: void
3335
+ }
3336
+ // Social monitoring operations
3337
+ upsertSocialPosts: {
3338
+ params: { posts: Omit<UpsertSocialPostParams, 'organizationId'>[] }
3339
+ result: UpsertSocialPostsResult
3340
+ }
3341
+ // Touchpoint operations
3342
+ recordTouchpoint: {
3343
+ params: {
3344
+ contactId?: string
3345
+ listMemberId?: string
3346
+ listId?: string
3347
+ direction: 'inbound' | 'outbound'
3348
+ channel: string
3349
+ kind: TouchpointKind
3350
+ payload?: Record<string, unknown>
3351
+ artifactId?: string
3352
+ sourceExecutionId?: string
3353
+ occurredAt?: string
3354
+ }
3355
+ result: { id: string }
3356
+ }
3357
+ setDealStateKey: {
3358
+ params: {
3359
+ dealId: string
3360
+ stateKey: string
3361
+ }
3362
+ result: { ok: true }
3363
+ }
3364
+ // CRM workflow helpers
3365
+ transitionDeal: {
3366
+ params: {
3367
+ dealId: string
3368
+ toStage: string
3369
+ toState?: string
3370
+ }
3371
+ result: { deal: AcqDeal }
3372
+ }
3373
+ loadDeal: {
3374
+ params: { dealId: string }
3375
+ result: DealDetail | null
3376
+ }
3377
+ listDealTouchpoints: {
3378
+ params: {
3379
+ dealId: string
3380
+ kind?: string
3381
+ limit?: number
3382
+ }
3383
+ result: Array<{
3384
+ id: string
3385
+ organization_id: string
3386
+ contact_id: string | null
3387
+ list_id: string | null
3388
+ list_member_id: string | null
3389
+ direction: string
3390
+ channel: string
3391
+ kind: string
3392
+ payload: Record<string, unknown> | null
3393
+ artifact_id: string | null
3394
+ source_execution_id: string | null
3395
+ occurred_at: string
3396
+ created_at: string
3397
+ }>
3398
+ }
3399
+ }
3400
+ ```
3401
+
3402
+ ### `ListToolMap`
3403
+
3404
+ ```typescript
3405
+ export type ListToolMap = {
3406
+ getConfig: { params: { listId: string }; result: ListConfig }
3407
+ recordExecution: {
3408
+ params: Omit<RecordListExecutionParams, 'organizationId'>
3409
+ result: void
3410
+ }
3411
+ updateCompanyStage: {
3412
+ params: Omit<UpdateCompanyStageParams, 'organizationId'>
3413
+ result: void
3414
+ }
3415
+ updateContactStage: {
3416
+ params: Omit<UpdateContactStageParams, 'organizationId'>
3417
+ result: void
3418
+ }
3419
+ }
3420
+ ```