@marlinjai/mail-contract 0.12.0 → 0.14.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
@@ -107,10 +107,13 @@ __export(index_exports, {
107
107
  BILLING_NOT_CONFIGURED_REASON: () => BILLING_NOT_CONFIGURED_REASON,
108
108
  BoundedAutomationFilter: () => BoundedAutomationFilter,
109
109
  BoundedSegmentFilter: () => BoundedSegmentFilter,
110
+ COMPANY_GRANT_ROLES: () => COMPANY_GRANT_ROLES,
111
+ COMPANY_ROLE_MAX_AGE_HOURS: () => COMPANY_ROLE_MAX_AGE_HOURS,
110
112
  CONTACT_PROPERTY_TYPES: () => CONTACT_PROPERTY_TYPES,
111
113
  CatalogPlan: () => CatalogPlan,
112
114
  CheckoutRequest: () => CheckoutRequest,
113
115
  CheckoutSession: () => CheckoutSession,
116
+ CompanyGrantRole: () => CompanyGrantRole,
114
117
  CompanyId: () => CompanyId,
115
118
  CompileMessage: () => CompileMessage,
116
119
  CompileRequest: () => CompileRequest,
@@ -249,6 +252,7 @@ __export(index_exports, {
249
252
  MAX_STEPS: () => MAX_STEPS,
250
253
  MAX_STEP_VISITS: () => MAX_STEP_VISITS,
251
254
  MAX_SUBJECT_LENGTH: () => MAX_SUBJECT_LENGTH,
255
+ MAX_SYNC_COMPANIES: () => MAX_SYNC_COMPANIES,
252
256
  MAX_TEMPLATE_INPUTS: () => MAX_TEMPLATE_INPUTS,
253
257
  MAX_TEMPLATE_INPUTS_APP_LENGTH: () => MAX_TEMPLATE_INPUTS_APP_LENGTH,
254
258
  MAX_TEMPLATE_INPUTS_SEEN: () => MAX_TEMPLATE_INPUTS_SEEN,
@@ -259,6 +263,7 @@ __export(index_exports, {
259
263
  MAX_TEMPLATE_TRANSLATIONS: () => MAX_TEMPLATE_TRANSLATIONS,
260
264
  MAX_TRIGGERS: () => MAX_TRIGGERS,
261
265
  MEMBER_ROLES: () => MEMBER_ROLES,
266
+ MEMBER_SOURCES: () => MEMBER_SOURCES,
262
267
  MERGE_FIELD_PATTERN: () => MERGE_FIELD_PATTERN,
263
268
  MESSAGE_OUTCOMES: () => MESSAGE_OUTCOMES,
264
269
  MIN_SPLIT_BRANCHES: () => MIN_SPLIT_BRANCHES,
@@ -294,8 +299,11 @@ __export(index_exports, {
294
299
  MailingTestResult: () => MailingTestResult,
295
300
  MailingUpdate: () => MailingUpdate,
296
301
  Member: () => Member,
302
+ MemberCompanySync: () => MemberCompanySync,
303
+ MemberCompanySyncResult: () => MemberCompanySyncResult,
297
304
  MemberCreate: () => MemberCreate,
298
305
  MemberRole: () => MemberRole,
306
+ MemberSource: () => MemberSource,
299
307
  MemberUpdate: () => MemberUpdate,
300
308
  MergeDefaultValue: () => MergeDefaultValue,
301
309
  MergeDefaults: () => MergeDefaults,
@@ -396,6 +404,11 @@ __export(index_exports, {
396
404
  TEMPLATE_SORTS: () => TEMPLATE_SORTS,
397
405
  TEMPLATE_SORT_DEFAULT_ORDER: () => TEMPLATE_SORT_DEFAULT_ORDER,
398
406
  TERMINAL_MAILING_STATUSES: () => TERMINAL_MAILING_STATUSES,
407
+ TRANSACTIONAL_BLOCKING_REASONS: () => TRANSACTIONAL_BLOCKING_REASONS,
408
+ TRANSACTIONAL_NEEDS_LETTER_REFUSAL: () => TRANSACTIONAL_NEEDS_LETTER_REFUSAL,
409
+ TRANSACTIONAL_NEEDS_TEMPLATE_LOCALE_REFUSAL: () => TRANSACTIONAL_NEEDS_TEMPLATE_LOCALE_REFUSAL,
410
+ TRANSACTIONAL_NOT_ALLOWED_REFUSAL: () => TRANSACTIONAL_NOT_ALLOWED_REFUSAL,
411
+ TRANSACTIONAL_UNSUBSCRIBE_URL_REFUSAL: () => TRANSACTIONAL_UNSUBSCRIBE_URL_REFUSAL,
399
412
  TRUSTED_APPS: () => TRUSTED_APPS,
400
413
  TRUSTED_APP_OPERATIONS: () => TRUSTED_APP_OPERATIONS,
401
414
  Tag: () => Tag,
@@ -1794,6 +1807,17 @@ var Mailing = import_zod8.z.object({
1794
1807
  * the workspace's defaults, below the recipient's own. Empty by default.
1795
1808
  */
1796
1809
  merge: MailingMerge.default({}),
1810
+ /**
1811
+ * Where a reader's reply goes for this mailing, or null for the sending
1812
+ * provider's own reply address (see `MailingContent.reply_to`).
1813
+ */
1814
+ reply_to: Email.nullable().default(null),
1815
+ /**
1816
+ * Whether this is a transactional letter (see `MailingCreate.transactional`
1817
+ * and "Transactional letters" in the contract's documentation). False for
1818
+ * every mailing made without the flag.
1819
+ */
1820
+ transactional: import_zod8.z.boolean().default(false),
1797
1821
  scheduled_at: Timestamp.nullable(),
1798
1822
  /** The A/B test on subject or content (S4), null when there is none. */
1799
1823
  ab_test: AbTestState.nullable(),
@@ -1818,22 +1842,46 @@ var MailingContent = {
1818
1842
  send_locale: LocaleTag.nullable().optional(),
1819
1843
  /**
1820
1844
  * Omitted or null makes the mailing a letter: one recipient at most, sent to
1821
- * anybody not blocked on every topic, with an unsubscribe link that offers
1822
- * every topic and no List-Unsubscribe headers. See "Letters" in the
1845
+ * anybody not blocked on every topic, with an unsubscribe link and
1846
+ * List-Unsubscribe headers that offer every topic. See "Letters" in the
1823
1847
  * contract's documentation.
1824
1848
  */
1825
1849
  topic: Slug.nullable().optional(),
1826
1850
  provider_id: Id,
1827
1851
  metadata: MailingMetadata.optional(),
1828
1852
  /** See `Mailing.merge`. `first_name` and `last_name` only on a letter (no topic). */
1829
- merge: MailingMerge.optional()
1853
+ merge: MailingMerge.optional(),
1854
+ /**
1855
+ * The Reply-To of this mailing's messages: one address, in place of the
1856
+ * sending provider's `reply_to`. Omitted or null keeps the provider's. The
1857
+ * sender (From) is always the provider's, and so is the mailto alternative
1858
+ * of the List-Unsubscribe header, since a request to leave belongs to the
1859
+ * workspace and not to whoever a reply reaches.
1860
+ */
1861
+ reply_to: Email.nullable().optional()
1830
1862
  };
1831
1863
  var MailingCreate = import_zod8.z.object({
1832
1864
  ...MailingContent,
1833
1865
  subject: MailingContent.subject.optional(),
1834
1866
  document: TemplateDocument.optional(),
1835
1867
  template_id: Id.optional(),
1836
- template_locale: LocaleTag.optional()
1868
+ template_locale: LocaleTag.optional(),
1869
+ /**
1870
+ * True makes the mailing a transactional letter: mail that answers
1871
+ * something its one reader did and that they need whatever they chose
1872
+ * about marketing (a booking's confirmation, its reminder, its
1873
+ * cancellation). It needs no `{{unsubscribe_url}}`, carries no
1874
+ * List-Unsubscribe headers and is not stopped by an unsubscribe; a hard
1875
+ * bounce, a spam complaint and a block set by hand still stop it.
1876
+ *
1877
+ * Only a key that a trusted application created may set it (`forbidden`,
1878
+ * `details.reason: "transactional_not_allowed"`), only on a letter
1879
+ * (`validation_failed`, `"transactional_needs_letter"`), from a template
1880
+ * only with `template_locale` (`"transactional_needs_template_locale"`),
1881
+ * and it is fixed when the mailing is created. See "Transactional letters" in the
1882
+ * contract's documentation.
1883
+ */
1884
+ transactional: import_zod8.z.boolean().optional()
1837
1885
  }).refine((v) => v.document === void 0 !== (v.template_id === void 0), {
1838
1886
  message: "exactly one of document or template_id",
1839
1887
  path: ["document"]
@@ -1860,6 +1908,8 @@ var MailingUpdate = import_zod8.z.object({
1860
1908
  * them.
1861
1909
  */
1862
1910
  merge: MailingMerge.optional(),
1911
+ /** See `MailingContent.reply_to`. Null goes back to the provider's reply address. */
1912
+ reply_to: Email.nullable().optional(),
1863
1913
  document: TemplateDocument.optional()
1864
1914
  }).refine((v) => Object.keys(v).length > 0, "at least one field");
1865
1915
  var MailingListQuery = PageQuery.extend({
@@ -1908,6 +1958,11 @@ var Recipient = import_zod8.z.object({
1908
1958
  var MAX_RECIPIENTS_PER_BATCH = 1e3;
1909
1959
  var MAX_LETTER_RECIPIENTS = 1;
1910
1960
  var LETTER_RECIPIENT_REFUSAL = "letter_has_one_recipient";
1961
+ var TRANSACTIONAL_NOT_ALLOWED_REFUSAL = "transactional_not_allowed";
1962
+ var TRANSACTIONAL_NEEDS_LETTER_REFUSAL = "transactional_needs_letter";
1963
+ var TRANSACTIONAL_NEEDS_TEMPLATE_LOCALE_REFUSAL = "transactional_needs_template_locale";
1964
+ var TRANSACTIONAL_UNSUBSCRIBE_URL_REFUSAL = "transactional_has_unsubscribe_url";
1965
+ var TRANSACTIONAL_BLOCKING_REASONS = ["bounced", "complained", "manual"];
1911
1966
  var RecipientInput = import_zod8.z.object({
1912
1967
  contact_id: Id.optional(),
1913
1968
  external_id: import_zod8.z.string().min(1).max(255).optional(),
@@ -3427,12 +3482,13 @@ var WorkspaceCreate = import_zod11.z.object({
3427
3482
  name: import_zod11.z.string().max(200).nullable().optional()
3428
3483
  }),
3429
3484
  /**
3430
- * The signed-in person's active auth-brain company. The dashboard always
3431
- * sends it, which is what links the workspace to auth-brain's company
3432
- * erasure; omitted, the workspace belongs to no company and no company
3433
- * erasure reaches it.
3485
+ * The auth-brain company the workspace is created in. Required since 0.14.0:
3486
+ * every new workspace belongs to a company, which is what links it to that
3487
+ * company's erasure and lets the company's owners and admins in without an
3488
+ * invitation (`members.syncCompanyRole`). A workspace created before, without
3489
+ * one, keeps working and simply has no such inheritance.
3434
3490
  */
3435
- company_id: CompanyId.optional()
3491
+ company_id: CompanyId
3436
3492
  });
3437
3493
  var WorkspaceUpdate = import_zod11.z.object({
3438
3494
  name: import_zod11.z.string().min(1).max(120),
@@ -3442,14 +3498,58 @@ var WorkspaceMove = import_zod11.z.object({ company_id: CompanyId });
3442
3498
  var MEMBER_ROLES = ["owner", "admin", "editor", "viewer"];
3443
3499
  var MemberRole = import_zod11.z.enum(MEMBER_ROLES);
3444
3500
  var WorkspaceMembership = Workspace.extend({ role: MemberRole });
3501
+ var MEMBER_SOURCES = ["invited", "company"];
3502
+ var MemberSource = import_zod11.z.enum(MEMBER_SOURCES);
3503
+ var COMPANY_GRANT_ROLES = ["owner", "admin"];
3504
+ var CompanyGrantRole = import_zod11.z.enum(COMPANY_GRANT_ROLES);
3505
+ var COMPANY_ROLE_MAX_AGE_HOURS = 24;
3445
3506
  var Member = import_zod11.z.object({
3446
3507
  id: Id,
3447
3508
  /** The person's auth-brain subject. */
3448
3509
  subject: import_zod11.z.string().min(1).max(255),
3449
3510
  email: Email,
3450
3511
  name: import_zod11.z.string().max(200).nullable(),
3512
+ /**
3513
+ * The role in force: the higher of the person's own role in the workspace
3514
+ * and the one a confirmed company role carries. For a `company` row that is
3515
+ * no longer confirmed, the role it would carry once confirmed again.
3516
+ */
3451
3517
  role: MemberRole,
3452
- created_at: Timestamp
3518
+ created_at: Timestamp,
3519
+ /*
3520
+ * The five fields below exist since 0.14.0 and are optional only so that a
3521
+ * client built against this contract can still read a service that predates
3522
+ * them: absent means an ordinary, invited member.
3523
+ */
3524
+ source: MemberSource.optional(),
3525
+ /** The company role last reported for the person (`members.syncCompanyRole`), or null. */
3526
+ company_role: CompanyGrantRole.nullable().optional(),
3527
+ /** When the dashboard last confirmed `company_role`; null without one. */
3528
+ company_checked_at: Timestamp.nullable().optional(),
3529
+ /** The person's own role in the workspace; null for a `company` row. */
3530
+ direct_role: MemberRole.nullable().optional(),
3531
+ /**
3532
+ * True while `role` is held only through a confirmed company role. Such a
3533
+ * member cannot be changed or removed here (`inherited_from_company`): the
3534
+ * role is managed in the shared login. A `company` row with `inherited`
3535
+ * false is one whose company role has not been confirmed within
3536
+ * `COMPANY_ROLE_MAX_AGE_HOURS`; it grants nothing and an admin may remove it.
3537
+ */
3538
+ inherited: import_zod11.z.boolean().optional()
3539
+ });
3540
+ var MAX_SYNC_COMPANIES = 100;
3541
+ var MemberCompanySync = import_zod11.z.object({
3542
+ email: Email,
3543
+ name: import_zod11.z.string().max(200).nullable().optional(),
3544
+ companies: import_zod11.z.array(import_zod11.z.object({ company_id: CompanyId, role: CompanyGrantRole })).max(MAX_SYNC_COMPANIES).refine((list2) => new Set(list2.map((c) => c.company_id)).size === list2.length, "each company at most once")
3545
+ });
3546
+ var MemberCompanySyncResult = import_zod11.z.object({
3547
+ /** Workspaces in which the person now holds a company role they did not hold, or held differently, before this call. */
3548
+ granted: import_zod11.z.array(Id),
3549
+ /** Workspaces in which a company role was taken away by this call. */
3550
+ revoked: import_zod11.z.array(Id),
3551
+ /** Workspaces in which the person's company role was already as stated. */
3552
+ unchanged: import_zod11.z.array(Id)
3453
3553
  });
3454
3554
  var MemberCreate = import_zod11.z.object({
3455
3555
  subject: import_zod11.z.string().min(1).max(255),
@@ -5057,6 +5157,24 @@ var foundationRoutes = {
5057
5157
  access: "admin",
5058
5158
  phase: "S0"
5059
5159
  },
5160
+ /**
5161
+ * The dashboard states the signed-in person's owner and admin roles in
5162
+ * companies, and the service brings the member lists in line (see
5163
+ * `MemberCompanySync`). `dashboard` access: the dashboard's service token
5164
+ * and the person's subject, no workspace. An API key is refused, and so is
5165
+ * every trusted application: it is not in any `TRUSTED_APP_OPERATIONS` list
5166
+ * and the handler refuses an application once more by itself.
5167
+ */
5168
+ "members.syncCompanyRole": {
5169
+ method: "POST",
5170
+ path: "/v1/members/company-sync",
5171
+ body: MemberCompanySync,
5172
+ response: MemberCompanySyncResult,
5173
+ status: 200,
5174
+ access: "dashboard",
5175
+ phase: "S0",
5176
+ statement: true
5177
+ },
5060
5178
  "apiKeys.list": {
5061
5179
  method: "GET",
5062
5180
  path: "/v1/api-keys",
@@ -6360,7 +6478,7 @@ function trustedAppMayCall(app, id) {
6360
6478
  return TRUSTED_APP_OPERATIONS[app].includes(id);
6361
6479
  }
6362
6480
  function acceptsIdempotencyKey(route) {
6363
- return route.method !== "GET";
6481
+ return route.method !== "GET" && route.statement !== true;
6364
6482
  }
6365
6483
  function buildPath(path, params = {}) {
6366
6484
  return path.replace(/:([a-z_]+)/g, (_, name) => {
@@ -6481,10 +6599,13 @@ function matchRoute(method, pathname) {
6481
6599
  BILLING_NOT_CONFIGURED_REASON,
6482
6600
  BoundedAutomationFilter,
6483
6601
  BoundedSegmentFilter,
6602
+ COMPANY_GRANT_ROLES,
6603
+ COMPANY_ROLE_MAX_AGE_HOURS,
6484
6604
  CONTACT_PROPERTY_TYPES,
6485
6605
  CatalogPlan,
6486
6606
  CheckoutRequest,
6487
6607
  CheckoutSession,
6608
+ CompanyGrantRole,
6488
6609
  CompanyId,
6489
6610
  CompileMessage,
6490
6611
  CompileRequest,
@@ -6623,6 +6744,7 @@ function matchRoute(method, pathname) {
6623
6744
  MAX_STEPS,
6624
6745
  MAX_STEP_VISITS,
6625
6746
  MAX_SUBJECT_LENGTH,
6747
+ MAX_SYNC_COMPANIES,
6626
6748
  MAX_TEMPLATE_INPUTS,
6627
6749
  MAX_TEMPLATE_INPUTS_APP_LENGTH,
6628
6750
  MAX_TEMPLATE_INPUTS_SEEN,
@@ -6633,6 +6755,7 @@ function matchRoute(method, pathname) {
6633
6755
  MAX_TEMPLATE_TRANSLATIONS,
6634
6756
  MAX_TRIGGERS,
6635
6757
  MEMBER_ROLES,
6758
+ MEMBER_SOURCES,
6636
6759
  MERGE_FIELD_PATTERN,
6637
6760
  MESSAGE_OUTCOMES,
6638
6761
  MIN_SPLIT_BRANCHES,
@@ -6668,8 +6791,11 @@ function matchRoute(method, pathname) {
6668
6791
  MailingTestResult,
6669
6792
  MailingUpdate,
6670
6793
  Member,
6794
+ MemberCompanySync,
6795
+ MemberCompanySyncResult,
6671
6796
  MemberCreate,
6672
6797
  MemberRole,
6798
+ MemberSource,
6673
6799
  MemberUpdate,
6674
6800
  MergeDefaultValue,
6675
6801
  MergeDefaults,
@@ -6770,6 +6896,11 @@ function matchRoute(method, pathname) {
6770
6896
  TEMPLATE_SORTS,
6771
6897
  TEMPLATE_SORT_DEFAULT_ORDER,
6772
6898
  TERMINAL_MAILING_STATUSES,
6899
+ TRANSACTIONAL_BLOCKING_REASONS,
6900
+ TRANSACTIONAL_NEEDS_LETTER_REFUSAL,
6901
+ TRANSACTIONAL_NEEDS_TEMPLATE_LOCALE_REFUSAL,
6902
+ TRANSACTIONAL_NOT_ALLOWED_REFUSAL,
6903
+ TRANSACTIONAL_UNSUBSCRIBE_URL_REFUSAL,
6773
6904
  TRUSTED_APPS,
6774
6905
  TRUSTED_APP_OPERATIONS,
6775
6906
  Tag,
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(),
@@ -2801,12 +2843,13 @@ var WorkspaceCreate = z11.object({
2801
2843
  name: z11.string().max(200).nullable().optional()
2802
2844
  }),
2803
2845
  /**
2804
- * The signed-in person's active auth-brain company. The dashboard always
2805
- * sends it, which is what links the workspace to auth-brain's company
2806
- * erasure; omitted, the workspace belongs to no company and no company
2807
- * erasure reaches it.
2846
+ * The auth-brain company the workspace is created in. Required since 0.14.0:
2847
+ * every new workspace belongs to a company, which is what links it to that
2848
+ * company's erasure and lets the company's owners and admins in without an
2849
+ * invitation (`members.syncCompanyRole`). A workspace created before, without
2850
+ * one, keeps working and simply has no such inheritance.
2808
2851
  */
2809
- company_id: CompanyId.optional()
2852
+ company_id: CompanyId
2810
2853
  });
2811
2854
  var WorkspaceUpdate = z11.object({
2812
2855
  name: z11.string().min(1).max(120),
@@ -2816,14 +2859,58 @@ var WorkspaceMove = z11.object({ company_id: CompanyId });
2816
2859
  var MEMBER_ROLES = ["owner", "admin", "editor", "viewer"];
2817
2860
  var MemberRole = z11.enum(MEMBER_ROLES);
2818
2861
  var WorkspaceMembership = Workspace.extend({ role: MemberRole });
2862
+ var MEMBER_SOURCES = ["invited", "company"];
2863
+ var MemberSource = z11.enum(MEMBER_SOURCES);
2864
+ var COMPANY_GRANT_ROLES = ["owner", "admin"];
2865
+ var CompanyGrantRole = z11.enum(COMPANY_GRANT_ROLES);
2866
+ var COMPANY_ROLE_MAX_AGE_HOURS = 24;
2819
2867
  var Member = z11.object({
2820
2868
  id: Id,
2821
2869
  /** The person's auth-brain subject. */
2822
2870
  subject: z11.string().min(1).max(255),
2823
2871
  email: Email,
2824
2872
  name: z11.string().max(200).nullable(),
2873
+ /**
2874
+ * The role in force: the higher of the person's own role in the workspace
2875
+ * and the one a confirmed company role carries. For a `company` row that is
2876
+ * no longer confirmed, the role it would carry once confirmed again.
2877
+ */
2825
2878
  role: MemberRole,
2826
- created_at: Timestamp
2879
+ created_at: Timestamp,
2880
+ /*
2881
+ * The five fields below exist since 0.14.0 and are optional only so that a
2882
+ * client built against this contract can still read a service that predates
2883
+ * them: absent means an ordinary, invited member.
2884
+ */
2885
+ source: MemberSource.optional(),
2886
+ /** The company role last reported for the person (`members.syncCompanyRole`), or null. */
2887
+ company_role: CompanyGrantRole.nullable().optional(),
2888
+ /** When the dashboard last confirmed `company_role`; null without one. */
2889
+ company_checked_at: Timestamp.nullable().optional(),
2890
+ /** The person's own role in the workspace; null for a `company` row. */
2891
+ direct_role: MemberRole.nullable().optional(),
2892
+ /**
2893
+ * True while `role` is held only through a confirmed company role. Such a
2894
+ * member cannot be changed or removed here (`inherited_from_company`): the
2895
+ * role is managed in the shared login. A `company` row with `inherited`
2896
+ * false is one whose company role has not been confirmed within
2897
+ * `COMPANY_ROLE_MAX_AGE_HOURS`; it grants nothing and an admin may remove it.
2898
+ */
2899
+ inherited: z11.boolean().optional()
2900
+ });
2901
+ var MAX_SYNC_COMPANIES = 100;
2902
+ var MemberCompanySync = z11.object({
2903
+ email: Email,
2904
+ name: z11.string().max(200).nullable().optional(),
2905
+ companies: z11.array(z11.object({ company_id: CompanyId, role: CompanyGrantRole })).max(MAX_SYNC_COMPANIES).refine((list2) => new Set(list2.map((c) => c.company_id)).size === list2.length, "each company at most once")
2906
+ });
2907
+ var MemberCompanySyncResult = z11.object({
2908
+ /** Workspaces in which the person now holds a company role they did not hold, or held differently, before this call. */
2909
+ granted: z11.array(Id),
2910
+ /** Workspaces in which a company role was taken away by this call. */
2911
+ revoked: z11.array(Id),
2912
+ /** Workspaces in which the person's company role was already as stated. */
2913
+ unchanged: z11.array(Id)
2827
2914
  });
2828
2915
  var MemberCreate = z11.object({
2829
2916
  subject: z11.string().min(1).max(255),
@@ -4431,6 +4518,24 @@ var foundationRoutes = {
4431
4518
  access: "admin",
4432
4519
  phase: "S0"
4433
4520
  },
4521
+ /**
4522
+ * The dashboard states the signed-in person's owner and admin roles in
4523
+ * companies, and the service brings the member lists in line (see
4524
+ * `MemberCompanySync`). `dashboard` access: the dashboard's service token
4525
+ * and the person's subject, no workspace. An API key is refused, and so is
4526
+ * every trusted application: it is not in any `TRUSTED_APP_OPERATIONS` list
4527
+ * and the handler refuses an application once more by itself.
4528
+ */
4529
+ "members.syncCompanyRole": {
4530
+ method: "POST",
4531
+ path: "/v1/members/company-sync",
4532
+ body: MemberCompanySync,
4533
+ response: MemberCompanySyncResult,
4534
+ status: 200,
4535
+ access: "dashboard",
4536
+ phase: "S0",
4537
+ statement: true
4538
+ },
4434
4539
  "apiKeys.list": {
4435
4540
  method: "GET",
4436
4541
  path: "/v1/api-keys",
@@ -5734,7 +5839,7 @@ function trustedAppMayCall(app, id) {
5734
5839
  return TRUSTED_APP_OPERATIONS[app].includes(id);
5735
5840
  }
5736
5841
  function acceptsIdempotencyKey(route) {
5737
- return route.method !== "GET";
5842
+ return route.method !== "GET" && route.statement !== true;
5738
5843
  }
5739
5844
  function buildPath(path, params = {}) {
5740
5845
  return path.replace(/:([a-z_]+)/g, (_, name) => {
@@ -5854,10 +5959,13 @@ export {
5854
5959
  BILLING_NOT_CONFIGURED_REASON,
5855
5960
  BoundedAutomationFilter,
5856
5961
  BoundedSegmentFilter,
5962
+ COMPANY_GRANT_ROLES,
5963
+ COMPANY_ROLE_MAX_AGE_HOURS,
5857
5964
  CONTACT_PROPERTY_TYPES,
5858
5965
  CatalogPlan,
5859
5966
  CheckoutRequest,
5860
5967
  CheckoutSession,
5968
+ CompanyGrantRole,
5861
5969
  CompanyId,
5862
5970
  CompileMessage,
5863
5971
  CompileRequest,
@@ -5996,6 +6104,7 @@ export {
5996
6104
  MAX_STEPS,
5997
6105
  MAX_STEP_VISITS,
5998
6106
  MAX_SUBJECT_LENGTH,
6107
+ MAX_SYNC_COMPANIES,
5999
6108
  MAX_TEMPLATE_INPUTS,
6000
6109
  MAX_TEMPLATE_INPUTS_APP_LENGTH,
6001
6110
  MAX_TEMPLATE_INPUTS_SEEN,
@@ -6006,6 +6115,7 @@ export {
6006
6115
  MAX_TEMPLATE_TRANSLATIONS,
6007
6116
  MAX_TRIGGERS,
6008
6117
  MEMBER_ROLES,
6118
+ MEMBER_SOURCES,
6009
6119
  MERGE_FIELD_PATTERN,
6010
6120
  MESSAGE_OUTCOMES,
6011
6121
  MIN_SPLIT_BRANCHES,
@@ -6041,8 +6151,11 @@ export {
6041
6151
  MailingTestResult,
6042
6152
  MailingUpdate,
6043
6153
  Member,
6154
+ MemberCompanySync,
6155
+ MemberCompanySyncResult,
6044
6156
  MemberCreate,
6045
6157
  MemberRole,
6158
+ MemberSource,
6046
6159
  MemberUpdate,
6047
6160
  MergeDefaultValue,
6048
6161
  MergeDefaults,
@@ -6143,6 +6256,11 @@ export {
6143
6256
  TEMPLATE_SORTS,
6144
6257
  TEMPLATE_SORT_DEFAULT_ORDER,
6145
6258
  TERMINAL_MAILING_STATUSES,
6259
+ TRANSACTIONAL_BLOCKING_REASONS,
6260
+ TRANSACTIONAL_NEEDS_LETTER_REFUSAL,
6261
+ TRANSACTIONAL_NEEDS_TEMPLATE_LOCALE_REFUSAL,
6262
+ TRANSACTIONAL_NOT_ALLOWED_REFUSAL,
6263
+ TRANSACTIONAL_UNSUBSCRIBE_URL_REFUSAL,
6146
6264
  TRUSTED_APPS,
6147
6265
  TRUSTED_APP_OPERATIONS,
6148
6266
  Tag,
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.12.0",
6
+ "version": "0.14.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": {