@marlinjai/mail-contract 0.11.0 → 0.13.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.
package/dist/index.js CHANGED
@@ -396,6 +396,13 @@ __export(index_exports, {
396
396
  TEMPLATE_SORTS: () => TEMPLATE_SORTS,
397
397
  TEMPLATE_SORT_DEFAULT_ORDER: () => TEMPLATE_SORT_DEFAULT_ORDER,
398
398
  TERMINAL_MAILING_STATUSES: () => TERMINAL_MAILING_STATUSES,
399
+ TRANSACTIONAL_BLOCKING_REASONS: () => TRANSACTIONAL_BLOCKING_REASONS,
400
+ TRANSACTIONAL_NEEDS_LETTER_REFUSAL: () => TRANSACTIONAL_NEEDS_LETTER_REFUSAL,
401
+ TRANSACTIONAL_NEEDS_TEMPLATE_LOCALE_REFUSAL: () => TRANSACTIONAL_NEEDS_TEMPLATE_LOCALE_REFUSAL,
402
+ TRANSACTIONAL_NOT_ALLOWED_REFUSAL: () => TRANSACTIONAL_NOT_ALLOWED_REFUSAL,
403
+ TRANSACTIONAL_UNSUBSCRIBE_URL_REFUSAL: () => TRANSACTIONAL_UNSUBSCRIBE_URL_REFUSAL,
404
+ TRUSTED_APPS: () => TRUSTED_APPS,
405
+ TRUSTED_APP_OPERATIONS: () => TRUSTED_APP_OPERATIONS,
399
406
  Tag: () => Tag,
400
407
  TagAssignment: () => TagAssignment,
401
408
  TagCreate: () => TagCreate,
@@ -441,6 +448,7 @@ __export(index_exports, {
441
448
  TrackingSettings: () => TrackingSettings,
442
449
  TranslationInsertion: () => TranslationInsertion,
443
450
  TranslationInsertions: () => TranslationInsertions,
451
+ TrustedApp: () => TrustedApp,
444
452
  UNSUBSCRIBE_PATH_PREFIX: () => UNSUBSCRIBE_PATH_PREFIX,
445
453
  UNSUBSCRIBE_SOURCES: () => UNSUBSCRIBE_SOURCES,
446
454
  USAGE_METRICS: () => USAGE_METRICS,
@@ -611,6 +619,7 @@ __export(index_exports, {
611
619
  translationInsertionProblems: () => translationInsertionProblems,
612
620
  translationTextFromInput: () => translationTextFromInput,
613
621
  triggerKey: () => triggerKey,
622
+ trustedAppMayCall: () => trustedAppMayCall,
614
623
  unknownRefusedInsertions: () => unknownRefusedInsertions,
615
624
  unplacedInsertions: () => unplacedInsertions,
616
625
  validateAutomation: () => validateAutomation,
@@ -1790,6 +1799,17 @@ var Mailing = import_zod8.z.object({
1790
1799
  * the workspace's defaults, below the recipient's own. Empty by default.
1791
1800
  */
1792
1801
  merge: MailingMerge.default({}),
1802
+ /**
1803
+ * Where a reader's reply goes for this mailing, or null for the sending
1804
+ * provider's own reply address (see `MailingContent.reply_to`).
1805
+ */
1806
+ reply_to: Email.nullable().default(null),
1807
+ /**
1808
+ * Whether this is a transactional letter (see `MailingCreate.transactional`
1809
+ * and "Transactional letters" in the contract's documentation). False for
1810
+ * every mailing made without the flag.
1811
+ */
1812
+ transactional: import_zod8.z.boolean().default(false),
1793
1813
  scheduled_at: Timestamp.nullable(),
1794
1814
  /** The A/B test on subject or content (S4), null when there is none. */
1795
1815
  ab_test: AbTestState.nullable(),
@@ -1814,22 +1834,46 @@ var MailingContent = {
1814
1834
  send_locale: LocaleTag.nullable().optional(),
1815
1835
  /**
1816
1836
  * Omitted or null makes the mailing a letter: one recipient at most, sent to
1817
- * anybody not blocked on every topic, with an unsubscribe link that offers
1818
- * every topic and no List-Unsubscribe headers. See "Letters" in the
1837
+ * anybody not blocked on every topic, with an unsubscribe link and
1838
+ * List-Unsubscribe headers that offer every topic. See "Letters" in the
1819
1839
  * contract's documentation.
1820
1840
  */
1821
1841
  topic: Slug.nullable().optional(),
1822
1842
  provider_id: Id,
1823
1843
  metadata: MailingMetadata.optional(),
1824
1844
  /** See `Mailing.merge`. `first_name` and `last_name` only on a letter (no topic). */
1825
- merge: MailingMerge.optional()
1845
+ merge: MailingMerge.optional(),
1846
+ /**
1847
+ * The Reply-To of this mailing's messages: one address, in place of the
1848
+ * sending provider's `reply_to`. Omitted or null keeps the provider's. The
1849
+ * sender (From) is always the provider's, and so is the mailto alternative
1850
+ * of the List-Unsubscribe header, since a request to leave belongs to the
1851
+ * workspace and not to whoever a reply reaches.
1852
+ */
1853
+ reply_to: Email.nullable().optional()
1826
1854
  };
1827
1855
  var MailingCreate = import_zod8.z.object({
1828
1856
  ...MailingContent,
1829
1857
  subject: MailingContent.subject.optional(),
1830
1858
  document: TemplateDocument.optional(),
1831
1859
  template_id: Id.optional(),
1832
- template_locale: LocaleTag.optional()
1860
+ template_locale: LocaleTag.optional(),
1861
+ /**
1862
+ * True makes the mailing a transactional letter: mail that answers
1863
+ * something its one reader did and that they need whatever they chose
1864
+ * about marketing (a booking's confirmation, its reminder, its
1865
+ * cancellation). It needs no `{{unsubscribe_url}}`, carries no
1866
+ * List-Unsubscribe headers and is not stopped by an unsubscribe; a hard
1867
+ * bounce, a spam complaint and a block set by hand still stop it.
1868
+ *
1869
+ * Only a key that a trusted application created may set it (`forbidden`,
1870
+ * `details.reason: "transactional_not_allowed"`), only on a letter
1871
+ * (`validation_failed`, `"transactional_needs_letter"`), from a template
1872
+ * only with `template_locale` (`"transactional_needs_template_locale"`),
1873
+ * and it is fixed when the mailing is created. See "Transactional letters" in the
1874
+ * contract's documentation.
1875
+ */
1876
+ transactional: import_zod8.z.boolean().optional()
1833
1877
  }).refine((v) => v.document === void 0 !== (v.template_id === void 0), {
1834
1878
  message: "exactly one of document or template_id",
1835
1879
  path: ["document"]
@@ -1856,6 +1900,8 @@ var MailingUpdate = import_zod8.z.object({
1856
1900
  * them.
1857
1901
  */
1858
1902
  merge: MailingMerge.optional(),
1903
+ /** See `MailingContent.reply_to`. Null goes back to the provider's reply address. */
1904
+ reply_to: Email.nullable().optional(),
1859
1905
  document: TemplateDocument.optional()
1860
1906
  }).refine((v) => Object.keys(v).length > 0, "at least one field");
1861
1907
  var MailingListQuery = PageQuery.extend({
@@ -1904,6 +1950,11 @@ var Recipient = import_zod8.z.object({
1904
1950
  var MAX_RECIPIENTS_PER_BATCH = 1e3;
1905
1951
  var MAX_LETTER_RECIPIENTS = 1;
1906
1952
  var LETTER_RECIPIENT_REFUSAL = "letter_has_one_recipient";
1953
+ var TRANSACTIONAL_NOT_ALLOWED_REFUSAL = "transactional_not_allowed";
1954
+ var TRANSACTIONAL_NEEDS_LETTER_REFUSAL = "transactional_needs_letter";
1955
+ var TRANSACTIONAL_NEEDS_TEMPLATE_LOCALE_REFUSAL = "transactional_needs_template_locale";
1956
+ var TRANSACTIONAL_UNSUBSCRIBE_URL_REFUSAL = "transactional_has_unsubscribe_url";
1957
+ var TRANSACTIONAL_BLOCKING_REASONS = ["bounced", "complained", "manual"];
1907
1958
  var RecipientInput = import_zod8.z.object({
1908
1959
  contact_id: Id.optional(),
1909
1960
  external_id: import_zod8.z.string().min(1).max(255).optional(),
@@ -3562,8 +3613,20 @@ var AUDIT_ACTIONS = [
3562
3613
  "event.recorded"
3563
3614
  ];
3564
3615
  var AuditAction = import_zod11.z.enum(AUDIT_ACTIONS);
3616
+ var TRUSTED_APPS = ["booking"];
3617
+ var TrustedApp = import_zod11.z.enum(TRUSTED_APPS);
3565
3618
  var AuditActor = import_zod11.z.discriminatedUnion("type", [
3566
- import_zod11.z.object({ type: import_zod11.z.literal("member"), member_id: Id, subject: import_zod11.z.string().min(1) }),
3619
+ import_zod11.z.object({
3620
+ type: import_zod11.z.literal("member"),
3621
+ member_id: Id,
3622
+ subject: import_zod11.z.string().min(1),
3623
+ /**
3624
+ * The application the person acted through, when it was not the dashboard:
3625
+ * the service set it from the service token the call carried, so it is
3626
+ * verified, unlike a key's `on_behalf_of`. Absent for the dashboard.
3627
+ */
3628
+ app: TrustedApp.optional()
3629
+ }),
3567
3630
  import_zod11.z.object({
3568
3631
  type: import_zod11.z.literal("api_key"),
3569
3632
  api_key_id: Id,
@@ -6337,6 +6400,12 @@ var routes = {
6337
6400
  ...billingRoutes,
6338
6401
  ...automationRoutes
6339
6402
  };
6403
+ var TRUSTED_APP_OPERATIONS = {
6404
+ booking: ["workspaces.list", "workspace.update", "providers.list", "apiKeys.create", "apiKeys.revoke"]
6405
+ };
6406
+ function trustedAppMayCall(app, id) {
6407
+ return TRUSTED_APP_OPERATIONS[app].includes(id);
6408
+ }
6340
6409
  function acceptsIdempotencyKey(route) {
6341
6410
  return route.method !== "GET";
6342
6411
  }
@@ -6748,6 +6817,13 @@ function matchRoute(method, pathname) {
6748
6817
  TEMPLATE_SORTS,
6749
6818
  TEMPLATE_SORT_DEFAULT_ORDER,
6750
6819
  TERMINAL_MAILING_STATUSES,
6820
+ TRANSACTIONAL_BLOCKING_REASONS,
6821
+ TRANSACTIONAL_NEEDS_LETTER_REFUSAL,
6822
+ TRANSACTIONAL_NEEDS_TEMPLATE_LOCALE_REFUSAL,
6823
+ TRANSACTIONAL_NOT_ALLOWED_REFUSAL,
6824
+ TRANSACTIONAL_UNSUBSCRIBE_URL_REFUSAL,
6825
+ TRUSTED_APPS,
6826
+ TRUSTED_APP_OPERATIONS,
6751
6827
  Tag,
6752
6828
  TagAssignment,
6753
6829
  TagCreate,
@@ -6793,6 +6869,7 @@ function matchRoute(method, pathname) {
6793
6869
  TrackingSettings,
6794
6870
  TranslationInsertion,
6795
6871
  TranslationInsertions,
6872
+ TrustedApp,
6796
6873
  UNSUBSCRIBE_PATH_PREFIX,
6797
6874
  UNSUBSCRIBE_SOURCES,
6798
6875
  USAGE_METRICS,
@@ -6963,6 +7040,7 @@ function matchRoute(method, pathname) {
6963
7040
  translationInsertionProblems,
6964
7041
  translationTextFromInput,
6965
7042
  triggerKey,
7043
+ trustedAppMayCall,
6966
7044
  unknownRefusedInsertions,
6967
7045
  unplacedInsertions,
6968
7046
  validateAutomation,
package/dist/index.mjs CHANGED
@@ -1168,6 +1168,17 @@ var Mailing = z8.object({
1168
1168
  * the workspace's defaults, below the recipient's own. Empty by default.
1169
1169
  */
1170
1170
  merge: MailingMerge.default({}),
1171
+ /**
1172
+ * Where a reader's reply goes for this mailing, or null for the sending
1173
+ * provider's own reply address (see `MailingContent.reply_to`).
1174
+ */
1175
+ reply_to: Email.nullable().default(null),
1176
+ /**
1177
+ * Whether this is a transactional letter (see `MailingCreate.transactional`
1178
+ * and "Transactional letters" in the contract's documentation). False for
1179
+ * every mailing made without the flag.
1180
+ */
1181
+ transactional: z8.boolean().default(false),
1171
1182
  scheduled_at: Timestamp.nullable(),
1172
1183
  /** The A/B test on subject or content (S4), null when there is none. */
1173
1184
  ab_test: AbTestState.nullable(),
@@ -1192,22 +1203,46 @@ var MailingContent = {
1192
1203
  send_locale: LocaleTag.nullable().optional(),
1193
1204
  /**
1194
1205
  * Omitted or null makes the mailing a letter: one recipient at most, sent to
1195
- * anybody not blocked on every topic, with an unsubscribe link that offers
1196
- * every topic and no List-Unsubscribe headers. See "Letters" in the
1206
+ * anybody not blocked on every topic, with an unsubscribe link and
1207
+ * List-Unsubscribe headers that offer every topic. See "Letters" in the
1197
1208
  * contract's documentation.
1198
1209
  */
1199
1210
  topic: Slug.nullable().optional(),
1200
1211
  provider_id: Id,
1201
1212
  metadata: MailingMetadata.optional(),
1202
1213
  /** See `Mailing.merge`. `first_name` and `last_name` only on a letter (no topic). */
1203
- merge: MailingMerge.optional()
1214
+ merge: MailingMerge.optional(),
1215
+ /**
1216
+ * The Reply-To of this mailing's messages: one address, in place of the
1217
+ * sending provider's `reply_to`. Omitted or null keeps the provider's. The
1218
+ * sender (From) is always the provider's, and so is the mailto alternative
1219
+ * of the List-Unsubscribe header, since a request to leave belongs to the
1220
+ * workspace and not to whoever a reply reaches.
1221
+ */
1222
+ reply_to: Email.nullable().optional()
1204
1223
  };
1205
1224
  var MailingCreate = z8.object({
1206
1225
  ...MailingContent,
1207
1226
  subject: MailingContent.subject.optional(),
1208
1227
  document: TemplateDocument.optional(),
1209
1228
  template_id: Id.optional(),
1210
- template_locale: LocaleTag.optional()
1229
+ template_locale: LocaleTag.optional(),
1230
+ /**
1231
+ * True makes the mailing a transactional letter: mail that answers
1232
+ * something its one reader did and that they need whatever they chose
1233
+ * about marketing (a booking's confirmation, its reminder, its
1234
+ * cancellation). It needs no `{{unsubscribe_url}}`, carries no
1235
+ * List-Unsubscribe headers and is not stopped by an unsubscribe; a hard
1236
+ * bounce, a spam complaint and a block set by hand still stop it.
1237
+ *
1238
+ * Only a key that a trusted application created may set it (`forbidden`,
1239
+ * `details.reason: "transactional_not_allowed"`), only on a letter
1240
+ * (`validation_failed`, `"transactional_needs_letter"`), from a template
1241
+ * only with `template_locale` (`"transactional_needs_template_locale"`),
1242
+ * and it is fixed when the mailing is created. See "Transactional letters" in the
1243
+ * contract's documentation.
1244
+ */
1245
+ transactional: z8.boolean().optional()
1211
1246
  }).refine((v) => v.document === void 0 !== (v.template_id === void 0), {
1212
1247
  message: "exactly one of document or template_id",
1213
1248
  path: ["document"]
@@ -1234,6 +1269,8 @@ var MailingUpdate = z8.object({
1234
1269
  * them.
1235
1270
  */
1236
1271
  merge: MailingMerge.optional(),
1272
+ /** See `MailingContent.reply_to`. Null goes back to the provider's reply address. */
1273
+ reply_to: Email.nullable().optional(),
1237
1274
  document: TemplateDocument.optional()
1238
1275
  }).refine((v) => Object.keys(v).length > 0, "at least one field");
1239
1276
  var MailingListQuery = PageQuery.extend({
@@ -1282,6 +1319,11 @@ var Recipient = z8.object({
1282
1319
  var MAX_RECIPIENTS_PER_BATCH = 1e3;
1283
1320
  var MAX_LETTER_RECIPIENTS = 1;
1284
1321
  var LETTER_RECIPIENT_REFUSAL = "letter_has_one_recipient";
1322
+ var TRANSACTIONAL_NOT_ALLOWED_REFUSAL = "transactional_not_allowed";
1323
+ var TRANSACTIONAL_NEEDS_LETTER_REFUSAL = "transactional_needs_letter";
1324
+ var TRANSACTIONAL_NEEDS_TEMPLATE_LOCALE_REFUSAL = "transactional_needs_template_locale";
1325
+ var TRANSACTIONAL_UNSUBSCRIBE_URL_REFUSAL = "transactional_has_unsubscribe_url";
1326
+ var TRANSACTIONAL_BLOCKING_REASONS = ["bounced", "complained", "manual"];
1285
1327
  var RecipientInput = z8.object({
1286
1328
  contact_id: Id.optional(),
1287
1329
  external_id: z8.string().min(1).max(255).optional(),
@@ -2940,8 +2982,20 @@ var AUDIT_ACTIONS = [
2940
2982
  "event.recorded"
2941
2983
  ];
2942
2984
  var AuditAction = z11.enum(AUDIT_ACTIONS);
2985
+ var TRUSTED_APPS = ["booking"];
2986
+ var TrustedApp = z11.enum(TRUSTED_APPS);
2943
2987
  var AuditActor = z11.discriminatedUnion("type", [
2944
- z11.object({ type: z11.literal("member"), member_id: Id, subject: z11.string().min(1) }),
2988
+ z11.object({
2989
+ type: z11.literal("member"),
2990
+ member_id: Id,
2991
+ subject: z11.string().min(1),
2992
+ /**
2993
+ * The application the person acted through, when it was not the dashboard:
2994
+ * the service set it from the service token the call carried, so it is
2995
+ * verified, unlike a key's `on_behalf_of`. Absent for the dashboard.
2996
+ */
2997
+ app: TrustedApp.optional()
2998
+ }),
2945
2999
  z11.object({
2946
3000
  type: z11.literal("api_key"),
2947
3001
  api_key_id: Id,
@@ -5715,6 +5769,12 @@ var routes = {
5715
5769
  ...billingRoutes,
5716
5770
  ...automationRoutes
5717
5771
  };
5772
+ var TRUSTED_APP_OPERATIONS = {
5773
+ booking: ["workspaces.list", "workspace.update", "providers.list", "apiKeys.create", "apiKeys.revoke"]
5774
+ };
5775
+ function trustedAppMayCall(app, id) {
5776
+ return TRUSTED_APP_OPERATIONS[app].includes(id);
5777
+ }
5718
5778
  function acceptsIdempotencyKey(route) {
5719
5779
  return route.method !== "GET";
5720
5780
  }
@@ -6125,6 +6185,13 @@ export {
6125
6185
  TEMPLATE_SORTS,
6126
6186
  TEMPLATE_SORT_DEFAULT_ORDER,
6127
6187
  TERMINAL_MAILING_STATUSES,
6188
+ TRANSACTIONAL_BLOCKING_REASONS,
6189
+ TRANSACTIONAL_NEEDS_LETTER_REFUSAL,
6190
+ TRANSACTIONAL_NEEDS_TEMPLATE_LOCALE_REFUSAL,
6191
+ TRANSACTIONAL_NOT_ALLOWED_REFUSAL,
6192
+ TRANSACTIONAL_UNSUBSCRIBE_URL_REFUSAL,
6193
+ TRUSTED_APPS,
6194
+ TRUSTED_APP_OPERATIONS,
6128
6195
  Tag,
6129
6196
  TagAssignment,
6130
6197
  TagCreate,
@@ -6170,6 +6237,7 @@ export {
6170
6237
  TrackingSettings,
6171
6238
  TranslationInsertion,
6172
6239
  TranslationInsertions,
6240
+ TrustedApp,
6173
6241
  UNSUBSCRIBE_PATH_PREFIX,
6174
6242
  UNSUBSCRIBE_SOURCES,
6175
6243
  USAGE_METRICS,
@@ -6340,6 +6408,7 @@ export {
6340
6408
  translationInsertionProblems,
6341
6409
  translationTextFromInput,
6342
6410
  triggerKey,
6411
+ trustedAppMayCall,
6343
6412
  unknownRefusedInsertions,
6344
6413
  unplacedInsertions,
6345
6414
  validateAutomation,
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.11.0",
6
+ "version": "0.13.0",
7
7
  "description": "The v1 API contract of the mail service: zod schemas and types for every request, response, error and webhook, the route table, and webhook signing helpers",
8
8
  "license": "MIT",
9
9
  "repository": {