@automate.ax/integration-contracts 0.55.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 (136) hide show
  1. package/dist/airtable/api.d.ts +19 -0
  2. package/dist/airtable/api.js +44 -0
  3. package/dist/airtable/index.d.ts +353 -0
  4. package/dist/airtable/index.js +32 -0
  5. package/dist/airtable/schemas.d.ts +716 -0
  6. package/dist/airtable/schemas.js +184 -0
  7. package/dist/asana/api.d.ts +70 -0
  8. package/dist/asana/api.js +119 -0
  9. package/dist/asana/index.d.ts +384 -0
  10. package/dist/asana/index.js +38 -0
  11. package/dist/asana/schemas.d.ts +1060 -0
  12. package/dist/asana/schemas.js +512 -0
  13. package/dist/brevo/api.d.ts +31 -0
  14. package/dist/brevo/api.js +64 -0
  15. package/dist/brevo/index.d.ts +822 -0
  16. package/dist/brevo/index.js +51 -0
  17. package/dist/brevo/schemas.d.ts +1217 -0
  18. package/dist/brevo/schemas.js +332 -0
  19. package/dist/github/index.d.ts +37 -0
  20. package/dist/github/index.js +106 -0
  21. package/dist/github/schemas.d.ts +8014 -0
  22. package/dist/github/schemas.js +113 -0
  23. package/dist/gmail/gmail.d.ts +172 -0
  24. package/dist/gmail/gmail.js +405 -0
  25. package/dist/gmail/index.d.ts +262 -0
  26. package/dist/gmail/index.js +23 -0
  27. package/dist/gmail/schemas.d.ts +444 -0
  28. package/dist/gmail/schemas.js +242 -0
  29. package/dist/google-calendar/event-schemas.d.ts +117 -0
  30. package/dist/google-calendar/event-schemas.js +13 -0
  31. package/dist/google-calendar/google-calendar.d.ts +147 -0
  32. package/dist/google-calendar/google-calendar.js +583 -0
  33. package/dist/google-calendar/index.d.ts +365 -0
  34. package/dist/google-calendar/index.js +24 -0
  35. package/dist/google-calendar/schemas.d.ts +592 -0
  36. package/dist/google-calendar/schemas.js +379 -0
  37. package/dist/google-forms/event-schemas.d.ts +105 -0
  38. package/dist/google-forms/event-schemas.js +20 -0
  39. package/dist/google-forms/google-forms.d.ts +243 -0
  40. package/dist/google-forms/google-forms.js +581 -0
  41. package/dist/google-forms/index.d.ts +141 -0
  42. package/dist/google-forms/index.js +20 -0
  43. package/dist/google-forms/schemas.d.ts +618 -0
  44. package/dist/google-forms/schemas.js +383 -0
  45. package/dist/linear/api.d.ts +63 -0
  46. package/dist/linear/api.js +88 -0
  47. package/dist/linear/index.d.ts +1338 -0
  48. package/dist/linear/index.js +55 -0
  49. package/dist/linear/schemas.d.ts +1795 -0
  50. package/dist/linear/schemas.js +760 -0
  51. package/dist/outlook/graph.d.ts +34 -0
  52. package/dist/outlook/graph.js +98 -0
  53. package/dist/outlook/index.d.ts +115 -0
  54. package/dist/outlook/index.js +11 -0
  55. package/dist/outlook/schemas.d.ts +529 -0
  56. package/dist/outlook/schemas.js +312 -0
  57. package/dist/resend/index.d.ts +707 -0
  58. package/dist/resend/index.js +54 -0
  59. package/dist/resend/schemas.d.ts +1033 -0
  60. package/dist/resend/schemas.js +377 -0
  61. package/dist/slack/index.d.ts +151 -0
  62. package/dist/slack/index.js +22 -0
  63. package/dist/slack/schemas.d.ts +641 -0
  64. package/dist/slack/schemas.js +581 -0
  65. package/dist/teams/graph.d.ts +33 -0
  66. package/dist/teams/graph.js +95 -0
  67. package/dist/teams/index.d.ts +164 -0
  68. package/dist/teams/index.js +21 -0
  69. package/dist/teams/schemas.d.ts +675 -0
  70. package/dist/teams/schemas.js +365 -0
  71. package/dist/trello/api.d.ts +69 -0
  72. package/dist/trello/api.js +140 -0
  73. package/dist/trello/event-schemas.d.ts +1101 -0
  74. package/dist/trello/event-schemas.js +233 -0
  75. package/dist/trello/index.d.ts +721 -0
  76. package/dist/trello/index.js +35 -0
  77. package/dist/trello/schemas.d.ts +283 -0
  78. package/dist/trello/schemas.js +401 -0
  79. package/dist/triggers.d.ts +26 -0
  80. package/dist/triggers.js +33 -0
  81. package/dist/vercel/index.d.ts +1065 -0
  82. package/dist/vercel/index.js +114 -0
  83. package/dist/vercel/schemas.d.ts +1518 -0
  84. package/dist/vercel/schemas.js +335 -0
  85. package/dist/whatsapp/api.d.ts +62 -0
  86. package/dist/whatsapp/api.js +121 -0
  87. package/dist/whatsapp/index.d.ts +614 -0
  88. package/dist/whatsapp/index.js +35 -0
  89. package/dist/whatsapp/schemas.d.ts +926 -0
  90. package/dist/whatsapp/schemas.js +326 -0
  91. package/package.json +169 -0
  92. package/src/airtable/api.ts +66 -0
  93. package/src/airtable/index.ts +41 -0
  94. package/src/airtable/schemas.ts +231 -0
  95. package/src/asana/api.ts +171 -0
  96. package/src/asana/index.ts +49 -0
  97. package/src/asana/schemas.ts +655 -0
  98. package/src/brevo/api.ts +89 -0
  99. package/src/brevo/index.ts +66 -0
  100. package/src/brevo/schemas.ts +449 -0
  101. package/src/github/index.ts +217 -0
  102. package/src/github/schemas.ts +435 -0
  103. package/src/gmail/gmail.ts +577 -0
  104. package/src/gmail/index.ts +26 -0
  105. package/src/gmail/schemas.ts +327 -0
  106. package/src/google-calendar/event-schemas.ts +16 -0
  107. package/src/google-calendar/google-calendar.ts +727 -0
  108. package/src/google-calendar/index.ts +27 -0
  109. package/src/google-calendar/schemas.ts +529 -0
  110. package/src/google-forms/event-schemas.ts +26 -0
  111. package/src/google-forms/google-forms.ts +683 -0
  112. package/src/google-forms/index.ts +23 -0
  113. package/src/google-forms/schemas.ts +499 -0
  114. package/src/linear/api.ts +141 -0
  115. package/src/linear/index.ts +71 -0
  116. package/src/linear/schemas.ts +1060 -0
  117. package/src/outlook/graph.ts +132 -0
  118. package/src/outlook/index.ts +14 -0
  119. package/src/outlook/schemas.ts +388 -0
  120. package/src/resend/index.ts +70 -0
  121. package/src/resend/schemas.ts +462 -0
  122. package/src/slack/index.ts +29 -0
  123. package/src/slack/schemas.ts +737 -0
  124. package/src/teams/graph.ts +130 -0
  125. package/src/teams/index.ts +27 -0
  126. package/src/teams/schemas.ts +463 -0
  127. package/src/trello/api.ts +190 -0
  128. package/src/trello/event-schemas.ts +290 -0
  129. package/src/trello/index.ts +45 -0
  130. package/src/trello/schemas.ts +507 -0
  131. package/src/triggers.ts +68 -0
  132. package/src/vercel/index.ts +147 -0
  133. package/src/vercel/schemas.ts +594 -0
  134. package/src/whatsapp/api.ts +171 -0
  135. package/src/whatsapp/index.ts +46 -0
  136. package/src/whatsapp/schemas.ts +341 -0
@@ -0,0 +1,89 @@
1
+ import { z } from "zod"
2
+
3
+ const BREVO_API_BASE_URL = "https://api.brevo.com/v3/"
4
+ const BREVO_API_ORIGIN = new URL(BREVO_API_BASE_URL).origin
5
+ const BREVO_SECRET_SCHEMA = z.object({
6
+ apiKey: z.string().min(1),
7
+ })
8
+ const BREVO_ERROR_SCHEMA = z.looseObject({
9
+ code: z.string().optional(),
10
+ message: z.string().optional(),
11
+ })
12
+
13
+ /** Options accepted by the authenticated Brevo request helper. */
14
+ export interface BrevoRequestOptions extends RequestInit {
15
+ /** Query parameters appended to the request URL. */
16
+ query?: Record<
17
+ string,
18
+ boolean | number | readonly (number | string)[] | string | undefined
19
+ >
20
+ }
21
+
22
+ /**
23
+ * Creates a small authenticated Brevo REST client.
24
+ *
25
+ * The returned helper is an escape hatch for provider endpoints that do not yet
26
+ * have a dedicated action. It returns the native `Response` after checking for
27
+ * provider errors.
28
+ *
29
+ * @param secret - Resolved Brevo integration secret.
30
+ */
31
+ export function getBrevoApi(secret: Record<string, unknown>) {
32
+ const { apiKey } = BREVO_SECRET_SCHEMA.parse(secret)
33
+
34
+ return {
35
+ /**
36
+ * Sends an authenticated request to the Brevo v3 API.
37
+ *
38
+ * @param path - Relative API path.
39
+ * @param options - Native fetch options and optional query parameters.
40
+ */
41
+ async request(path: string, options: BrevoRequestOptions = {}) {
42
+ const { query, ...requestInit } = options
43
+ const url = new URL(path.replace(/^\//, ""), BREVO_API_BASE_URL)
44
+ if (url.origin !== BREVO_API_ORIGIN) {
45
+ throw new Error("Brevo API paths must use the Brevo API origin.")
46
+ }
47
+ for (const [name, value] of Object.entries(query ?? {})) {
48
+ if (value !== undefined) {
49
+ url.searchParams.set(
50
+ name,
51
+ Array.isArray(value) ? value.join(",") : String(value),
52
+ )
53
+ }
54
+ }
55
+
56
+ const headers = new Headers(requestInit.headers)
57
+ headers.set("api-key", apiKey)
58
+ headers.set("Accept", "application/json")
59
+ if (requestInit.body !== undefined && !headers.has("Content-Type")) {
60
+ headers.set("Content-Type", "application/json")
61
+ }
62
+
63
+ const response = await fetch(url, { ...requestInit, headers })
64
+ if (response.ok) return response
65
+
66
+ const error = BREVO_ERROR_SCHEMA.safeParse(
67
+ await response.json().catch(() => ({})),
68
+ )
69
+ throw new Error(
70
+ error.success && error.data.message
71
+ ? `Brevo API error (${response.status}): ${error.data.message}`
72
+ : `Brevo API request failed with status ${response.status}.`,
73
+ )
74
+ },
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Parses one successful Brevo JSON response.
80
+ *
81
+ * @param response - Checked provider response.
82
+ * @param schema - Schema describing the provider response.
83
+ */
84
+ export async function parseBrevoResponse<T extends z.ZodType>(
85
+ response: Response,
86
+ schema: T,
87
+ ): Promise<z.output<T>> {
88
+ return schema.parse(await response.json())
89
+ }
@@ -0,0 +1,66 @@
1
+ import { z } from "zod"
2
+ import {
3
+ BREVO_EMAIL_BLOCKED_EVENT_SCHEMA,
4
+ BREVO_EMAIL_CLICKED_EVENT_SCHEMA,
5
+ BREVO_EMAIL_DEFERRED_EVENT_SCHEMA,
6
+ BREVO_EMAIL_DELIVERED_EVENT_SCHEMA,
7
+ BREVO_EMAIL_HARD_BOUNCED_EVENT_SCHEMA,
8
+ BREVO_EMAIL_MARKED_AS_SPAM_EVENT_SCHEMA,
9
+ BREVO_EMAIL_OPENED_EVENT_SCHEMA,
10
+ BREVO_EMAIL_SENT_EVENT_SCHEMA,
11
+ BREVO_EMAIL_SOFT_BOUNCED_EVENT_SCHEMA,
12
+ BREVO_EMAIL_UNSUBSCRIBED_EVENT_SCHEMA,
13
+ BREVO_TRANSACTIONAL_EVENT_SCHEMA,
14
+ } from "./schemas"
15
+
16
+ export * from "./api"
17
+ export * from "./schemas"
18
+
19
+ export const BREVO_TRIGGER_CONFIG_SCHEMA = z.object({})
20
+
21
+ export const brevoTriggerContracts = {
22
+ "brevo.transactional.blocked": {
23
+ configSchema: BREVO_TRIGGER_CONFIG_SCHEMA,
24
+ eventSchema: BREVO_EMAIL_BLOCKED_EVENT_SCHEMA,
25
+ },
26
+ "brevo.transactional.clicked": {
27
+ configSchema: BREVO_TRIGGER_CONFIG_SCHEMA,
28
+ eventSchema: BREVO_EMAIL_CLICKED_EVENT_SCHEMA,
29
+ },
30
+ "brevo.transactional.deferred": {
31
+ configSchema: BREVO_TRIGGER_CONFIG_SCHEMA,
32
+ eventSchema: BREVO_EMAIL_DEFERRED_EVENT_SCHEMA,
33
+ },
34
+ "brevo.transactional.delivered": {
35
+ configSchema: BREVO_TRIGGER_CONFIG_SCHEMA,
36
+ eventSchema: BREVO_EMAIL_DELIVERED_EVENT_SCHEMA,
37
+ },
38
+ "brevo.transactional.event": {
39
+ configSchema: BREVO_TRIGGER_CONFIG_SCHEMA,
40
+ eventSchema: BREVO_TRANSACTIONAL_EVENT_SCHEMA,
41
+ },
42
+ "brevo.transactional.hardBounced": {
43
+ configSchema: BREVO_TRIGGER_CONFIG_SCHEMA,
44
+ eventSchema: BREVO_EMAIL_HARD_BOUNCED_EVENT_SCHEMA,
45
+ },
46
+ "brevo.transactional.markedAsSpam": {
47
+ configSchema: BREVO_TRIGGER_CONFIG_SCHEMA,
48
+ eventSchema: BREVO_EMAIL_MARKED_AS_SPAM_EVENT_SCHEMA,
49
+ },
50
+ "brevo.transactional.opened": {
51
+ configSchema: BREVO_TRIGGER_CONFIG_SCHEMA,
52
+ eventSchema: BREVO_EMAIL_OPENED_EVENT_SCHEMA,
53
+ },
54
+ "brevo.transactional.sent": {
55
+ configSchema: BREVO_TRIGGER_CONFIG_SCHEMA,
56
+ eventSchema: BREVO_EMAIL_SENT_EVENT_SCHEMA,
57
+ },
58
+ "brevo.transactional.softBounced": {
59
+ configSchema: BREVO_TRIGGER_CONFIG_SCHEMA,
60
+ eventSchema: BREVO_EMAIL_SOFT_BOUNCED_EVENT_SCHEMA,
61
+ },
62
+ "brevo.transactional.unsubscribed": {
63
+ configSchema: BREVO_TRIGGER_CONFIG_SCHEMA,
64
+ eventSchema: BREVO_EMAIL_UNSUBSCRIBED_EVENT_SCHEMA,
65
+ },
66
+ } as const
@@ -0,0 +1,449 @@
1
+ import { z } from "zod"
2
+
3
+ export const BREVO_TRANSACTIONAL_EVENT_TYPES = [
4
+ "blocked",
5
+ "click",
6
+ "deferred",
7
+ "delivered",
8
+ "error",
9
+ "hard_bounce",
10
+ "invalid_email",
11
+ "opened",
12
+ "request",
13
+ "soft_bounce",
14
+ "spam",
15
+ "unique_opened",
16
+ "unsubscribed",
17
+ ] as const
18
+
19
+ const BREVO_PROVIDER_EVENT_BASE_SCHEMA = z
20
+ .looseObject({
21
+ /** Custom header supplied with the original email. */
22
+ "X-Mailin-custom": z.string().optional(),
23
+
24
+ /** Provider-local formatted date for the event. */
25
+ date: z.string().optional(),
26
+
27
+ /** Recipient email address. */
28
+ email: z.string().optional(),
29
+
30
+ /** Brevo webhook ID that delivered the event. */
31
+ id: z.number().int().nonnegative(),
32
+
33
+ /** Provider message identity. */
34
+ "message-id": z.string().optional(),
35
+
36
+ /** Email subject. */
37
+ subject: z.string().optional(),
38
+
39
+ /** Tags supplied with the original email. */
40
+ tags: z.string().array().optional(),
41
+
42
+ /** Internal template ID used to send the email. */
43
+ template_id: z.number().int().nonnegative().optional(),
44
+
45
+ /** Unix timestamp in seconds for the underlying email event. */
46
+ ts: z.number().int().nonnegative().optional(),
47
+
48
+ /** Unix timestamp in seconds when Brevo emitted the webhook event. */
49
+ ts_event: z.number().int().nonnegative(),
50
+ })
51
+ .catchall(z.json())
52
+
53
+ const BREVO_PROVIDER_DELIVERY_METADATA_SCHEMA = z.object({
54
+ /** Sending IP used for the message. */
55
+ sending_ip: z.string().optional(),
56
+
57
+ /** Provider timestamp in milliseconds. */
58
+ ts_epoch: z.number().int().nonnegative().optional(),
59
+ })
60
+
61
+ const BREVO_PROVIDER_ENGAGEMENT_METADATA_SCHEMA = z.object({
62
+ /** Brevo contact ID, or zero when the recipient is not a contact. */
63
+ contact_id: z.number().int().nonnegative().optional(),
64
+
65
+ /** Device category reported for the interaction. */
66
+ device_used: z.string().optional(),
67
+
68
+ /** Brevo log preview URL. */
69
+ mirror_link: z.string().optional(),
70
+
71
+ /** Browser or email-client user agent. */
72
+ user_agent: z.string().optional(),
73
+ })
74
+
75
+ const BREVO_PROVIDER_SENT_EVENT_SCHEMA =
76
+ BREVO_PROVIDER_EVENT_BASE_SCHEMA.extend({
77
+ ...BREVO_PROVIDER_DELIVERY_METADATA_SCHEMA.shape,
78
+ ...BREVO_PROVIDER_ENGAGEMENT_METADATA_SCHEMA.pick({
79
+ contact_id: true,
80
+ mirror_link: true,
81
+ }).shape,
82
+ event: z.literal("request"),
83
+ })
84
+
85
+ const BREVO_PROVIDER_DELIVERED_EVENT_SCHEMA =
86
+ BREVO_PROVIDER_EVENT_BASE_SCHEMA.extend({
87
+ ...BREVO_PROVIDER_DELIVERY_METADATA_SCHEMA.shape,
88
+ event: z.literal("delivered"),
89
+ })
90
+
91
+ const BREVO_PROVIDER_HARD_BOUNCED_EVENT_SCHEMA =
92
+ BREVO_PROVIDER_EVENT_BASE_SCHEMA.extend({
93
+ ...BREVO_PROVIDER_DELIVERY_METADATA_SCHEMA.shape,
94
+ event: z.literal("hard_bounce"),
95
+ reason: z.string().optional(),
96
+ })
97
+
98
+ const BREVO_PROVIDER_SOFT_BOUNCED_EVENT_SCHEMA =
99
+ BREVO_PROVIDER_EVENT_BASE_SCHEMA.extend({
100
+ ...BREVO_PROVIDER_DELIVERY_METADATA_SCHEMA.shape,
101
+ event: z.literal("soft_bounce"),
102
+ reason: z.string().optional(),
103
+ })
104
+
105
+ const BREVO_PROVIDER_BLOCKED_EVENT_SCHEMA =
106
+ BREVO_PROVIDER_EVENT_BASE_SCHEMA.extend({
107
+ event: z.literal("blocked"),
108
+ ts_epoch: z.number().int().nonnegative().optional(),
109
+ })
110
+
111
+ const BREVO_PROVIDER_DEFERRED_EVENT_SCHEMA =
112
+ BREVO_PROVIDER_EVENT_BASE_SCHEMA.extend({
113
+ ...BREVO_PROVIDER_DELIVERY_METADATA_SCHEMA.shape,
114
+ event: z.literal("deferred"),
115
+ reason: z.string().optional(),
116
+ })
117
+
118
+ const BREVO_PROVIDER_OPENED_EVENT_SCHEMA =
119
+ BREVO_PROVIDER_EVENT_BASE_SCHEMA.extend({
120
+ ...BREVO_PROVIDER_DELIVERY_METADATA_SCHEMA.shape,
121
+ ...BREVO_PROVIDER_ENGAGEMENT_METADATA_SCHEMA.shape,
122
+ event: z.literal("opened"),
123
+ })
124
+
125
+ const BREVO_PROVIDER_UNIQUE_OPENED_EVENT_SCHEMA =
126
+ BREVO_PROVIDER_EVENT_BASE_SCHEMA.extend({
127
+ ...BREVO_PROVIDER_DELIVERY_METADATA_SCHEMA.shape,
128
+ ...BREVO_PROVIDER_ENGAGEMENT_METADATA_SCHEMA.shape,
129
+ event: z.literal("unique_opened"),
130
+ })
131
+
132
+ const BREVO_PROVIDER_CLICKED_EVENT_SCHEMA =
133
+ BREVO_PROVIDER_EVENT_BASE_SCHEMA.extend({
134
+ ...BREVO_PROVIDER_DELIVERY_METADATA_SCHEMA.shape,
135
+ ...BREVO_PROVIDER_ENGAGEMENT_METADATA_SCHEMA.shape,
136
+ event: z.literal("click"),
137
+ link: z.string().optional(),
138
+ })
139
+
140
+ const BREVO_PROVIDER_SPAM_EVENT_SCHEMA =
141
+ BREVO_PROVIDER_EVENT_BASE_SCHEMA.extend({ event: z.literal("spam") })
142
+
143
+ const BREVO_PROVIDER_UNSUBSCRIBED_EVENT_SCHEMA =
144
+ BREVO_PROVIDER_EVENT_BASE_SCHEMA.extend({
145
+ ...BREVO_PROVIDER_DELIVERY_METADATA_SCHEMA.shape,
146
+ ...BREVO_PROVIDER_ENGAGEMENT_METADATA_SCHEMA.shape,
147
+ event: z.literal("unsubscribed"),
148
+ tag: z.string().optional(),
149
+ })
150
+
151
+ const BREVO_PROVIDER_INVALID_EMAIL_EVENT_SCHEMA =
152
+ BREVO_PROVIDER_EVENT_BASE_SCHEMA.extend({
153
+ event: z.literal("invalid_email"),
154
+ ts_epoch: z.number().int().nonnegative().optional(),
155
+ })
156
+
157
+ const BREVO_PROVIDER_ERROR_EVENT_SCHEMA =
158
+ BREVO_PROVIDER_EVENT_BASE_SCHEMA.extend({
159
+ event: z.literal("error"),
160
+ ts_epoch: z.number().int().nonnegative().optional(),
161
+ })
162
+
163
+ const BREVO_PROVIDER_TRANSACTIONAL_EVENT_SCHEMA = z.discriminatedUnion(
164
+ "event",
165
+ [
166
+ BREVO_PROVIDER_BLOCKED_EVENT_SCHEMA,
167
+ BREVO_PROVIDER_CLICKED_EVENT_SCHEMA,
168
+ BREVO_PROVIDER_DEFERRED_EVENT_SCHEMA,
169
+ BREVO_PROVIDER_DELIVERED_EVENT_SCHEMA,
170
+ BREVO_PROVIDER_ERROR_EVENT_SCHEMA,
171
+ BREVO_PROVIDER_HARD_BOUNCED_EVENT_SCHEMA,
172
+ BREVO_PROVIDER_INVALID_EMAIL_EVENT_SCHEMA,
173
+ BREVO_PROVIDER_OPENED_EVENT_SCHEMA,
174
+ BREVO_PROVIDER_SENT_EVENT_SCHEMA,
175
+ BREVO_PROVIDER_SOFT_BOUNCED_EVENT_SCHEMA,
176
+ BREVO_PROVIDER_SPAM_EVENT_SCHEMA,
177
+ BREVO_PROVIDER_UNIQUE_OPENED_EVENT_SCHEMA,
178
+ BREVO_PROVIDER_UNSUBSCRIBED_EVENT_SCHEMA,
179
+ ],
180
+ )
181
+
182
+ const BREVO_EVENT_BASE_SCHEMA = z.object({
183
+ /** Custom header supplied with the original email. */
184
+ customHeader: z.string().optional(),
185
+
186
+ /** Provider-local formatted date for the event. */
187
+ date: z.string().optional(),
188
+
189
+ /** Recipient email address. */
190
+ email: z.string().optional(),
191
+
192
+ /** Provider message identity. */
193
+ messageId: z.string().optional(),
194
+
195
+ /** Unix timestamp in seconds when Brevo emitted the webhook event. */
196
+ occurredAt: z.number().int().nonnegative(),
197
+
198
+ /** Email subject. */
199
+ subject: z.string().optional(),
200
+
201
+ /** Tags supplied with the original email. */
202
+ tags: z.string().array().optional(),
203
+
204
+ /** Internal template ID used to send the email. */
205
+ templateId: z.number().int().nonnegative().optional(),
206
+
207
+ /** Unix timestamp in seconds for the underlying email event. */
208
+ timestamp: z.number().int().nonnegative().optional(),
209
+
210
+ /** Brevo webhook ID that delivered the event. */
211
+ webhookId: z.number().int().nonnegative(),
212
+ })
213
+
214
+ const BREVO_DELIVERY_METADATA_SCHEMA = z.object({
215
+ /** Sending IP used for the message. */
216
+ sendingIp: z.string().optional(),
217
+
218
+ /** Provider timestamp in milliseconds. */
219
+ timestampMilliseconds: z.number().int().nonnegative().optional(),
220
+ })
221
+
222
+ const BREVO_ENGAGEMENT_METADATA_SCHEMA = z.object({
223
+ /** Brevo contact ID, or zero when the recipient is not a contact. */
224
+ contactId: z.number().int().nonnegative().optional(),
225
+
226
+ /** Device category reported for the interaction. */
227
+ deviceUsed: z.string().optional(),
228
+
229
+ /** Brevo log preview URL. */
230
+ mirrorLink: z.string().optional(),
231
+
232
+ /** Browser or email-client user agent. */
233
+ userAgent: z.string().optional(),
234
+ })
235
+
236
+ export const BREVO_EMAIL_SENT_EVENT_SCHEMA = BREVO_EVENT_BASE_SCHEMA.extend({
237
+ ...BREVO_DELIVERY_METADATA_SCHEMA.shape,
238
+ ...BREVO_ENGAGEMENT_METADATA_SCHEMA.pick({
239
+ contactId: true,
240
+ mirrorLink: true,
241
+ }).shape,
242
+
243
+ /** Original provider webhook payload. */
244
+ event: BREVO_PROVIDER_SENT_EVENT_SCHEMA,
245
+
246
+ /** Brevo transactional event category. */
247
+ type: z.literal("request"),
248
+ })
249
+
250
+ export const BREVO_EMAIL_DELIVERED_EVENT_SCHEMA =
251
+ BREVO_EVENT_BASE_SCHEMA.extend({
252
+ ...BREVO_DELIVERY_METADATA_SCHEMA.shape,
253
+
254
+ /** Original provider webhook payload. */
255
+ event: BREVO_PROVIDER_DELIVERED_EVENT_SCHEMA,
256
+
257
+ /** Brevo transactional event category. */
258
+ type: z.literal("delivered"),
259
+ })
260
+
261
+ export const BREVO_EMAIL_HARD_BOUNCED_EVENT_SCHEMA =
262
+ BREVO_EVENT_BASE_SCHEMA.extend({
263
+ ...BREVO_DELIVERY_METADATA_SCHEMA.shape,
264
+
265
+ /** Original provider webhook payload. */
266
+ event: BREVO_PROVIDER_HARD_BOUNCED_EVENT_SCHEMA,
267
+
268
+ /** Provider explanation for the permanent delivery failure. */
269
+ reason: z.string().optional(),
270
+
271
+ /** Brevo transactional event category. */
272
+ type: z.literal("hard_bounce"),
273
+ })
274
+
275
+ export const BREVO_EMAIL_SOFT_BOUNCED_EVENT_SCHEMA =
276
+ BREVO_EVENT_BASE_SCHEMA.extend({
277
+ ...BREVO_DELIVERY_METADATA_SCHEMA.shape,
278
+
279
+ /** Original provider webhook payload. */
280
+ event: BREVO_PROVIDER_SOFT_BOUNCED_EVENT_SCHEMA,
281
+
282
+ /** Provider explanation for the temporary delivery failure. */
283
+ reason: z.string().optional(),
284
+
285
+ /** Brevo transactional event category. */
286
+ type: z.literal("soft_bounce"),
287
+ })
288
+
289
+ export const BREVO_EMAIL_BLOCKED_EVENT_SCHEMA = BREVO_EVENT_BASE_SCHEMA.extend({
290
+ /** Original provider webhook payload. */
291
+ event: BREVO_PROVIDER_BLOCKED_EVENT_SCHEMA,
292
+
293
+ /** Provider timestamp in milliseconds. */
294
+ timestampMilliseconds: z.number().int().nonnegative().optional(),
295
+
296
+ /** Brevo transactional event category. */
297
+ type: z.literal("blocked"),
298
+ })
299
+
300
+ export const BREVO_EMAIL_DEFERRED_EVENT_SCHEMA = BREVO_EVENT_BASE_SCHEMA.extend(
301
+ {
302
+ ...BREVO_DELIVERY_METADATA_SCHEMA.shape,
303
+
304
+ /** Original provider webhook payload. */
305
+ event: BREVO_PROVIDER_DEFERRED_EVENT_SCHEMA,
306
+
307
+ /** Provider explanation for the delayed delivery attempt. */
308
+ reason: z.string().optional(),
309
+
310
+ /** Brevo transactional event category. */
311
+ type: z.literal("deferred"),
312
+ },
313
+ )
314
+
315
+ const BREVO_EMAIL_OPENED_EVENT_MEMBER_SCHEMA = BREVO_EVENT_BASE_SCHEMA.extend({
316
+ ...BREVO_DELIVERY_METADATA_SCHEMA.shape,
317
+ ...BREVO_ENGAGEMENT_METADATA_SCHEMA.shape,
318
+
319
+ /** Original provider webhook payload. */
320
+ event: BREVO_PROVIDER_OPENED_EVENT_SCHEMA,
321
+
322
+ /** Brevo transactional event category. */
323
+ type: z.literal("opened"),
324
+ })
325
+
326
+ const BREVO_EMAIL_UNIQUE_OPENED_EVENT_MEMBER_SCHEMA =
327
+ BREVO_EVENT_BASE_SCHEMA.extend({
328
+ ...BREVO_DELIVERY_METADATA_SCHEMA.shape,
329
+ ...BREVO_ENGAGEMENT_METADATA_SCHEMA.shape,
330
+
331
+ /** Original provider webhook payload. */
332
+ event: BREVO_PROVIDER_UNIQUE_OPENED_EVENT_SCHEMA,
333
+
334
+ /** Brevo transactional event category. */
335
+ type: z.literal("unique_opened"),
336
+ })
337
+
338
+ export const BREVO_EMAIL_OPENED_EVENT_SCHEMA = z.discriminatedUnion("type", [
339
+ BREVO_EMAIL_OPENED_EVENT_MEMBER_SCHEMA,
340
+ BREVO_EMAIL_UNIQUE_OPENED_EVENT_MEMBER_SCHEMA,
341
+ ])
342
+
343
+ export const BREVO_EMAIL_CLICKED_EVENT_SCHEMA = BREVO_EVENT_BASE_SCHEMA.extend({
344
+ ...BREVO_DELIVERY_METADATA_SCHEMA.shape,
345
+ ...BREVO_ENGAGEMENT_METADATA_SCHEMA.shape,
346
+
347
+ /** Original provider webhook payload. */
348
+ event: BREVO_PROVIDER_CLICKED_EVENT_SCHEMA,
349
+
350
+ /** URL followed by the recipient. */
351
+ link: z.string().optional(),
352
+
353
+ /** Brevo transactional event category. */
354
+ type: z.literal("click"),
355
+ })
356
+
357
+ export const BREVO_EMAIL_MARKED_AS_SPAM_EVENT_SCHEMA =
358
+ BREVO_EVENT_BASE_SCHEMA.extend({
359
+ /** Original provider webhook payload. */
360
+ event: BREVO_PROVIDER_SPAM_EVENT_SCHEMA,
361
+
362
+ /** Brevo transactional event category. */
363
+ type: z.literal("spam"),
364
+ })
365
+
366
+ export const BREVO_EMAIL_UNSUBSCRIBED_EVENT_SCHEMA =
367
+ BREVO_EVENT_BASE_SCHEMA.extend({
368
+ ...BREVO_DELIVERY_METADATA_SCHEMA.shape,
369
+ ...BREVO_ENGAGEMENT_METADATA_SCHEMA.shape,
370
+
371
+ /** Original provider webhook payload. */
372
+ event: BREVO_PROVIDER_UNSUBSCRIBED_EVENT_SCHEMA,
373
+
374
+ /** Provider-native serialized tag value for the unsubscribe. */
375
+ tag: z.string().optional(),
376
+
377
+ /** Brevo transactional event category. */
378
+ type: z.literal("unsubscribed"),
379
+ })
380
+
381
+ const BREVO_INVALID_EMAIL_EVENT_SCHEMA = BREVO_EVENT_BASE_SCHEMA.extend({
382
+ /** Original provider webhook payload. */
383
+ event: BREVO_PROVIDER_INVALID_EMAIL_EVENT_SCHEMA,
384
+
385
+ /** Provider timestamp in milliseconds. */
386
+ timestampMilliseconds: z.number().int().nonnegative().optional(),
387
+
388
+ /** Brevo transactional event category. */
389
+ type: z.literal("invalid_email"),
390
+ })
391
+
392
+ const BREVO_ERROR_EVENT_SCHEMA = BREVO_EVENT_BASE_SCHEMA.extend({
393
+ /** Original provider webhook payload. */
394
+ event: BREVO_PROVIDER_ERROR_EVENT_SCHEMA,
395
+
396
+ /** Provider timestamp in milliseconds. */
397
+ timestampMilliseconds: z.number().int().nonnegative().optional(),
398
+
399
+ /** Brevo transactional event category. */
400
+ type: z.literal("error"),
401
+ })
402
+
403
+ export const BREVO_TRANSACTIONAL_EVENT_SCHEMA = z.discriminatedUnion("type", [
404
+ BREVO_EMAIL_BLOCKED_EVENT_SCHEMA,
405
+ BREVO_EMAIL_CLICKED_EVENT_SCHEMA,
406
+ BREVO_EMAIL_DEFERRED_EVENT_SCHEMA,
407
+ BREVO_EMAIL_DELIVERED_EVENT_SCHEMA,
408
+ BREVO_ERROR_EVENT_SCHEMA,
409
+ BREVO_EMAIL_HARD_BOUNCED_EVENT_SCHEMA,
410
+ BREVO_INVALID_EMAIL_EVENT_SCHEMA,
411
+ BREVO_EMAIL_OPENED_EVENT_MEMBER_SCHEMA,
412
+ BREVO_EMAIL_SENT_EVENT_SCHEMA,
413
+ BREVO_EMAIL_SOFT_BOUNCED_EVENT_SCHEMA,
414
+ BREVO_EMAIL_MARKED_AS_SPAM_EVENT_SCHEMA,
415
+ BREVO_EMAIL_UNIQUE_OPENED_EVENT_MEMBER_SCHEMA,
416
+ BREVO_EMAIL_UNSUBSCRIBED_EVENT_SCHEMA,
417
+ ])
418
+
419
+ /**
420
+ * Normalizes one provider-native Brevo transactional webhook.
421
+ *
422
+ * @param value - Untrusted provider webhook payload.
423
+ */
424
+ export function normalizeBrevoTransactionalEvent(value: unknown) {
425
+ const event = BREVO_PROVIDER_TRANSACTIONAL_EVENT_SCHEMA.parse(value)
426
+ return BREVO_TRANSACTIONAL_EVENT_SCHEMA.parse({
427
+ contactId: event.contact_id,
428
+ customHeader: event["X-Mailin-custom"],
429
+ date: event.date,
430
+ deviceUsed: event.device_used,
431
+ email: event.email,
432
+ event,
433
+ link: event.link,
434
+ messageId: event["message-id"],
435
+ mirrorLink: event.mirror_link,
436
+ occurredAt: event.ts_event,
437
+ reason: event.reason,
438
+ sendingIp: event.sending_ip,
439
+ subject: event.subject,
440
+ tag: event.tag,
441
+ tags: event.tags,
442
+ templateId: event.template_id,
443
+ timestamp: event.ts,
444
+ timestampMilliseconds: event.ts_epoch,
445
+ type: event.event,
446
+ userAgent: event.user_agent,
447
+ webhookId: event.id,
448
+ })
449
+ }