@automate.ax/integration-contracts 0.114.4 → 0.115.2
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.
- package/dist/asana/index.d.ts +156 -8
- package/dist/asana/semantic-events.d.ts +156 -8
- package/dist/asana/semantic-events.js +27 -4
- package/dist/brevo/index.d.ts +1879 -565
- package/dist/brevo/index.js +46 -45
- package/dist/brevo/schemas.d.ts +600 -1029
- package/dist/brevo/schemas.js +227 -291
- package/dist/close/events.d.ts +2 -2
- package/dist/close/schemas.d.ts +2 -2
- package/dist/convex/index.d.ts +4 -4
- package/dist/convex/schemas.d.ts +2 -2
- package/dist/linear/schemas.d.ts +1 -1
- package/dist/notion/schemas.d.ts +4 -4
- package/dist/resend/action-schemas.d.ts +14 -14
- package/dist/whatsapp/index.d.ts +2 -2
- package/dist/whatsapp/schemas.d.ts +5 -5
- package/package.json +2 -2
- package/src/asana/semantic-events.ts +36 -14
- package/src/brevo/index.ts +99 -55
- package/src/brevo/schemas.ts +266 -395
package/src/brevo/schemas.ts
CHANGED
|
@@ -1,449 +1,320 @@
|
|
|
1
1
|
import * as z from "zod"
|
|
2
2
|
|
|
3
|
-
export const
|
|
3
|
+
export const BREVO_EVENT_FAMILIES = [
|
|
4
|
+
"transactionalEmail",
|
|
5
|
+
"transactionalSms",
|
|
6
|
+
"marketingEmail",
|
|
7
|
+
"marketingSms",
|
|
8
|
+
"contact",
|
|
9
|
+
] as const
|
|
10
|
+
|
|
11
|
+
export const BREVO_TRANSACTIONAL_EMAIL_EVENT_TYPES = [
|
|
12
|
+
"sent",
|
|
13
|
+
"delivered",
|
|
14
|
+
"hardBounced",
|
|
15
|
+
"softBounced",
|
|
4
16
|
"blocked",
|
|
5
|
-
"
|
|
17
|
+
"markedAsSpam",
|
|
18
|
+
"invalid",
|
|
6
19
|
"deferred",
|
|
7
|
-
"
|
|
8
|
-
"error",
|
|
9
|
-
"hard_bounce",
|
|
10
|
-
"invalid_email",
|
|
20
|
+
"clicked",
|
|
11
21
|
"opened",
|
|
12
|
-
"
|
|
13
|
-
"soft_bounce",
|
|
14
|
-
"spam",
|
|
15
|
-
"unique_opened",
|
|
22
|
+
"firstOpened",
|
|
16
23
|
"unsubscribed",
|
|
17
24
|
] as const
|
|
18
25
|
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
})
|
|
26
|
+
export const BREVO_TRANSACTIONAL_SMS_EVENT_TYPES = [
|
|
27
|
+
"sent",
|
|
28
|
+
"delivered",
|
|
29
|
+
"hardBounced",
|
|
30
|
+
"softBounced",
|
|
31
|
+
"replied",
|
|
32
|
+
"unsubscribed",
|
|
33
|
+
] as const
|
|
150
34
|
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
35
|
+
export const BREVO_MARKETING_EMAIL_EVENT_TYPES = [
|
|
36
|
+
"delivered",
|
|
37
|
+
"hardBounced",
|
|
38
|
+
"softBounced",
|
|
39
|
+
"markedAsSpam",
|
|
40
|
+
"opened",
|
|
41
|
+
"clicked",
|
|
42
|
+
"unsubscribed",
|
|
43
|
+
] as const
|
|
156
44
|
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
45
|
+
export const BREVO_CONTACT_EVENT_TYPES = [
|
|
46
|
+
"addedToList",
|
|
47
|
+
"updated",
|
|
48
|
+
"deleted",
|
|
49
|
+
] as const
|
|
162
50
|
|
|
163
|
-
const
|
|
164
|
-
"
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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
|
-
)
|
|
51
|
+
export const BREVO_MARKETING_SMS_EVENT_TYPES = [
|
|
52
|
+
"sent",
|
|
53
|
+
"delivered",
|
|
54
|
+
"hardBounced",
|
|
55
|
+
"softBounced",
|
|
56
|
+
"replied",
|
|
57
|
+
"unsubscribed",
|
|
58
|
+
] as const
|
|
181
59
|
|
|
182
|
-
const
|
|
183
|
-
|
|
60
|
+
const BREVO_NORMALIZED_EVENT_FIELDS = {
|
|
61
|
+
bounceType: z.string().optional(),
|
|
62
|
+
campaignId: z.number().int().nonnegative().optional(),
|
|
63
|
+
campaignName: z.string().optional(),
|
|
64
|
+
contactId: z.number().int().nonnegative().optional(),
|
|
65
|
+
content: z.json().optional(),
|
|
66
|
+
creditsUsed: z.number().nonnegative().optional(),
|
|
184
67
|
customHeader: z.string().optional(),
|
|
185
|
-
|
|
186
|
-
/** Provider-local formatted date for the event. */
|
|
187
68
|
date: z.string().optional(),
|
|
188
|
-
|
|
189
|
-
|
|
69
|
+
dateEvent: z.string().optional(),
|
|
70
|
+
dateSent: z.string().optional(),
|
|
71
|
+
description: z.string().optional(),
|
|
72
|
+
deviceUsed: z.string().optional(),
|
|
190
73
|
email: z.string().optional(),
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
74
|
+
errorCode: z.number().int().optional(),
|
|
75
|
+
event: z.record(z.string(), z.json()),
|
|
76
|
+
key: z.string().optional(),
|
|
77
|
+
link: z.string().optional(),
|
|
78
|
+
listIds: z.number().int().nonnegative().array().optional(),
|
|
79
|
+
messageId: z.union([z.string(), z.number().int()]).optional(),
|
|
80
|
+
mirrorLink: z.string().optional(),
|
|
81
|
+
occurredAt: z.number().int().nonnegative().optional(),
|
|
82
|
+
phone: z.string().optional(),
|
|
83
|
+
reason: z.string().optional(),
|
|
84
|
+
reference: z.union([z.string(), z.record(z.string(), z.string())]).optional(),
|
|
85
|
+
remainingCredit: z.number().nonnegative().optional(),
|
|
86
|
+
reply: z.string().optional(),
|
|
87
|
+
segmentIds: z.number().int().nonnegative().array().optional(),
|
|
88
|
+
senderEmail: z.string().optional(),
|
|
89
|
+
sendingIp: z.string().optional(),
|
|
90
|
+
smsCount: z.number().int().nonnegative().optional(),
|
|
91
|
+
status: z.string().optional(),
|
|
199
92
|
subject: z.string().optional(),
|
|
200
|
-
|
|
201
|
-
/** Tags supplied with the original email. */
|
|
93
|
+
tag: z.union([z.string(), z.string().array()]).optional(),
|
|
202
94
|
tags: z.string().array().optional(),
|
|
203
|
-
|
|
204
|
-
/** Internal template ID used to send the email. */
|
|
205
95
|
templateId: z.number().int().nonnegative().optional(),
|
|
206
|
-
|
|
207
|
-
/** Unix timestamp in seconds for the underlying email event. */
|
|
208
96
|
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
97
|
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. */
|
|
98
|
+
timestampSent: z.number().int().nonnegative().optional(),
|
|
233
99
|
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(),
|
|
100
|
+
webhookId: z.number().int().nonnegative(),
|
|
101
|
+
}
|
|
284
102
|
|
|
285
|
-
|
|
286
|
-
|
|
103
|
+
const createBrevoFamilyEventSchema = <
|
|
104
|
+
const TFamily extends (typeof BREVO_EVENT_FAMILIES)[number],
|
|
105
|
+
const TTypes extends readonly [string, ...string[]],
|
|
106
|
+
>(
|
|
107
|
+
family: TFamily,
|
|
108
|
+
types: TTypes,
|
|
109
|
+
) =>
|
|
110
|
+
z.object({
|
|
111
|
+
...BREVO_NORMALIZED_EVENT_FIELDS,
|
|
112
|
+
family: z.literal(family),
|
|
113
|
+
type: z.enum(types),
|
|
287
114
|
})
|
|
288
115
|
|
|
289
|
-
export const
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
/** Brevo transactional event category. */
|
|
311
|
-
type: z.literal("deferred"),
|
|
312
|
-
},
|
|
116
|
+
export const BREVO_TRANSACTIONAL_EMAIL_EVENT_SCHEMA =
|
|
117
|
+
createBrevoFamilyEventSchema(
|
|
118
|
+
"transactionalEmail",
|
|
119
|
+
BREVO_TRANSACTIONAL_EMAIL_EVENT_TYPES,
|
|
120
|
+
)
|
|
121
|
+
export const BREVO_TRANSACTIONAL_SMS_EVENT_SCHEMA =
|
|
122
|
+
createBrevoFamilyEventSchema(
|
|
123
|
+
"transactionalSms",
|
|
124
|
+
BREVO_TRANSACTIONAL_SMS_EVENT_TYPES,
|
|
125
|
+
)
|
|
126
|
+
export const BREVO_MARKETING_EMAIL_EVENT_SCHEMA = createBrevoFamilyEventSchema(
|
|
127
|
+
"marketingEmail",
|
|
128
|
+
BREVO_MARKETING_EMAIL_EVENT_TYPES,
|
|
129
|
+
)
|
|
130
|
+
export const BREVO_MARKETING_SMS_EVENT_SCHEMA = createBrevoFamilyEventSchema(
|
|
131
|
+
"marketingSms",
|
|
132
|
+
BREVO_MARKETING_SMS_EVENT_TYPES,
|
|
133
|
+
)
|
|
134
|
+
export const BREVO_CONTACT_EVENT_SCHEMA = createBrevoFamilyEventSchema(
|
|
135
|
+
"contact",
|
|
136
|
+
BREVO_CONTACT_EVENT_TYPES,
|
|
313
137
|
)
|
|
314
138
|
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
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,
|
|
139
|
+
export const BREVO_EVENT_SCHEMA = z.discriminatedUnion("family", [
|
|
140
|
+
BREVO_TRANSACTIONAL_EMAIL_EVENT_SCHEMA,
|
|
141
|
+
BREVO_TRANSACTIONAL_SMS_EVENT_SCHEMA,
|
|
142
|
+
BREVO_MARKETING_EMAIL_EVENT_SCHEMA,
|
|
143
|
+
BREVO_MARKETING_SMS_EVENT_SCHEMA,
|
|
144
|
+
BREVO_CONTACT_EVENT_SCHEMA,
|
|
341
145
|
])
|
|
342
146
|
|
|
343
|
-
export const
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
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"),
|
|
147
|
+
export const createBrevoSemanticEventSchema = <
|
|
148
|
+
const TFamily extends (typeof BREVO_EVENT_FAMILIES)[number],
|
|
149
|
+
const TType extends string,
|
|
150
|
+
>(
|
|
151
|
+
family: TFamily,
|
|
152
|
+
type: TType,
|
|
153
|
+
) =>
|
|
154
|
+
z.object({
|
|
155
|
+
...BREVO_NORMALIZED_EVENT_FIELDS,
|
|
156
|
+
family: z.literal(family),
|
|
157
|
+
type: z.literal(type),
|
|
364
158
|
})
|
|
365
159
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
160
|
+
const TRANSACTIONAL_EMAIL_PROVIDER_TYPES = {
|
|
161
|
+
blocked: "blocked",
|
|
162
|
+
click: "clicked",
|
|
163
|
+
deferred: "deferred",
|
|
164
|
+
delivered: "delivered",
|
|
165
|
+
hard_bounce: "hardBounced",
|
|
166
|
+
invalid_email: "invalid",
|
|
167
|
+
opened: "opened",
|
|
168
|
+
proxy_open: "opened",
|
|
169
|
+
request: "sent",
|
|
170
|
+
soft_bounce: "softBounced",
|
|
171
|
+
spam: "markedAsSpam",
|
|
172
|
+
unique_opened: "firstOpened",
|
|
173
|
+
unique_proxy_open: "firstOpened",
|
|
174
|
+
unsubscribed: "unsubscribed",
|
|
175
|
+
} as const
|
|
176
|
+
const SMS_PROVIDER_TYPES = {
|
|
177
|
+
delivered: "delivered",
|
|
178
|
+
hard_bounce: "hardBounced",
|
|
179
|
+
replied: "replied",
|
|
180
|
+
sent: "sent",
|
|
181
|
+
soft_bounce: "softBounced",
|
|
182
|
+
unsubscribed: "unsubscribed",
|
|
183
|
+
} as const
|
|
184
|
+
const MARKETING_EMAIL_PROVIDER_TYPES = {
|
|
185
|
+
click: "clicked",
|
|
186
|
+
delivered: "delivered",
|
|
187
|
+
hard_bounce: "hardBounced",
|
|
188
|
+
opened: "opened",
|
|
189
|
+
proxy_open: "opened",
|
|
190
|
+
soft_bounce: "softBounced",
|
|
191
|
+
spam: "markedAsSpam",
|
|
192
|
+
unsubscribe: "unsubscribed",
|
|
193
|
+
unsubscribed: "unsubscribed",
|
|
194
|
+
} as const
|
|
195
|
+
const CONTACT_PROVIDER_TYPES = {
|
|
196
|
+
contact_deleted: "deleted",
|
|
197
|
+
contact_updated: "updated",
|
|
198
|
+
list_addition: "addedToList",
|
|
199
|
+
} as const
|
|
200
|
+
|
|
201
|
+
const BREVO_PROVIDER_EVENT_SCHEMA = z
|
|
202
|
+
.looseObject({
|
|
203
|
+
camp_id: z.number().int().nonnegative().optional(),
|
|
204
|
+
campaign_id: z.number().int().nonnegative().optional(),
|
|
205
|
+
event: z.string().optional(),
|
|
206
|
+
id: z.number().int().nonnegative(),
|
|
207
|
+
msg_status: z.string().optional(),
|
|
208
|
+
type: z.string().optional(),
|
|
379
209
|
})
|
|
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
|
-
])
|
|
210
|
+
.catchall(z.json())
|
|
418
211
|
|
|
419
212
|
/**
|
|
420
|
-
* Normalizes
|
|
213
|
+
* Normalizes every supported Brevo managed-webhook delivery.
|
|
421
214
|
*
|
|
422
|
-
* @param value - Untrusted provider
|
|
215
|
+
* @param value - Untrusted provider callback payload.
|
|
216
|
+
* @param webhookId - Authenticated provider webhook registration identity.
|
|
217
|
+
* @throws When the payload or event discriminator is unsupported.
|
|
423
218
|
*/
|
|
424
|
-
export function
|
|
425
|
-
const event =
|
|
426
|
-
|
|
219
|
+
export function normalizeBrevoEvent(value: unknown, webhookId?: number) {
|
|
220
|
+
const event = BREVO_PROVIDER_EVENT_SCHEMA.parse(value)
|
|
221
|
+
const family = classifyBrevoEventFamily(event)
|
|
222
|
+
const providerType = event.msg_status ?? event.event
|
|
223
|
+
if (!providerType) throw new Error("Brevo webhook event type is missing.")
|
|
224
|
+
// Resolve the semantic discriminator before assembling the normalized payload.
|
|
225
|
+
const type = normalizeBrevoProviderType(family, providerType)
|
|
226
|
+
// Keep provider-to-public field mapping readable and validate it as one unit.
|
|
227
|
+
const payload = {
|
|
228
|
+
bounceType: event.bounce_type,
|
|
229
|
+
campaignId: event.camp_id ?? event.campaign_id,
|
|
230
|
+
campaignName: event["campaign name"] ?? event.campaign_name,
|
|
427
231
|
contactId: event.contact_id,
|
|
232
|
+
content: event.content,
|
|
233
|
+
creditsUsed: event.credits_used,
|
|
428
234
|
customHeader: event["X-Mailin-custom"],
|
|
429
235
|
date: event.date,
|
|
236
|
+
dateEvent: event.date_event,
|
|
237
|
+
dateSent: event.date_sent,
|
|
238
|
+
description: event.description,
|
|
430
239
|
deviceUsed: event.device_used,
|
|
431
240
|
email: event.email,
|
|
241
|
+
errorCode: event.error_code,
|
|
432
242
|
event,
|
|
243
|
+
family,
|
|
244
|
+
key: event.key,
|
|
433
245
|
link: event.link,
|
|
434
|
-
|
|
246
|
+
listIds: event.list_id,
|
|
247
|
+
messageId: event["message-id"] ?? event.messageId,
|
|
435
248
|
mirrorLink: event.mirror_link,
|
|
436
249
|
occurredAt: event.ts_event,
|
|
250
|
+
phone: event.to,
|
|
437
251
|
reason: event.reason,
|
|
252
|
+
reference: event.reference,
|
|
253
|
+
remainingCredit: event.remaining_credit,
|
|
254
|
+
reply: event.reply,
|
|
255
|
+
segmentIds: event.segment_ids,
|
|
256
|
+
senderEmail: event.sender_email,
|
|
438
257
|
sendingIp: event.sending_ip,
|
|
258
|
+
smsCount: event.sms_count,
|
|
259
|
+
status: event.status,
|
|
439
260
|
subject: event.subject,
|
|
440
261
|
tag: event.tag,
|
|
441
262
|
tags: event.tags,
|
|
442
263
|
templateId: event.template_id,
|
|
443
264
|
timestamp: event.ts,
|
|
444
265
|
timestampMilliseconds: event.ts_epoch,
|
|
445
|
-
|
|
266
|
+
timestampSent: event.ts_sent,
|
|
446
267
|
userAgent: event.user_agent,
|
|
447
|
-
|
|
448
|
-
|
|
268
|
+
type,
|
|
269
|
+
webhookId: webhookId ?? event.id,
|
|
270
|
+
}
|
|
271
|
+
return BREVO_EVENT_SCHEMA.parse(payload)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Classifies a provider callback into its managed webhook family.
|
|
276
|
+
*
|
|
277
|
+
* @param event - Parsed provider callback.
|
|
278
|
+
*/
|
|
279
|
+
function classifyBrevoEventFamily(
|
|
280
|
+
event: z.infer<typeof BREVO_PROVIDER_EVENT_SCHEMA>,
|
|
281
|
+
) {
|
|
282
|
+
if (event.msg_status) {
|
|
283
|
+
return event.type?.toLowerCase() === "marketing"
|
|
284
|
+
? ("marketingSms" as const)
|
|
285
|
+
: ("transactionalSms" as const)
|
|
286
|
+
}
|
|
287
|
+
if (event.event && Object.hasOwn(CONTACT_PROVIDER_TYPES, event.event)) {
|
|
288
|
+
return "contact" as const
|
|
289
|
+
}
|
|
290
|
+
return event.camp_id !== undefined
|
|
291
|
+
? ("marketingEmail" as const)
|
|
292
|
+
: ("transactionalEmail" as const)
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Maps a provider event discriminator to the public semantic type.
|
|
297
|
+
*
|
|
298
|
+
* @param family - Managed webhook family.
|
|
299
|
+
* @param providerType - Provider event discriminator.
|
|
300
|
+
* @throws When the provider event is unsupported for the family.
|
|
301
|
+
*/
|
|
302
|
+
function normalizeBrevoProviderType(
|
|
303
|
+
family: (typeof BREVO_EVENT_FAMILIES)[number],
|
|
304
|
+
providerType: string,
|
|
305
|
+
) {
|
|
306
|
+
// Select one family mapping before performing the shared lookup and failure path.
|
|
307
|
+
const mapping =
|
|
308
|
+
family === "transactionalEmail"
|
|
309
|
+
? TRANSACTIONAL_EMAIL_PROVIDER_TYPES
|
|
310
|
+
: family === "marketingEmail"
|
|
311
|
+
? MARKETING_EMAIL_PROVIDER_TYPES
|
|
312
|
+
: family === "contact"
|
|
313
|
+
? CONTACT_PROVIDER_TYPES
|
|
314
|
+
: SMS_PROVIDER_TYPES
|
|
315
|
+
const type = (mapping as Record<string, string>)[providerType]
|
|
316
|
+
if (!type) {
|
|
317
|
+
throw new Error(`Unsupported Brevo ${family} event: ${providerType}`)
|
|
318
|
+
}
|
|
319
|
+
return type
|
|
449
320
|
}
|