@dalmore/api-contracts 0.0.0-dev.d070175 → 0.0.0-dev.d29fb02

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 (188) hide show
  1. package/README.md +23 -19
  2. package/common/types/account-contact.types.ts +2 -1
  3. package/common/types/account-manager.types.ts +3 -7
  4. package/common/types/account-setting.types.ts +65 -0
  5. package/common/types/account.types.ts +1 -0
  6. package/common/types/auth.types.ts +7 -18
  7. package/common/types/bonus-tier.types.ts +9 -0
  8. package/common/types/common.types.ts +26 -0
  9. package/common/types/contact-us.types.ts +6 -2
  10. package/common/types/covered-person.types.ts +2 -1
  11. package/common/types/escrow-account.types.ts +3 -3
  12. package/common/types/individuals.types.ts +3 -2
  13. package/common/types/investor-account.types.ts +2 -1
  14. package/common/types/invite.types.ts +27 -1
  15. package/common/types/issuer-offering.types.ts +5 -8
  16. package/common/types/legal-entity.types.ts +3 -2
  17. package/common/types/mail-template.types.ts +34 -0
  18. package/common/types/note.types.ts +1 -1
  19. package/common/types/notification.types.ts +525 -29
  20. package/common/types/site-settings.types.ts +2 -1
  21. package/common/types/trade.types.ts +47 -1
  22. package/common/types/trusted-contact.types.ts +7 -7
  23. package/common/types/user.types.ts +2 -5
  24. package/index.ts +10 -0
  25. package/package.json +23 -32
  26. package/contracts/compliance/account-contacts/index.ts +0 -82
  27. package/contracts/compliance/account-managers/index.ts +0 -142
  28. package/contracts/compliance/accounts/index.ts +0 -187
  29. package/contracts/compliance/activities/index.ts +0 -55
  30. package/contracts/compliance/aic/index.ts +0 -60
  31. package/contracts/compliance/api-keys/index.ts +0 -91
  32. package/contracts/compliance/assets/index.ts +0 -122
  33. package/contracts/compliance/auth/index.ts +0 -134
  34. package/contracts/compliance/batch-jobs/index.ts +0 -62
  35. package/contracts/compliance/bonus-tiers/index.ts +0 -74
  36. package/contracts/compliance/checklist/index.ts +0 -87
  37. package/contracts/compliance/checklist-items/index.ts +0 -86
  38. package/contracts/compliance/covered-persons/index.ts +0 -97
  39. package/contracts/compliance/dashboard/index.ts +0 -111
  40. package/contracts/compliance/data-records/index.ts +0 -116
  41. package/contracts/compliance/data-rooms/index.ts +0 -113
  42. package/contracts/compliance/default-theme-configs/index.ts +0 -95
  43. package/contracts/compliance/disbursement/index.ts +0 -165
  44. package/contracts/compliance/disbursement-approval-users/index.ts +0 -84
  45. package/contracts/compliance/disbursement-transactions/index.ts +0 -37
  46. package/contracts/compliance/domain-filters/index.ts +0 -117
  47. package/contracts/compliance/email-themes/index.ts +0 -284
  48. package/contracts/compliance/escrow-accounts/index.ts +0 -85
  49. package/contracts/compliance/exchange-api-keys/index.ts +0 -129
  50. package/contracts/compliance/exchange-imports/index.ts +0 -137
  51. package/contracts/compliance/files/index.ts +0 -267
  52. package/contracts/compliance/files-public/index.ts +0 -188
  53. package/contracts/compliance/health/index.ts +0 -26
  54. package/contracts/compliance/index.ts +0 -147
  55. package/contracts/compliance/individuals/index.ts +0 -57
  56. package/contracts/compliance/investor-accounts/index.ts +0 -141
  57. package/contracts/compliance/invites/index.ts +0 -137
  58. package/contracts/compliance/issuer-bank-accounts/index.ts +0 -81
  59. package/contracts/compliance/issuer-payment-methods/index.ts +0 -81
  60. package/contracts/compliance/issuers/index.ts +0 -97
  61. package/contracts/compliance/job-items/index.ts +0 -58
  62. package/contracts/compliance/jobs/index.ts +0 -59
  63. package/contracts/compliance/kyb/index.ts +0 -54
  64. package/contracts/compliance/kyc/index.ts +0 -77
  65. package/contracts/compliance/legal-entities/index.ts +0 -57
  66. package/contracts/compliance/login-histories/index.ts +0 -37
  67. package/contracts/compliance/notes/index.ts +0 -69
  68. package/contracts/compliance/notion-databases/index.ts +0 -107
  69. package/contracts/compliance/notion-pages/index.ts +0 -105
  70. package/contracts/compliance/offering-reports/index.ts +0 -149
  71. package/contracts/compliance/offerings/index.ts +0 -233
  72. package/contracts/compliance/pages/index.ts +0 -178
  73. package/contracts/compliance/payment-methods/index.ts +0 -57
  74. package/contracts/compliance/rejection-reasons/index.ts +0 -32
  75. package/contracts/compliance/review/index.ts +0 -169
  76. package/contracts/compliance/roles/index.ts +0 -34
  77. package/contracts/compliance/secondary-customers/index.ts +0 -77
  78. package/contracts/compliance/secondary-orders/index.ts +0 -60
  79. package/contracts/compliance/secondary-trades/index.ts +0 -100
  80. package/contracts/compliance/secure-requests/index.ts +0 -54
  81. package/contracts/compliance/signer/index.ts +0 -369
  82. package/contracts/compliance/site-links/index.ts +0 -128
  83. package/contracts/compliance/site-settings/index.ts +0 -669
  84. package/contracts/compliance/sites/index.ts +0 -56
  85. package/contracts/compliance/state-machine/index.ts +0 -94
  86. package/contracts/compliance/tasks/index.ts +0 -91
  87. package/contracts/compliance/third-parties/index.ts +0 -52
  88. package/contracts/compliance/trade-line-items/index.ts +0 -59
  89. package/contracts/compliance/trades/index.ts +0 -230
  90. package/contracts/compliance/transactions/index.ts +0 -161
  91. package/contracts/compliance/user-manuals/index.ts +0 -271
  92. package/contracts/compliance/user-settings/index.ts +0 -189
  93. package/contracts/compliance/users/index.ts +0 -221
  94. package/contracts/compliance/webhooks/index.ts +0 -41
  95. package/contracts/compliance-apikey/accounts/index.ts +0 -58
  96. package/contracts/compliance-apikey/index.ts +0 -14
  97. package/contracts/index.ts +0 -6
  98. package/contracts/investors/account-contacts/index.ts +0 -58
  99. package/contracts/investors/aic/index.ts +0 -59
  100. package/contracts/investors/assets/index.ts +0 -61
  101. package/contracts/investors/auth/index.ts +0 -116
  102. package/contracts/investors/bonus-tiers/index.ts +0 -55
  103. package/contracts/investors/cart/index.ts +0 -75
  104. package/contracts/investors/contact-us/index.ts +0 -48
  105. package/contracts/investors/data-records/index.ts +0 -113
  106. package/contracts/investors/data-rooms/index.ts +0 -96
  107. package/contracts/investors/files/index.ts +0 -167
  108. package/contracts/investors/files-public/index.ts +0 -185
  109. package/contracts/investors/index.ts +0 -72
  110. package/contracts/investors/individuals/index.ts +0 -121
  111. package/contracts/investors/investor-accounts/index.ts +0 -110
  112. package/contracts/investors/issuer-payment-methods/index.ts +0 -36
  113. package/contracts/investors/issuers/index.ts +0 -30
  114. package/contracts/investors/legal-entities/index.ts +0 -93
  115. package/contracts/investors/notes/index.ts +0 -69
  116. package/contracts/investors/offerings/index.ts +0 -93
  117. package/contracts/investors/pages/index.ts +0 -88
  118. package/contracts/investors/payment-methods/index.ts +0 -149
  119. package/contracts/investors/portfolios/index.ts +0 -53
  120. package/contracts/investors/sites/index.ts +0 -96
  121. package/contracts/investors/tasks/index.ts +0 -111
  122. package/contracts/investors/trade-line-items/index.ts +0 -75
  123. package/contracts/investors/trades/index.ts +0 -114
  124. package/contracts/investors/transactions/index.ts +0 -37
  125. package/contracts/investors/trusted-contacts/index.ts +0 -93
  126. package/contracts/investors/user-manuals/index.ts +0 -62
  127. package/contracts/investors/user-settings/index.ts +0 -170
  128. package/contracts/investors/users/index.ts +0 -45
  129. package/contracts/investors/webhooks/index.ts +0 -30
  130. package/contracts/issuers/account-contacts/index.ts +0 -76
  131. package/contracts/issuers/account-integrations/index.ts +0 -97
  132. package/contracts/issuers/accounts/index.ts +0 -97
  133. package/contracts/issuers/activities/index.ts +0 -54
  134. package/contracts/issuers/aic/index.ts +0 -39
  135. package/contracts/issuers/api-key-logs/index.ts +0 -53
  136. package/contracts/issuers/api-keys/index.ts +0 -93
  137. package/contracts/issuers/assets/index.ts +0 -122
  138. package/contracts/issuers/auth/index.ts +0 -152
  139. package/contracts/issuers/bonus-tiers/index.ts +0 -73
  140. package/contracts/issuers/contact-us/index.ts +0 -48
  141. package/contracts/issuers/covered-persons/index.ts +0 -136
  142. package/contracts/issuers/dashboard/index.ts +0 -72
  143. package/contracts/issuers/data-records/index.ts +0 -257
  144. package/contracts/issuers/data-rooms/index.ts +0 -134
  145. package/contracts/issuers/disbursement-approval-users/index.ts +0 -82
  146. package/contracts/issuers/disbursement-transactions/index.ts +0 -53
  147. package/contracts/issuers/disbursements/index.ts +0 -189
  148. package/contracts/issuers/email-themes/index.ts +0 -242
  149. package/contracts/issuers/escrow-accounts/index.ts +0 -81
  150. package/contracts/issuers/exchange-api-keys/index.ts +0 -144
  151. package/contracts/issuers/files/index.ts +0 -166
  152. package/contracts/issuers/files-public/index.ts +0 -166
  153. package/contracts/issuers/health/index.ts +0 -24
  154. package/contracts/issuers/index.ts +0 -112
  155. package/contracts/issuers/investor-accounts/index.ts +0 -148
  156. package/contracts/issuers/invites/index.ts +0 -129
  157. package/contracts/issuers/issuer/index.ts +0 -94
  158. package/contracts/issuers/issuer-bank-accounts/index.ts +0 -81
  159. package/contracts/issuers/issuer-payment-methods/index.ts +0 -136
  160. package/contracts/issuers/kyc/index.ts +0 -38
  161. package/contracts/issuers/login-histories/index.ts +0 -51
  162. package/contracts/issuers/notes/index.ts +0 -69
  163. package/contracts/issuers/offerings/index.ts +0 -206
  164. package/contracts/issuers/pages/index.ts +0 -138
  165. package/contracts/issuers/payment-methods/index.ts +0 -61
  166. package/contracts/issuers/portfolios/index.ts +0 -36
  167. package/contracts/issuers/rejection-reasons/index.ts +0 -32
  168. package/contracts/issuers/review/index.ts +0 -63
  169. package/contracts/issuers/secondary-customers/index.ts +0 -55
  170. package/contracts/issuers/secondary-orders/index.ts +0 -57
  171. package/contracts/issuers/secondary-trades/index.ts +0 -57
  172. package/contracts/issuers/secure-requests/index.ts +0 -34
  173. package/contracts/issuers/site-links/index.ts +0 -116
  174. package/contracts/issuers/site-settings/index.ts +0 -585
  175. package/contracts/issuers/sites/index.ts +0 -32
  176. package/contracts/issuers/tasks/index.ts +0 -111
  177. package/contracts/issuers/trades/index.ts +0 -132
  178. package/contracts/issuers/transactions/index.ts +0 -158
  179. package/contracts/issuers/user-manuals/index.ts +0 -62
  180. package/contracts/issuers/user-settings/index.ts +0 -170
  181. package/contracts/issuers/users/index.ts +0 -126
  182. package/contracts/secondaries/accounts/index.ts +0 -58
  183. package/contracts/secondaries/index.ts +0 -23
  184. package/contracts/secondaries/secondary-customers/index.ts +0 -55
  185. package/contracts/secondaries/secondary-issuers/index.ts +0 -94
  186. package/contracts/secondaries/secondary-orders/index.ts +0 -56
  187. package/contracts/secondaries/secondary-securities/index.ts +0 -95
  188. package/contracts/secondaries/secondary-trades/index.ts +0 -56
@@ -1,58 +1,554 @@
1
1
  import { extendZodWithOpenApi } from '@anatine/zod-openapi';
2
2
  import { z } from 'zod';
3
3
  import { TypeID } from 'typeid-js';
4
- import { accountIdSchema } from './account.types';
4
+ import { IBaseEntity } from './entity.types';
5
+ import { IPaginationMeta, StringToBooleanSchema } from './common.types';
6
+ import {
7
+ ActivityTypeAction,
8
+ ActivityTypeCategory,
9
+ ActivityTypeResource,
10
+ } from './activity.types';
5
11
 
6
12
  extendZodWithOpenApi(z);
7
- export enum NotificationChannel {
13
+
14
+ export enum NotificationChannelType {
8
15
  EMAIL = 'EMAIL',
9
16
  SLACK = 'SLACK',
17
+ WEBHOOK = 'WEBHOOK',
10
18
  }
11
- export enum NotificationType {
12
- COMPLIANCE_TRADE_ALERT = 'COMPLIANCE_TRADE_ALERT',
13
- TRADE_ALERT = 'TRADE_ALERT',
19
+
20
+ export enum NotificationRecordStatus {
21
+ PENDING = 'PENDING',
22
+ SENT = 'SENT',
23
+ FAILED = 'FAILED',
14
24
  }
15
25
 
16
- export const notificationIdSchema = z.string().refine(
26
+ export type TriggerExclusion = {
27
+ category?: ActivityTypeCategory;
28
+ resource?: ActivityTypeResource;
29
+ action?: ActivityTypeAction;
30
+ };
31
+
32
+ // Excluded notifications
33
+ export const EXCLUDED_NOTIFICATION_TRIGGERS: TriggerExclusion[] = [
34
+ // { action: ActivityTypeAction.RE_SIGN },
35
+ // { action: ActivityTypeAction.RESET_2FA },
36
+ // { action: ActivityTypeAction.LOGIN },
37
+ // { action: ActivityTypeAction.RESET_PASSWORD },
38
+ // { action: ActivityTypeAction.TWO_FACTOR_LOGIN },
39
+ // { action: ActivityTypeAction.FORGOT_PASSWORD },
40
+ // { resource: ActivityTypeResource.USERS },
41
+ // { resource: ActivityTypeResource.THEME_SETTINGS },
42
+ ];
43
+
44
+ export const ISSUER_ALLOWED_CATEGORIES: ActivityTypeCategory[] = [
45
+ ActivityTypeCategory.ISSUER,
46
+ ];
47
+
48
+ /**
49
+ * Checks if an activity type should be excluded from notifications.
50
+ *
51
+ * Each exclusion pattern can specify any combination of category, resource, and action.
52
+ * Omitted fields act as wildcards (match any value).
53
+ *
54
+ * @example
55
+ * // Excludes ALL activities with action RE_SIGN (any category, any resource)
56
+ * { action: ActivityTypeAction.RE_SIGN }
57
+ *
58
+ * @example
59
+ * // Excludes ALL USERS activities (any category, any action)
60
+ * { resource: ActivityTypeResource.USERS }
61
+ *
62
+ * @example
63
+ * // Excludes only COMPLIANCE + TRADES + CREATE (all three must match)
64
+ * { category: ActivityTypeCategory.COMPLIANCE, resource: ActivityTypeResource.TRADES, action: ActivityTypeAction.CREATE }
65
+ *
66
+ * @param category - The activity type category to check
67
+ * @param resource - The activity type resource to check
68
+ * @param action - The activity type action to check
69
+ * @param exclusions - Array of exclusion patterns (defaults to EXCLUDED_NOTIFICATION_TRIGGERS)
70
+ * @returns true if the activity type matches any exclusion pattern
71
+ */
72
+ export function isTriggerExcluded(
73
+ category: string,
74
+ resource: string,
75
+ action: string,
76
+ exclusions: TriggerExclusion[] = EXCLUDED_NOTIFICATION_TRIGGERS,
77
+ ): boolean {
78
+ return exclusions.some((exclusion) => {
79
+ const categoryMatch =
80
+ !exclusion.category || exclusion.category === category;
81
+ const resourceMatch =
82
+ !exclusion.resource || exclusion.resource === resource;
83
+ const actionMatch = !exclusion.action || exclusion.action === action;
84
+ return categoryMatch && resourceMatch && actionMatch;
85
+ });
86
+ }
87
+
88
+ /**
89
+ * Checks if a category is allowed for notification triggers.
90
+ *
91
+ * Used to restrict which activity type categories a user can subscribe to.
92
+ * For example, issuers can only subscribe to ISSUER category triggers.
93
+ *
94
+ * @param category - The activity type category to check
95
+ * @param allowedCategories - Optional array of allowed categories. If empty or undefined, all categories are allowed.
96
+ * @returns true if the category is allowed (or if no restrictions are set)
97
+ */
98
+ export function isCategoryAllowed(
99
+ category: string,
100
+ allowedCategories?: ActivityTypeCategory[],
101
+ ): boolean {
102
+ if (!allowedCategories || allowedCategories.length === 0) {
103
+ return true;
104
+ }
105
+ return allowedCategories.includes(category as ActivityTypeCategory);
106
+ }
107
+
108
+ export const notificationChannelIdSchema = z.string().refine(
17
109
  (value) => {
18
110
  try {
19
111
  const tid = TypeID.fromString(value);
20
- return tid.getType() === 'notification';
112
+ return tid.getType() === 'notification_channel';
21
113
  } catch {
22
114
  return false;
23
115
  }
24
116
  },
25
117
  {
26
118
  message:
27
- 'Invalid notification ID format. Must be a valid TypeID with "notification" prefix.',
119
+ 'Invalid notification channel ID format. Must be a valid TypeID with "notification_channel" prefix.',
28
120
  },
29
121
  );
30
122
 
31
- export const SendNotificationZod = z.object({
32
- message: z.string(),
123
+ export const notificationChannelTriggerIdSchema = z.string().refine(
124
+ (value) => {
125
+ try {
126
+ const tid = TypeID.fromString(value);
127
+ return tid.getType() === 'notification_channel_trigger';
128
+ } catch {
129
+ return false;
130
+ }
131
+ },
132
+ {
133
+ message:
134
+ 'Invalid notification channel trigger ID format. Must be a valid TypeID with "notification_channel_trigger" prefix.',
135
+ },
136
+ );
137
+
138
+ export const notificationRecordIdSchema = z.string().refine(
139
+ (value) => {
140
+ try {
141
+ const tid = TypeID.fromString(value);
142
+ return tid.getType() === 'notification_record';
143
+ } catch {
144
+ return false;
145
+ }
146
+ },
147
+ {
148
+ message:
149
+ 'Invalid notification record ID format. Must be a valid TypeID with "notification_record" prefix.',
150
+ },
151
+ );
152
+
153
+ export const EmailChannelSettingsSchema = z.object({
154
+ recipients: z
155
+ .array(z.string().email())
156
+ .min(1)
157
+ .max(100, 'At most 100 recipients are allowed'),
158
+ subjectTemplate: z.string().optional(),
159
+ });
160
+ export type EmailChannelSettings = z.infer<typeof EmailChannelSettingsSchema>;
161
+
162
+ export const SlackChannelSettingsSchema = z
163
+ .object({
164
+ webhookUrl: z.string().url().optional(),
165
+ channelEmail: z.string().email().optional(),
166
+ })
167
+ .refine((data) => data.webhookUrl || data.channelEmail, {
168
+ message: 'Either webhookUrl or channelEmail is required',
169
+ });
170
+ export type SlackChannelSettings = z.infer<typeof SlackChannelSettingsSchema>;
171
+
172
+ export const WebhookChannelSettingsSchema = z.object({
173
+ url: z.string().url(),
174
+ method: z.enum(['POST', 'PUT']),
175
+ headers: z.record(z.string()).optional(),
176
+ authType: z.enum(['none', 'bearer', 'basic', 'api_key']).default('none'),
177
+ authValue: z.string().optional(),
178
+ });
179
+ export type WebhookChannelSettings = z.infer<
180
+ typeof WebhookChannelSettingsSchema
181
+ >;
182
+
183
+ export const NotificationChannelSettingsSchema = z.discriminatedUnion('type', [
184
+ z.object({
185
+ type: z.literal(NotificationChannelType.EMAIL),
186
+ config: EmailChannelSettingsSchema,
187
+ }),
188
+ z.object({
189
+ type: z.literal(NotificationChannelType.SLACK),
190
+ config: SlackChannelSettingsSchema,
191
+ }),
192
+ z.object({
193
+ type: z.literal(NotificationChannelType.WEBHOOK),
194
+ config: WebhookChannelSettingsSchema,
195
+ }),
196
+ ]);
197
+ export type NotificationChannelSettings = z.infer<
198
+ typeof NotificationChannelSettingsSchema
199
+ >;
200
+
201
+ export const EmailNotificationPayloadSchema = z.object({
202
+ to: z.array(z.string().email()),
33
203
  subject: z.string(),
34
- buttonText: z.string(),
35
- link: z.string(),
36
- debug: z.boolean().default(false),
37
- notificationChannel: z.nativeEnum(NotificationChannel),
38
- channelName: z.string(),
39
- channelEmail: z.string().email(),
40
- notificationType: z.nativeEnum(NotificationType),
204
+ body: z.string(),
205
+ html: z.string().optional(),
206
+ });
207
+ export type EmailNotificationPayload = z.infer<
208
+ typeof EmailNotificationPayloadSchema
209
+ >;
210
+
211
+ /**
212
+ * Payload for Slack notifications sent via Incoming Webhooks.
213
+ *
214
+ * @property webhookUrl - Slack Incoming Webhook URL
215
+ * @property channelEmail - Slack channel email integration (alternative delivery method)
216
+ * @property message - Plain text message (also serves as fallback for notifications)
217
+ * @property blocks - Optional Slack Block Kit elements for rich message formatting.
218
+ * Blocks allow structured layouts with headers, sections, images, buttons, etc.
219
+ * @see https://docs.slack.dev/messaging/creating-interactive-messages
220
+ */
221
+ export const SlackNotificationPayloadSchema = z.object({
222
+ webhookUrl: z.string().url().optional(),
223
+ channelEmail: z.string().email().optional(),
224
+ message: z.string(),
225
+ blocks: z.array(z.record(z.unknown())).optional(),
226
+ });
227
+ export type SlackNotificationPayload = z.infer<
228
+ typeof SlackNotificationPayloadSchema
229
+ >;
230
+
231
+ export const WebhookNotificationPayloadSchema = z.object({
232
+ url: z.string().url(),
233
+ method: z.enum(['POST', 'PUT']),
234
+ headers: z.record(z.string()),
235
+ body: z.record(z.unknown()),
41
236
  });
42
- export type SendNotificationZod = z.infer<typeof SendNotificationZod>;
237
+ export type WebhookNotificationPayload = z.infer<
238
+ typeof WebhookNotificationPayloadSchema
239
+ >;
240
+
241
+ export const NotificationPayloadSchema = z.discriminatedUnion('type', [
242
+ z.object({
243
+ type: z.literal(NotificationChannelType.EMAIL),
244
+ data: EmailNotificationPayloadSchema,
245
+ }),
246
+ z.object({
247
+ type: z.literal(NotificationChannelType.SLACK),
248
+ data: SlackNotificationPayloadSchema,
249
+ }),
250
+ z.object({
251
+ type: z.literal(NotificationChannelType.WEBHOOK),
252
+ data: WebhookNotificationPayloadSchema,
253
+ }),
254
+ ]);
255
+ export type NotificationPayload = z.infer<typeof NotificationPayloadSchema>;
43
256
 
44
- const SlackSettingsSchema = z.object({
45
- channelName: z.string().min(1).max(50),
46
- channelEmail: z.string().email(),
257
+ export const EmailNotificationResponseSchema = z.object({
258
+ messageId: z.string().optional(),
259
+ accepted: z.array(z.string()).optional(),
260
+ rejected: z.array(z.string()).optional(),
47
261
  });
262
+ export type EmailNotificationResponse = z.infer<
263
+ typeof EmailNotificationResponseSchema
264
+ >;
48
265
 
49
- export const PostNotificationZod = z.object({
50
- accountId: accountIdSchema,
51
- notificationChannel: z.literal(NotificationChannel.SLACK), // Only Slack for now
52
- notificationType: z.nativeEnum(NotificationType),
53
- enabled: z.boolean().default(true),
54
- debug: z.boolean().default(false),
55
- settings: SlackSettingsSchema,
266
+ export const SlackNotificationResponseSchema = z.object({
267
+ ok: z.boolean().optional(),
268
+ error: z.string().optional(),
269
+ responseBody: z.string().optional(),
56
270
  });
271
+ export type SlackNotificationResponse = z.infer<
272
+ typeof SlackNotificationResponseSchema
273
+ >;
57
274
 
58
- export type PostNotificationZod = z.infer<typeof PostNotificationZod>;
275
+ export const WebhookNotificationResponseSchema = z.object({
276
+ statusCode: z.number(),
277
+ headers: z.record(z.string()).optional(),
278
+ body: z.unknown().optional(),
279
+ });
280
+ export type WebhookNotificationResponse = z.infer<
281
+ typeof WebhookNotificationResponseSchema
282
+ >;
283
+
284
+ export const NotificationResponseSchema = z.discriminatedUnion('type', [
285
+ z.object({
286
+ type: z.literal(NotificationChannelType.EMAIL),
287
+ data: EmailNotificationResponseSchema,
288
+ }),
289
+ z.object({
290
+ type: z.literal(NotificationChannelType.SLACK),
291
+ data: SlackNotificationResponseSchema,
292
+ }),
293
+ z.object({
294
+ type: z.literal(NotificationChannelType.WEBHOOK),
295
+ data: WebhookNotificationResponseSchema,
296
+ }),
297
+ ]);
298
+ export type NotificationResponse = z.infer<typeof NotificationResponseSchema>;
299
+
300
+ export const INotificationChannelZod = IBaseEntity.extend({
301
+ id: notificationChannelIdSchema.openapi({
302
+ example: 'notification_channel_01j5y5ghx8fvc83dmx3pznq7hv',
303
+ }),
304
+ accountSettingsId: z.string().openapi({
305
+ example: 'account_setting_01j5y5ghx8fvc83dmx3pznq7hv',
306
+ }),
307
+ channelType: z.nativeEnum(NotificationChannelType).openapi({
308
+ example: NotificationChannelType.SLACK,
309
+ }),
310
+ name: z.string().openapi({
311
+ example: 'Compliance Alerts',
312
+ }),
313
+ enabled: z.boolean().openapi({
314
+ example: true,
315
+ }),
316
+ settings: NotificationChannelSettingsSchema.openapi({
317
+ example: {
318
+ type: NotificationChannelType.SLACK,
319
+ config: {
320
+ webhookUrl: 'https://hooks.slack.com/test',
321
+ },
322
+ },
323
+ }),
324
+ });
325
+ export type INotificationChannelZod = z.infer<typeof INotificationChannelZod>;
326
+
327
+ export const INotificationChannelTriggerZod = IBaseEntity.extend({
328
+ id: notificationChannelTriggerIdSchema.openapi({
329
+ example: 'notification_channel_trigger_01j5y5ghx8fvc83dmx3pznq7hv',
330
+ }),
331
+ notificationChannelId: notificationChannelIdSchema.openapi({
332
+ example: 'notification_channel_01j5y5ghx8fvc83dmx3pznq7hv',
333
+ }),
334
+ activityTypeId: z.string().openapi({
335
+ example: 'activity_type_01j5y5ghx8fvc83dmx3pznq7hv',
336
+ }),
337
+ enabled: z.boolean().openapi({
338
+ example: true,
339
+ }),
340
+ });
341
+ export type INotificationChannelTriggerZod = z.infer<
342
+ typeof INotificationChannelTriggerZod
343
+ >;
344
+
345
+ export const IAvailableTriggerZod = z.object({
346
+ activityTypeId: z.string().openapi({
347
+ example: 'activity_type_01j5y5ghx8fvc83dmx3pznq7hv',
348
+ }),
349
+ category: z.string().openapi({
350
+ example: 'COMPLIANCE',
351
+ }),
352
+ resource: z.string().openapi({
353
+ example: 'TRADES',
354
+ }),
355
+ action: z.string().openapi({
356
+ example: 'CREATE',
357
+ }),
358
+ enabled: z.boolean().openapi({
359
+ description: 'Whether the trigger is enabled for this activity type',
360
+ example: false,
361
+ }),
362
+ trigger: INotificationChannelTriggerZod.nullable().openapi({
363
+ description: 'The configured trigger, or null if not configured',
364
+ }),
365
+ });
366
+ export type IAvailableTriggerZod = z.infer<typeof IAvailableTriggerZod>;
367
+
368
+ export const IAvailableTriggersResponse = z.object({
369
+ items: z.array(IAvailableTriggerZod),
370
+ });
371
+ export type IAvailableTriggersResponse = z.infer<
372
+ typeof IAvailableTriggersResponse
373
+ >;
374
+
375
+ export const INotificationRecordZod = IBaseEntity.extend({
376
+ id: notificationRecordIdSchema.openapi({
377
+ example: 'notification_record_01j5y5ghx8fvc83dmx3pznq7hv',
378
+ }),
379
+ notificationChannelId: notificationChannelIdSchema.openapi({
380
+ example: 'notification_channel_01j5y5ghx8fvc83dmx3pznq7hv',
381
+ }),
382
+ activityId: z.string().nullable().openapi({
383
+ example: 'activity_01j5y5ghx8fvc83dmx3pznq7hv',
384
+ }),
385
+ targetTable: z.string().openapi({
386
+ example: 'trades',
387
+ }),
388
+ targetId: z.string().openapi({
389
+ example: 'trade_01j5y5ghx8fvc83dmx3pznq7hv',
390
+ }),
391
+ status: z.nativeEnum(NotificationRecordStatus).openapi({
392
+ example: NotificationRecordStatus.SENT,
393
+ }),
394
+ payload: NotificationPayloadSchema,
395
+ response: NotificationResponseSchema.nullable(),
396
+ errorMessage: z.string().nullable().openapi({
397
+ example: null,
398
+ }),
399
+ retryCount: z.number().openapi({
400
+ example: 0,
401
+ }),
402
+ sentAt: z.date().nullable().openapi({
403
+ example: new Date(),
404
+ }),
405
+ });
406
+ export type INotificationRecordZod = z.infer<typeof INotificationRecordZod>;
407
+
408
+ export const CreateChannelInputSchema = z
409
+ .object({
410
+ accountSettingsId: z.string(),
411
+ channelType: z.nativeEnum(NotificationChannelType),
412
+ name: z
413
+ .string()
414
+ .min(1, 'Name is required')
415
+ .max(255, 'Name must be less than 255 characters'),
416
+ enabled: z.boolean().optional(),
417
+ settings: NotificationChannelSettingsSchema,
418
+ })
419
+ .refine((data) => data.channelType === data.settings.type, {
420
+ message: 'Channel type must match settings type',
421
+ path: ['settings', 'type'],
422
+ });
423
+ export type CreateChannelInput = z.infer<typeof CreateChannelInputSchema>;
424
+
425
+ export const IssuerCreateChannelInputSchema = z
426
+ .object({
427
+ channelType: z.nativeEnum(NotificationChannelType),
428
+ name: z
429
+ .string()
430
+ .min(1, 'Name is required')
431
+ .max(255, 'Name must be less than 255 characters'),
432
+ enabled: z.boolean().optional(),
433
+ settings: NotificationChannelSettingsSchema,
434
+ })
435
+ .refine((data) => data.channelType === data.settings.type, {
436
+ message: 'Channel type must match settings type',
437
+ path: ['settings', 'type'],
438
+ });
439
+ export type IssuerCreateChannelInput = z.infer<
440
+ typeof IssuerCreateChannelInputSchema
441
+ >;
442
+
443
+ export const UpdateChannelInputSchema = z.object({
444
+ name: z.string().optional(),
445
+ enabled: z.boolean().optional(),
446
+ settings: NotificationChannelSettingsSchema.optional(),
447
+ });
448
+ export type UpdateChannelInput = z.infer<typeof UpdateChannelInputSchema>;
449
+
450
+ export const CreateTriggerInputSchema = z.object({
451
+ notificationChannelId: notificationChannelIdSchema,
452
+ activityTypeId: z.string(),
453
+ enabled: z.boolean().optional(),
454
+ });
455
+ export type CreateTriggerInput = z.infer<typeof CreateTriggerInputSchema>;
456
+
457
+ export const IPaginatedNotificationChannel = z.object({
458
+ items: z.array(INotificationChannelZod),
459
+ meta: IPaginationMeta,
460
+ });
461
+ export type IPaginatedNotificationChannel = z.infer<
462
+ typeof IPaginatedNotificationChannel
463
+ >;
464
+
465
+ export const IPaginatedNotificationChannelTrigger = z.object({
466
+ items: z.array(INotificationChannelTriggerZod),
467
+ meta: IPaginationMeta,
468
+ });
469
+ export type IPaginatedNotificationChannelTrigger = z.infer<
470
+ typeof IPaginatedNotificationChannelTrigger
471
+ >;
472
+
473
+ export const IPaginatedNotificationRecord = z.object({
474
+ items: z.array(INotificationRecordZod),
475
+ meta: IPaginationMeta,
476
+ });
477
+ export type IPaginatedNotificationRecord = z.infer<
478
+ typeof IPaginatedNotificationRecord
479
+ >;
480
+
481
+ export const NotificationChannelFilters = z.object({
482
+ accountSettingsId: z.string().optional(),
483
+ channelType: z.nativeEnum(NotificationChannelType).optional(),
484
+ enabled: StringToBooleanSchema.optional(),
485
+ });
486
+ export type NotificationChannelFilters = z.infer<
487
+ typeof NotificationChannelFilters
488
+ >;
489
+
490
+ export const NotificationChannelTriggerFilters = z.object({
491
+ notificationChannelId: notificationChannelIdSchema.optional(),
492
+ activityTypeId: z.string().optional(),
493
+ enabled: StringToBooleanSchema.optional(),
494
+ });
495
+ export type NotificationChannelTriggerFilters = z.infer<
496
+ typeof NotificationChannelTriggerFilters
497
+ >;
498
+
499
+ export const NotificationRecordFilters = z.object({
500
+ notificationChannelId: notificationChannelIdSchema.optional(),
501
+ status: z.nativeEnum(NotificationRecordStatus).optional(),
502
+ });
503
+ export type NotificationRecordFilters = z.infer<
504
+ typeof NotificationRecordFilters
505
+ >;
506
+
507
+ export const notificationChannelsInclude = z.enum([
508
+ 'triggers',
509
+ 'accountSettings',
510
+ 'notificationRecords',
511
+ ]);
512
+
513
+ export const NotificationChannelsIncludeQuery = z.object({
514
+ include: z
515
+ .string()
516
+ .optional()
517
+ .transform((str) => (str ? str.split(',') : []))
518
+ .refine(
519
+ (includes) =>
520
+ includes.every((include) =>
521
+ notificationChannelsInclude.options.includes(include as any),
522
+ ),
523
+ {
524
+ message: `Invalid include value. Valid values are: ${notificationChannelsInclude.options.join(', ')}`,
525
+ },
526
+ ),
527
+ });
528
+ export type NotificationChannelsIncludeQuery = z.infer<
529
+ typeof NotificationChannelsIncludeQuery
530
+ >;
531
+
532
+ export const notificationRecordsInclude = z.enum([
533
+ 'notificationChannel',
534
+ 'activity',
535
+ ]);
536
+
537
+ export const NotificationRecordsIncludeQuery = z.object({
538
+ include: z
539
+ .string()
540
+ .optional()
541
+ .transform((str) => (str ? str.split(',') : []))
542
+ .refine(
543
+ (includes) =>
544
+ includes.every((include) =>
545
+ notificationRecordsInclude.options.includes(include as any),
546
+ ),
547
+ {
548
+ message: `Invalid include value. Valid values are: ${notificationRecordsInclude.options.join(', ')}`,
549
+ },
550
+ ),
551
+ });
552
+ export type NotificationRecordsIncludeQuery = z.infer<
553
+ typeof NotificationRecordsIncludeQuery
554
+ >;
@@ -3,6 +3,7 @@ import { z } from 'zod';
3
3
  import { fileIdSchema } from './file.types';
4
4
  import {
5
5
  AccountZod,
6
+ EmailSchema,
6
7
  HexCodeColorSchema,
7
8
  IPaginationMeta,
8
9
  SubdomainSchema,
@@ -462,7 +463,7 @@ export const GetSenderEmailZod = z.object({
462
463
  export type GetSenderEmailZod = z.infer<typeof GetSenderEmailZod>;
463
464
 
464
465
  export const PatchSenderEmailZod = z.object({
465
- senderEmail: z.string().email().openapi({ example: 'example@example.com' }),
466
+ senderEmail: EmailSchema,
466
467
  });
467
468
  export type PatchSenderEmailZod = z.infer<typeof PatchSenderEmailZod>;
468
469
 
@@ -22,12 +22,13 @@ import {
22
22
  AccountStatus,
23
23
  SavedQuery,
24
24
  } from './common.types';
25
- import { offeringIdSchema } from './offering.types';
25
+ import { IOffering, offeringIdSchema } from './offering.types';
26
26
  import { IBaseEntity } from './entity.types';
27
27
  import { extendZodWithOpenApi } from '@anatine/zod-openapi';
28
28
  import { TypeID } from 'typeid-js';
29
29
  import {
30
30
  paymentMethodIdSchema,
31
+ PaymentMethodResponse,
31
32
  PaymentMethodType,
32
33
  } from './payment-methods.types';
33
34
  import {
@@ -35,6 +36,7 @@ import {
35
36
  InvestorAccountExportFilters,
36
37
  ComplianceInvestorAccountExportFilters,
37
38
  RegisteredInvestorUserRawZod,
39
+ IInvestorAccount,
38
40
  } from './investor-account.types';
39
41
  import {
40
42
  tradeLineItemIdSchema,
@@ -132,6 +134,7 @@ export const CheckResultsSchema = z.object({
132
134
  export type CheckResults = z.infer<typeof CheckResultsSchema>;
133
135
 
134
136
  export const TradeIdPrefix = 'trade';
137
+ export const TradeSnapshotIdPrefix = 'trade_snapshot';
135
138
 
136
139
  export const tradeIdSchema = z.string().refine(
137
140
  (value) => {
@@ -170,6 +173,21 @@ export const tradeIdOrTidSchema = z.union([
170
173
  .openapi({ example: 'tid_00041061050r3gg28a1c60t3gf' }),
171
174
  ]);
172
175
 
176
+ export const tradeSnapshotIdSchema = z.string().refine(
177
+ (value) => {
178
+ try {
179
+ const tid = TypeID.fromString(value);
180
+ return tid.getType() === TradeSnapshotIdPrefix;
181
+ } catch {
182
+ return false;
183
+ }
184
+ },
185
+ {
186
+ message:
187
+ 'Invalid trade snapshot ID format. Must be a valid TypeID with "trade_snapshot" prefix. ex: trade_snapshot_00041061050r3gg28a1c60t3gf',
188
+ },
189
+ );
190
+
173
191
  export enum SaStatus {
174
192
  PENDING = BaseStatus.PENDING,
175
193
  SIGNED = BaseStatus.SIGNED,
@@ -274,9 +292,21 @@ export type TradeBalanceResultZod = z.infer<typeof TradeBalanceResultZod>;
274
292
 
275
293
  export const TradeZod = IBaseEntity.extend({
276
294
  investorAccountId: z.lazy(() => investorAccountIdSchema.nullable()),
295
+ investorAccount: z
296
+ .lazy(() => IInvestorAccount)
297
+ .optional()
298
+ .nullable(),
299
+ offering: z
300
+ .lazy(() => IOffering)
301
+ .optional()
302
+ .nullable(),
277
303
  accountId: accountIdSchema.nullable(),
278
304
  offeringId: z.lazy(() => offeringIdSchema.nullable()),
279
305
  paymentMethodId: z.lazy(() => paymentMethodIdSchema).nullable(),
306
+ paymentMethod: z
307
+ .lazy(() => PaymentMethodResponse)
308
+ .optional()
309
+ .nullable(),
280
310
  userId: z.lazy(() => userIdSchema.nullable()),
281
311
  tradeNumber: z.string(),
282
312
  ip: z.string().nullable(),
@@ -342,6 +372,21 @@ export const TradeZod = IBaseEntity.extend({
342
372
  });
343
373
  export type TradeZod = z.infer<typeof TradeZod>;
344
374
 
375
+ export enum SnapshotEvent {
376
+ COMPLIANCE_APPROVED = 'COMPLIANCE_APPROVED',
377
+ COMPLIANCE_REJECTED = 'COMPLIANCE_REJECTED',
378
+ CANCELLED = 'CANCELLED',
379
+ PLACED = 'PLACED',
380
+ SETTLED = 'SETTLED',
381
+ }
382
+
383
+ export const GetTradeSnapshotEventsResponse = z.object({
384
+ events: z.array(z.nativeEnum(SnapshotEvent)),
385
+ });
386
+ export type GetTradeSnapshotEventsResponse = z.infer<
387
+ typeof GetTradeSnapshotEventsResponse
388
+ >;
389
+
345
390
  export const IPaginatedTrade = z.object({
346
391
  items: z.array(TradeZod),
347
392
  meta: IPaginationMeta,
@@ -424,6 +469,7 @@ export const TradeFiltersZod = z.object({
424
469
  .openapi({
425
470
  example: 'issuer_01jfw4q6qef30s6fpqtsxw94ya',
426
471
  }),
472
+ snapshotEvent: z.nativeEnum(SnapshotEvent).optional(),
427
473
  offeringType: z.nativeEnum(OfferingType).optional(),
428
474
  managedBy: z.nativeEnum(ManagedByType).optional(),
429
475
  platform: z.nativeEnum(Platforms).optional(),