@propertysimple/client 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/API.md +79 -0
  2. package/README.md +348 -0
  3. package/client/src/directory-sync.d.ts +123 -0
  4. package/client/src/directory-sync.js +404 -0
  5. package/client/src/index.d.ts +5123 -0
  6. package/client/src/index.js +285 -0
  7. package/client/src/managed-sync.d.ts +129 -0
  8. package/client/src/managed-sync.js +410 -0
  9. package/contracts/src/catalog-derived.d.ts +7 -0
  10. package/contracts/src/catalog-derived.js +78 -0
  11. package/contracts/src/catalog-fields.d.ts +231 -0
  12. package/contracts/src/catalog-fields.js +194 -0
  13. package/contracts/src/catalog-inventory.d.ts +14 -0
  14. package/contracts/src/catalog-inventory.js +10 -0
  15. package/contracts/src/directory-execution.d.ts +320 -0
  16. package/contracts/src/directory-execution.js +122 -0
  17. package/contracts/src/directory-identity.d.ts +1255 -0
  18. package/contracts/src/directory-identity.js +129 -0
  19. package/contracts/src/directory-sales.d.ts +333 -0
  20. package/contracts/src/directory-sales.js +102 -0
  21. package/contracts/src/directory.d.ts +1088 -0
  22. package/contracts/src/directory.js +179 -0
  23. package/contracts/src/index.d.ts +18 -0
  24. package/contracts/src/index.js +18 -0
  25. package/contracts/src/intelligence.d.ts +366 -0
  26. package/contracts/src/intelligence.js +157 -0
  27. package/contracts/src/location-serving.d.ts +13 -0
  28. package/contracts/src/location-serving.js +89 -0
  29. package/contracts/src/location.d.ts +73 -0
  30. package/contracts/src/location.js +75 -0
  31. package/contracts/src/openapi.d.ts +22 -0
  32. package/contracts/src/openapi.js +109 -0
  33. package/contracts/src/operations.d.ts +8316 -0
  34. package/contracts/src/operations.js +56 -0
  35. package/contracts/src/opportunity-events.d.ts +38 -0
  36. package/contracts/src/opportunity-events.js +69 -0
  37. package/contracts/src/platform-api.d.ts +12 -0
  38. package/contracts/src/platform-api.js +1 -0
  39. package/contracts/src/professional-intelligence.d.ts +256 -0
  40. package/contracts/src/professional-intelligence.js +90 -0
  41. package/contracts/src/property-features.d.ts +94 -0
  42. package/contracts/src/property-features.js +398 -0
  43. package/contracts/src/publication.d.ts +44 -0
  44. package/contracts/src/publication.js +42 -0
  45. package/contracts/src/routes.d.ts +437 -0
  46. package/contracts/src/routes.js +496 -0
  47. package/contracts/src/schemas.d.ts +3198 -0
  48. package/contracts/src/schemas.js +674 -0
  49. package/contracts/src/search.d.ts +2630 -0
  50. package/contracts/src/search.js +223 -0
  51. package/contracts/src/smart-feed.d.ts +3731 -0
  52. package/contracts/src/smart-feed.js +109 -0
  53. package/openapi.json +43542 -0
  54. package/package.json +60 -0
@@ -0,0 +1,129 @@
1
+ import { z } from 'zod';
2
+ import { DirectoryKind } from './directory.js';
3
+ import { ListingSchema, MarketStatus, CatalogSourceFreshnessSchema } from './schemas.js';
4
+ const Id = z.string().min(1).max(256);
5
+ const Revision = z.string().regex(/^\d+$/);
6
+ export const DirectoryIdentityMemberSchema = z.strictObject({
7
+ entity_id: Id,
8
+ source_namespace: Id,
9
+ });
10
+ /** A feed resource is anchored to an immutable source ID; hidden source members are omitted. */
11
+ export const DirectoryIdentityResolutionSchema = z
12
+ .strictObject({
13
+ id: Id,
14
+ source_namespace: Id,
15
+ kind: DirectoryKind,
16
+ revision: Revision,
17
+ canonical_id: Id.nullable(),
18
+ state: z.enum(['source_identity', 'canonical']),
19
+ members: z.array(DirectoryIdentityMemberSchema).min(1).max(200),
20
+ })
21
+ .superRefine((value, context) => {
22
+ const anchor = value.members.find((member) => member.entity_id === value.id);
23
+ if (!anchor ||
24
+ anchor.source_namespace !== value.source_namespace ||
25
+ new Set(value.members.map((member) => member.entity_id)).size !== value.members.length)
26
+ context.addIssue({
27
+ code: 'custom',
28
+ message: 'Identity members must be unique and include the source anchor.',
29
+ path: ['members'],
30
+ });
31
+ if (value.state === 'source_identity'
32
+ ? value.canonical_id !== null || value.members.length !== 1
33
+ : value.canonical_id === null)
34
+ context.addIssue({
35
+ code: 'custom',
36
+ message: 'Canonical state and membership must agree.',
37
+ path: ['canonical_id'],
38
+ });
39
+ });
40
+ export const DirectoryCanonicalIdentitySchema = z.strictObject({
41
+ requested_id: Id,
42
+ state: z.enum(['source_identity', 'canonical', 'redirected', 'split']),
43
+ canonical_id: Id.nullable(),
44
+ kind: DirectoryKind,
45
+ // Scope-dependent content hash, never the count/revision of hidden evidence.
46
+ revision: z.string(),
47
+ members: z.array(DirectoryIdentityMemberSchema).max(200),
48
+ alternatives: z
49
+ .array(z.strictObject({
50
+ canonical_id: Id,
51
+ members: z.array(DirectoryIdentityMemberSchema).min(1).max(200),
52
+ }))
53
+ .max(200),
54
+ bindings: z
55
+ .array(z.strictObject({
56
+ entity_id: Id,
57
+ canonical_id: Id.nullable(),
58
+ revision: Revision,
59
+ }))
60
+ .max(200),
61
+ });
62
+ export const DirectoryCanonicalQuery = z.strictObject({ id: Id, dataset_id: Id.optional() });
63
+ const ExpectedBinding = z.strictObject({
64
+ entity_id: Id,
65
+ canonical_id: Id.nullable(),
66
+ revision: Revision,
67
+ });
68
+ const Mutation = z.strictObject({
69
+ dataset_id: Id.optional(),
70
+ producer_id: Id,
71
+ request_id: Id,
72
+ reason: z.string().min(10).max(2000),
73
+ evidence_ref: Id,
74
+ expected_bindings: z.array(ExpectedBinding).min(2).max(200),
75
+ });
76
+ export const DirectoryMergeIdentities = Mutation;
77
+ export const DirectorySplitIdentity = Mutation.extend({
78
+ canonical_id: Id,
79
+ partitions: z.array(z.array(Id).min(1).max(199)).min(2).max(200),
80
+ });
81
+ export const DirectoryIdentityMutationResult = z.strictObject({
82
+ operation_id: Id,
83
+ identity_ids: z.array(Id).min(1).max(200),
84
+ });
85
+ export const DirectoryInventoryQuery = z.strictObject({
86
+ id: Id,
87
+ dataset_id: Id.optional(),
88
+ inventory_basis: z.enum(['listing_representation', 'current_roster']),
89
+ rollup: z.enum(['none', 'verified_corporate']).default('none'),
90
+ cursor: z.string().max(4096).optional(),
91
+ limit: z.coerce.number().int().min(1).max(200).default(100),
92
+ market_status: z.lazy(() => MarketStatus).optional(),
93
+ transaction_type: z.enum(['for_sale', 'for_rent']).optional(),
94
+ include_counts: z.enum(['true', 'false']).default('false'),
95
+ });
96
+ export const DirectoryInventoryPage = z.strictObject({
97
+ data: z.array(z.lazy(() => ListingSchema)),
98
+ next_cursor: z.string().nullable(),
99
+ has_more: z.boolean(),
100
+ meta: z.strictObject({
101
+ dataset_id: Id,
102
+ inventory_basis: z.enum(['listing_representation', 'current_roster']),
103
+ rollup: z.enum(['none', 'verified_corporate']),
104
+ source_namespaces: z.array(Id),
105
+ coverage: z.literal('authorized_published_listings'),
106
+ calculated_at: z.iso.datetime({ offset: true }),
107
+ source_freshness: z.array(z.lazy(() => CatalogSourceFreshnessSchema)),
108
+ definition_version: z.literal('verified-inventory-v1'),
109
+ rollup_policy: z.literal('verified-current-effective-operates-v1'),
110
+ listing_count: z.number().int().nonnegative().optional(),
111
+ active_listing_count: z.number().int().nonnegative().optional(),
112
+ relationship_fingerprint: z.string(),
113
+ }),
114
+ });
115
+ export const directoryIdentityOperations = {
116
+ directoryGetCanonicalIdentity: {
117
+ input: DirectoryCanonicalQuery,
118
+ output: DirectoryCanonicalIdentitySchema,
119
+ },
120
+ directoryMergeIdentities: {
121
+ input: DirectoryMergeIdentities,
122
+ output: DirectoryIdentityMutationResult,
123
+ },
124
+ directorySplitIdentity: {
125
+ input: DirectorySplitIdentity,
126
+ output: DirectoryIdentityMutationResult,
127
+ },
128
+ directoryListInventory: { input: DirectoryInventoryQuery, output: DirectoryInventoryPage },
129
+ };
@@ -0,0 +1,333 @@
1
+ import { z } from 'zod';
2
+ export declare const DirectorySalesContextSchema: z.ZodObject<{
3
+ schema_version: z.ZodLiteral<"directory-sales-v1">;
4
+ product_id: z.ZodString;
5
+ adoption_status: z.ZodEnum<{
6
+ unknown: "unknown";
7
+ active: "active";
8
+ prospect: "prospect";
9
+ trial: "trial";
10
+ paused: "paused";
11
+ churned: "churned";
12
+ }>;
13
+ account_id: z.ZodNullable<z.ZodString>;
14
+ verified_account_binding_id: z.ZodNullable<z.ZodString>;
15
+ last_activity_at: z.ZodNullable<z.ZodISODateTime>;
16
+ last_contacted_at: z.ZodNullable<z.ZodISODateTime>;
17
+ suppressed: z.ZodBoolean;
18
+ unsubscribed: z.ZodBoolean;
19
+ snoozed_until: z.ZodNullable<z.ZodISODateTime>;
20
+ }, z.core.$strict>;
21
+ export type DirectorySalesContext = z.infer<typeof DirectorySalesContextSchema>;
22
+ export declare const DirectorySalesProfilePolicySchema: z.ZodObject<{
23
+ reviewed: z.ZodBoolean;
24
+ required_context: z.ZodArray<z.ZodString>;
25
+ max_context_age_minutes: z.ZodNumber;
26
+ max_source_age_minutes: z.ZodNumber;
27
+ allowed_audience_ids: z.ZodArray<z.ZodString>;
28
+ allowed_subject_kinds: z.ZodArray<z.ZodEnum<{
29
+ office: "office";
30
+ professional: "professional";
31
+ organization: "organization";
32
+ }>>;
33
+ eligible_adoption_states: z.ZodArray<z.ZodEnum<{
34
+ active: "active";
35
+ prospect: "prospect";
36
+ trial: "trial";
37
+ paused: "paused";
38
+ churned: "churned";
39
+ }>>;
40
+ minimum_active_listings: z.ZodNumber;
41
+ contact_cooldown_hours: z.ZodNumber;
42
+ validity_minutes: z.ZodNumber;
43
+ purpose_family: z.ZodString;
44
+ intent: z.ZodEnum<{
45
+ adoption_outreach: "adoption_outreach";
46
+ expansion_review: "expansion_review";
47
+ reactivation_review: "reactivation_review";
48
+ }>;
49
+ }, z.core.$strict>;
50
+ export declare const DirectorySalesPlanRequest: z.ZodObject<{
51
+ dataset_id: z.ZodString;
52
+ profile_id: z.ZodString;
53
+ audience_id: z.ZodString;
54
+ subject_ids: z.ZodArray<z.ZodString>;
55
+ target_count: z.ZodDefault<z.ZodNumber>;
56
+ }, z.core.$strict>;
57
+ export declare const DirectorySalesRecommendationSchema: z.ZodObject<{
58
+ intent_id: z.ZodString;
59
+ subject: z.ZodObject<{
60
+ id: z.ZodString;
61
+ kind: z.ZodEnum<{
62
+ office: "office";
63
+ professional: "professional";
64
+ organization: "organization";
65
+ }>;
66
+ revision: z.ZodString;
67
+ }, z.core.$strict>;
68
+ target_product_id: z.ZodString;
69
+ intent: z.ZodEnum<{
70
+ adoption_outreach: "adoption_outreach";
71
+ expansion_review: "expansion_review";
72
+ reactivation_review: "reactivation_review";
73
+ }>;
74
+ authority: z.ZodLiteral<"recommendation_only">;
75
+ reasons: z.ZodArray<z.ZodString>;
76
+ metrics: z.ZodObject<{
77
+ active_listing_count: z.ZodNumber;
78
+ represented_listing_count: z.ZodNumber;
79
+ definition_version: z.ZodLiteral<"directory-sales-inventory-v1">;
80
+ coverage: z.ZodLiteral<"authorized_observed_listings">;
81
+ }, z.core.$strict>;
82
+ context_fingerprint: z.ZodString;
83
+ adoption_status: z.ZodEnum<{
84
+ unknown: "unknown";
85
+ active: "active";
86
+ prospect: "prospect";
87
+ trial: "trial";
88
+ paused: "paused";
89
+ churned: "churned";
90
+ }>;
91
+ account_id: z.ZodNullable<z.ZodString>;
92
+ verified_account_binding_id: z.ZodNullable<z.ZodString>;
93
+ contact_resolution: z.ZodObject<{
94
+ entity_id: z.ZodString;
95
+ operation: z.ZodLiteral<"directoryGetContacts">;
96
+ purpose: z.ZodEnum<{
97
+ office: "office";
98
+ professional: "professional";
99
+ organization: "organization";
100
+ }>;
101
+ requires_current_authorization: z.ZodLiteral<true>;
102
+ }, z.core.$strict>;
103
+ expires_at: z.ZodISODateTime;
104
+ }, z.core.$strict>;
105
+ export declare const DirectorySalesPlanSchema: z.ZodObject<{
106
+ id: z.ZodString;
107
+ profile_id: z.ZodString;
108
+ policy_revision: z.ZodString;
109
+ dataset_id: z.ZodString;
110
+ audience_id: z.ZodString;
111
+ status: z.ZodEnum<{
112
+ stale: "stale";
113
+ expired: "expired";
114
+ blocked: "blocked";
115
+ ready: "ready";
116
+ }>;
117
+ authority: z.ZodLiteral<"recommendation_only">;
118
+ requested_count: z.ZodNumber;
119
+ examined_count: z.ZodNumber;
120
+ selected_count: z.ZodNumber;
121
+ shortfall: z.ZodNumber;
122
+ selection_scope: z.ZodLiteral<"explicit_bounded_subject_set">;
123
+ data: z.ZodArray<z.ZodObject<{
124
+ intent_id: z.ZodString;
125
+ subject: z.ZodObject<{
126
+ id: z.ZodString;
127
+ kind: z.ZodEnum<{
128
+ office: "office";
129
+ professional: "professional";
130
+ organization: "organization";
131
+ }>;
132
+ revision: z.ZodString;
133
+ }, z.core.$strict>;
134
+ target_product_id: z.ZodString;
135
+ intent: z.ZodEnum<{
136
+ adoption_outreach: "adoption_outreach";
137
+ expansion_review: "expansion_review";
138
+ reactivation_review: "reactivation_review";
139
+ }>;
140
+ authority: z.ZodLiteral<"recommendation_only">;
141
+ reasons: z.ZodArray<z.ZodString>;
142
+ metrics: z.ZodObject<{
143
+ active_listing_count: z.ZodNumber;
144
+ represented_listing_count: z.ZodNumber;
145
+ definition_version: z.ZodLiteral<"directory-sales-inventory-v1">;
146
+ coverage: z.ZodLiteral<"authorized_observed_listings">;
147
+ }, z.core.$strict>;
148
+ context_fingerprint: z.ZodString;
149
+ adoption_status: z.ZodEnum<{
150
+ unknown: "unknown";
151
+ active: "active";
152
+ prospect: "prospect";
153
+ trial: "trial";
154
+ paused: "paused";
155
+ churned: "churned";
156
+ }>;
157
+ account_id: z.ZodNullable<z.ZodString>;
158
+ verified_account_binding_id: z.ZodNullable<z.ZodString>;
159
+ contact_resolution: z.ZodObject<{
160
+ entity_id: z.ZodString;
161
+ operation: z.ZodLiteral<"directoryGetContacts">;
162
+ purpose: z.ZodEnum<{
163
+ office: "office";
164
+ professional: "professional";
165
+ organization: "organization";
166
+ }>;
167
+ requires_current_authorization: z.ZodLiteral<true>;
168
+ }, z.core.$strict>;
169
+ expires_at: z.ZodISODateTime;
170
+ }, z.core.$strict>>;
171
+ exclusions: z.ZodRecord<z.ZodString, z.ZodNumber>;
172
+ created_at: z.ZodISODateTime;
173
+ expires_at: z.ZodISODateTime;
174
+ }, z.core.$strict>;
175
+ export declare const directorySalesOperations: {
176
+ readonly createDirectorySalesPlan: {
177
+ readonly input: z.ZodObject<{
178
+ dataset_id: z.ZodString;
179
+ profile_id: z.ZodString;
180
+ audience_id: z.ZodString;
181
+ subject_ids: z.ZodArray<z.ZodString>;
182
+ target_count: z.ZodDefault<z.ZodNumber>;
183
+ }, z.core.$strict>;
184
+ readonly output: z.ZodObject<{
185
+ id: z.ZodString;
186
+ profile_id: z.ZodString;
187
+ policy_revision: z.ZodString;
188
+ dataset_id: z.ZodString;
189
+ audience_id: z.ZodString;
190
+ status: z.ZodEnum<{
191
+ stale: "stale";
192
+ expired: "expired";
193
+ blocked: "blocked";
194
+ ready: "ready";
195
+ }>;
196
+ authority: z.ZodLiteral<"recommendation_only">;
197
+ requested_count: z.ZodNumber;
198
+ examined_count: z.ZodNumber;
199
+ selected_count: z.ZodNumber;
200
+ shortfall: z.ZodNumber;
201
+ selection_scope: z.ZodLiteral<"explicit_bounded_subject_set">;
202
+ data: z.ZodArray<z.ZodObject<{
203
+ intent_id: z.ZodString;
204
+ subject: z.ZodObject<{
205
+ id: z.ZodString;
206
+ kind: z.ZodEnum<{
207
+ office: "office";
208
+ professional: "professional";
209
+ organization: "organization";
210
+ }>;
211
+ revision: z.ZodString;
212
+ }, z.core.$strict>;
213
+ target_product_id: z.ZodString;
214
+ intent: z.ZodEnum<{
215
+ adoption_outreach: "adoption_outreach";
216
+ expansion_review: "expansion_review";
217
+ reactivation_review: "reactivation_review";
218
+ }>;
219
+ authority: z.ZodLiteral<"recommendation_only">;
220
+ reasons: z.ZodArray<z.ZodString>;
221
+ metrics: z.ZodObject<{
222
+ active_listing_count: z.ZodNumber;
223
+ represented_listing_count: z.ZodNumber;
224
+ definition_version: z.ZodLiteral<"directory-sales-inventory-v1">;
225
+ coverage: z.ZodLiteral<"authorized_observed_listings">;
226
+ }, z.core.$strict>;
227
+ context_fingerprint: z.ZodString;
228
+ adoption_status: z.ZodEnum<{
229
+ unknown: "unknown";
230
+ active: "active";
231
+ prospect: "prospect";
232
+ trial: "trial";
233
+ paused: "paused";
234
+ churned: "churned";
235
+ }>;
236
+ account_id: z.ZodNullable<z.ZodString>;
237
+ verified_account_binding_id: z.ZodNullable<z.ZodString>;
238
+ contact_resolution: z.ZodObject<{
239
+ entity_id: z.ZodString;
240
+ operation: z.ZodLiteral<"directoryGetContacts">;
241
+ purpose: z.ZodEnum<{
242
+ office: "office";
243
+ professional: "professional";
244
+ organization: "organization";
245
+ }>;
246
+ requires_current_authorization: z.ZodLiteral<true>;
247
+ }, z.core.$strict>;
248
+ expires_at: z.ZodISODateTime;
249
+ }, z.core.$strict>>;
250
+ exclusions: z.ZodRecord<z.ZodString, z.ZodNumber>;
251
+ created_at: z.ZodISODateTime;
252
+ expires_at: z.ZodISODateTime;
253
+ }, z.core.$strict>;
254
+ };
255
+ readonly getDirectorySalesPlan: {
256
+ readonly input: z.ZodObject<{
257
+ id: z.ZodString;
258
+ }, z.core.$strict>;
259
+ readonly output: z.ZodObject<{
260
+ id: z.ZodString;
261
+ profile_id: z.ZodString;
262
+ policy_revision: z.ZodString;
263
+ dataset_id: z.ZodString;
264
+ audience_id: z.ZodString;
265
+ status: z.ZodEnum<{
266
+ stale: "stale";
267
+ expired: "expired";
268
+ blocked: "blocked";
269
+ ready: "ready";
270
+ }>;
271
+ authority: z.ZodLiteral<"recommendation_only">;
272
+ requested_count: z.ZodNumber;
273
+ examined_count: z.ZodNumber;
274
+ selected_count: z.ZodNumber;
275
+ shortfall: z.ZodNumber;
276
+ selection_scope: z.ZodLiteral<"explicit_bounded_subject_set">;
277
+ data: z.ZodArray<z.ZodObject<{
278
+ intent_id: z.ZodString;
279
+ subject: z.ZodObject<{
280
+ id: z.ZodString;
281
+ kind: z.ZodEnum<{
282
+ office: "office";
283
+ professional: "professional";
284
+ organization: "organization";
285
+ }>;
286
+ revision: z.ZodString;
287
+ }, z.core.$strict>;
288
+ target_product_id: z.ZodString;
289
+ intent: z.ZodEnum<{
290
+ adoption_outreach: "adoption_outreach";
291
+ expansion_review: "expansion_review";
292
+ reactivation_review: "reactivation_review";
293
+ }>;
294
+ authority: z.ZodLiteral<"recommendation_only">;
295
+ reasons: z.ZodArray<z.ZodString>;
296
+ metrics: z.ZodObject<{
297
+ active_listing_count: z.ZodNumber;
298
+ represented_listing_count: z.ZodNumber;
299
+ definition_version: z.ZodLiteral<"directory-sales-inventory-v1">;
300
+ coverage: z.ZodLiteral<"authorized_observed_listings">;
301
+ }, z.core.$strict>;
302
+ context_fingerprint: z.ZodString;
303
+ adoption_status: z.ZodEnum<{
304
+ unknown: "unknown";
305
+ active: "active";
306
+ prospect: "prospect";
307
+ trial: "trial";
308
+ paused: "paused";
309
+ churned: "churned";
310
+ }>;
311
+ account_id: z.ZodNullable<z.ZodString>;
312
+ verified_account_binding_id: z.ZodNullable<z.ZodString>;
313
+ contact_resolution: z.ZodObject<{
314
+ entity_id: z.ZodString;
315
+ operation: z.ZodLiteral<"directoryGetContacts">;
316
+ purpose: z.ZodEnum<{
317
+ office: "office";
318
+ professional: "professional";
319
+ organization: "organization";
320
+ }>;
321
+ requires_current_authorization: z.ZodLiteral<true>;
322
+ }, z.core.$strict>;
323
+ expires_at: z.ZodISODateTime;
324
+ }, z.core.$strict>>;
325
+ exclusions: z.ZodRecord<z.ZodString, z.ZodNumber>;
326
+ created_at: z.ZodISODateTime;
327
+ expires_at: z.ZodISODateTime;
328
+ }, z.core.$strict>;
329
+ };
330
+ };
331
+ export type DirectorySalesPlanInput = z.output<typeof DirectorySalesPlanRequest>;
332
+ export type DirectorySalesPlan = z.infer<typeof DirectorySalesPlanSchema>;
333
+ export type DirectorySalesRecommendation = z.infer<typeof DirectorySalesRecommendationSchema>;
@@ -0,0 +1,102 @@
1
+ import { z } from 'zod';
2
+ const Id = z.string().min(1).max(256), Time = z.iso.datetime({ offset: true });
3
+ export const DirectorySalesContextSchema = z
4
+ .strictObject({
5
+ schema_version: z.literal('directory-sales-v1'),
6
+ product_id: Id,
7
+ adoption_status: z.enum(['prospect', 'trial', 'active', 'paused', 'churned', 'unknown']),
8
+ account_id: Id.nullable(),
9
+ verified_account_binding_id: Id.nullable(),
10
+ last_activity_at: Time.nullable(),
11
+ last_contacted_at: Time.nullable(),
12
+ suppressed: z.boolean(),
13
+ unsubscribed: z.boolean(),
14
+ snoozed_until: Time.nullable(),
15
+ })
16
+ .refine((value) => (value.account_id === null) === (value.verified_account_binding_id === null), {
17
+ message: 'Account and verified binding must be supplied together',
18
+ });
19
+ export const DirectorySalesProfilePolicySchema = z.strictObject({
20
+ reviewed: z.boolean(),
21
+ required_context: z.array(Id).min(1).max(10),
22
+ max_context_age_minutes: z.number().int().min(1).max(1440),
23
+ max_source_age_minutes: z.number().int().min(1).max(720),
24
+ allowed_audience_ids: z.array(Id).min(1).max(100),
25
+ allowed_subject_kinds: z
26
+ .array(z.enum(['professional', 'office', 'organization']))
27
+ .min(1)
28
+ .max(3),
29
+ eligible_adoption_states: z
30
+ .array(z.enum(['prospect', 'trial', 'active', 'paused', 'churned']))
31
+ .min(1)
32
+ .max(5),
33
+ minimum_active_listings: z.number().int().min(1).max(1000000),
34
+ contact_cooldown_hours: z.number().int().min(0).max(8760),
35
+ validity_minutes: z.number().int().min(1).max(1440),
36
+ purpose_family: Id,
37
+ intent: z.enum(['adoption_outreach', 'expansion_review', 'reactivation_review']),
38
+ });
39
+ export const DirectorySalesPlanRequest = z.strictObject({
40
+ dataset_id: Id,
41
+ profile_id: Id,
42
+ audience_id: Id,
43
+ subject_ids: z
44
+ .array(Id)
45
+ .min(1)
46
+ .max(500)
47
+ .refine((values) => new Set(values).size === values.length, {
48
+ message: 'Duplicate subjects are not allowed',
49
+ }),
50
+ target_count: z.number().int().min(1).max(500).default(100),
51
+ });
52
+ export const DirectorySalesRecommendationSchema = z.strictObject({
53
+ intent_id: Id,
54
+ subject: z.strictObject({
55
+ id: Id,
56
+ kind: z.enum(['professional', 'office', 'organization']),
57
+ revision: z.string(),
58
+ }),
59
+ target_product_id: Id,
60
+ intent: z.enum(['adoption_outreach', 'expansion_review', 'reactivation_review']),
61
+ authority: z.literal('recommendation_only'),
62
+ reasons: z.array(z.string()),
63
+ metrics: z.strictObject({
64
+ active_listing_count: z.number().int().nonnegative(),
65
+ represented_listing_count: z.number().int().nonnegative(),
66
+ definition_version: z.literal('directory-sales-inventory-v1'),
67
+ coverage: z.literal('authorized_observed_listings'),
68
+ }),
69
+ context_fingerprint: z.string(),
70
+ adoption_status: DirectorySalesContextSchema.shape.adoption_status,
71
+ account_id: Id.nullable(),
72
+ verified_account_binding_id: Id.nullable(),
73
+ contact_resolution: z.strictObject({
74
+ entity_id: Id,
75
+ operation: z.literal('directoryGetContacts'),
76
+ purpose: z.enum(['professional', 'office', 'organization']),
77
+ requires_current_authorization: z.literal(true),
78
+ }),
79
+ expires_at: Time,
80
+ });
81
+ export const DirectorySalesPlanSchema = z.strictObject({
82
+ id: Id,
83
+ profile_id: Id,
84
+ policy_revision: z.string(),
85
+ dataset_id: Id,
86
+ audience_id: Id,
87
+ status: z.enum(['ready', 'blocked', 'stale', 'expired']),
88
+ authority: z.literal('recommendation_only'),
89
+ requested_count: z.number().int(),
90
+ examined_count: z.number().int(),
91
+ selected_count: z.number().int(),
92
+ shortfall: z.number().int(),
93
+ selection_scope: z.literal('explicit_bounded_subject_set'),
94
+ data: z.array(DirectorySalesRecommendationSchema),
95
+ exclusions: z.record(z.string(), z.number().int().nonnegative()),
96
+ created_at: Time,
97
+ expires_at: Time,
98
+ });
99
+ export const directorySalesOperations = {
100
+ createDirectorySalesPlan: { input: DirectorySalesPlanRequest, output: DirectorySalesPlanSchema },
101
+ getDirectorySalesPlan: { input: z.strictObject({ id: Id }), output: DirectorySalesPlanSchema },
102
+ };