@aglyn/shared-util-email 1.0.0-beta.244 → 1.0.0-beta.247

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aglyn/shared-util-email",
3
- "version": "1.0.0-beta.244",
3
+ "version": "1.0.0-beta.247",
4
4
  "license": "Apache-2.0",
5
5
  "homepage": "https://aglyn.com",
6
6
  "repository": {
@@ -25,8 +25,8 @@
25
25
  "./package.json": "./package.json"
26
26
  },
27
27
  "dependencies": {
28
- "@aglyn/shared-data-enums": "1.0.0-beta.244",
29
- "@aglyn/shared-util-http": "1.0.0-beta.244",
28
+ "@aglyn/shared-data-enums": "1.0.0-beta.247",
29
+ "@aglyn/shared-util-http": "1.0.0-beta.247",
30
30
  "@msgpack/msgpack": "^3.1.3",
31
31
  "@swc/helpers": "0.5.23"
32
32
  },
@@ -1,3 +1,4 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
1
2
  /**
2
3
  * @license
3
4
  * Copyright 2026 Aglyn LLC
@@ -72,6 +73,22 @@
72
73
  *=========================================*/ import { ACCOUNT_LURE_PATTERNS, DOCUMENT_SHARE_LURE_PATTERNS, isWorkspaceOwnBrand, linkHostsIn, linkUrlsIn, isBrandsOwnDomain, lookalikeBrandForHost, PHISHING_LURE_PATTERNS, PHISHING_SCREEN_BRANDS, registrableDomain, isAnyOfficialBrandDomain, isCommonLinkDomain, squashScreenText, visibleTextOf } from "./outbound-phishing-screen.js";
73
74
  /** The most a page's text is read to, so a huge page costs a bounded scan. */ const MAX_PAGE_TEXT = 2000000;
74
75
  /** The most nodes the walk visits. */ const MAX_NODES = 20000;
76
+ /**
77
+ * MUI's separate form elements that take an answer (`libs/plugins/mui`
78
+ * form-controls.tsx). Each is a field the author defined, and none carries
79
+ * its own words: those sit BESIDE it, in a Form Label inside the same Form
80
+ * Control or in the Choice Label holding a Checkbox or Radio.
81
+ */ const FIELD_ELEMENT_COMPONENTS = new Set([
82
+ 'muiInput',
83
+ 'muiSelect',
84
+ 'muiCheckbox',
85
+ 'muiRadioGroup',
86
+ 'muiRadio',
87
+ 'muiRating'
88
+ ]);
89
+ const FORM_CONTROL_COMPONENT = 'muiFormControl';
90
+ const FORM_LABEL_COMPONENT = 'muiFormLabel';
91
+ const CHOICE_LABEL_COMPONENT = 'muiFormControlLabel';
75
92
  /** Props that describe a field the author defined, rather than content. */ const FIELD_PROP_KEYS = [
76
93
  'fieldType',
77
94
  'fieldName',
@@ -157,23 +174,38 @@
157
174
  /** Every node in the tree, breadth-first in document order, bounded. */ function walkNodes(root) {
158
175
  const found = [];
159
176
  const seen = new Set();
177
+ // Each queued value carries the walk index of the node it is nested in, so
178
+ // a nested tree knows its parents without stored ids.
160
179
  const stack = [
161
- root
180
+ {
181
+ value: root
182
+ }
162
183
  ];
163
184
  for(let head = 0; head < stack.length && found.length < MAX_NODES; head += 1){
164
- const value = stack[head];
185
+ const { value, parentIndex } = stack[head];
165
186
  if (!value || typeof value !== 'object' || seen.has(value)) continue;
166
187
  seen.add(value);
167
188
  const record = value;
189
+ let enclosing = parentIndex;
168
190
  if (typeof record['componentId'] === 'string') {
169
191
  const props = record['props'];
170
- found.push({
192
+ enclosing = found.length;
193
+ found.push(_extends({
171
194
  componentId: record['componentId'],
172
195
  props: props && typeof props === 'object' ? props : {}
173
- });
196
+ }, typeof record['$id'] === 'string' ? {
197
+ id: record['$id']
198
+ } : {}, typeof record['parentId'] === 'string' ? {
199
+ parentId: record['parentId']
200
+ } : {}, parentIndex !== undefined ? {
201
+ parentIndex
202
+ } : {}));
174
203
  }
175
204
  for (const child of Array.isArray(value) ? value : Object.values(record)){
176
- if (child && typeof child === 'object') stack.push(child);
205
+ if (child && typeof child === 'object') stack.push({
206
+ value: child,
207
+ parentIndex: enclosing
208
+ });
177
209
  }
178
210
  }
179
211
  return found;
@@ -195,17 +227,53 @@
195
227
  return out;
196
228
  }
197
229
  /** Does this node describe a field its author defined? */ function isAuthoredField(node) {
230
+ if (FIELD_ELEMENT_COMPONENTS.has(node.componentId)) return true;
198
231
  if (/field|input/i.test(node.componentId)) return true;
199
232
  return FIELD_PROP_KEYS.some((key)=>node.props[key] !== undefined);
200
233
  }
201
- /** The credential a field asks for, or null. */ function credentialOf(node) {
234
+ /**
235
+ * The words BESIDE one of MUI's separate form elements, which carries none of
236
+ * its own: the Choice Label holding a Checkbox or Radio, and the Form Label in
237
+ * the Form Control around it. Read through stored `parentId`s (a flat node
238
+ * map) or through nesting (a tree), whichever the page has.
239
+ */ function siblingLabelOf(nodes, index) {
240
+ const node = nodes[index];
241
+ if (!node || !FIELD_ELEMENT_COMPONENTS.has(node.componentId)) return '';
242
+ const byId = new Map();
243
+ nodes.forEach((one, at)=>{
244
+ if (one.id && !byId.has(one.id)) byId.set(one.id, at);
245
+ });
246
+ const parentOf = (at)=>{
247
+ const one = nodes[at];
248
+ const stored = (one == null ? void 0 : one.parentId) !== undefined ? byId.get(one.parentId) : undefined;
249
+ return stored !== undefined && stored !== at ? stored : one == null ? void 0 : one.parentIndex;
250
+ };
251
+ const textOf = (one, key)=>typeof (one == null ? void 0 : one.props[key]) === 'string' ? one.props[key].trim() : '';
252
+ const words = [];
253
+ let at = parentOf(index);
254
+ for(let depth = 0; at !== undefined && depth < 8; depth += 1){
255
+ const ancestor = nodes[at];
256
+ if ((ancestor == null ? void 0 : ancestor.componentId) === CHOICE_LABEL_COMPONENT && textOf(ancestor, 'label')) {
257
+ words.push(textOf(ancestor, 'label'));
258
+ }
259
+ if ((ancestor == null ? void 0 : ancestor.componentId) === FORM_CONTROL_COMPONENT) {
260
+ const control = at;
261
+ const formLabel = nodes.find((one, oneAt)=>one.componentId === FORM_LABEL_COMPONENT && parentOf(oneAt) === control);
262
+ if (textOf(formLabel, 'children')) words.unshift(textOf(formLabel, 'children'));
263
+ break;
264
+ }
265
+ at = parentOf(at);
266
+ }
267
+ return words.join(' ');
268
+ }
269
+ /** The credential a field asks for, or null. */ function credentialOf(node, besideLabel = '') {
202
270
  const text = (key)=>typeof node.props[key] === 'string' ? node.props[key] : '';
203
271
  const type = [
204
272
  text('fieldType'),
205
273
  text('type'),
206
274
  text('inputType')
207
275
  ].join(' ').toLowerCase();
208
- const label = text('label') || text('placeholder') || text('fieldName') || text('name');
276
+ const label = text('label') || besideLabel || text('ariaLabel') || text('placeholder') || text('fieldName') || text('name');
209
277
  if (/\bpassword\b/.test(type)) return {
210
278
  field: 'password',
211
279
  label: label || 'password'
@@ -218,6 +286,8 @@
218
286
  };
219
287
  const wording = [
220
288
  text('label'),
289
+ besideLabel,
290
+ text('ariaLabel'),
221
291
  text('placeholder'),
222
292
  text('fieldName'),
223
293
  text('name'),
@@ -311,7 +381,7 @@
311
381
  };
312
382
  nodes.forEach((node, index)=>{
313
383
  if (isAuthoredField(node)) {
314
- const credential = credentialOf(node);
384
+ const credential = credentialOf(node, siblingLabelOf(nodes, index));
315
385
  if (credential) noteCredential(credential.field, credential.label);
316
386
  }
317
387
  for (const html of nodeStrings[index]){
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/shared/util/email/src/lib/hosted-page-screen.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*==========================================\n * THE HOSTED PAGE SCREEN (AGL-3362).\n *\n * The outbound phishing screen, read over a PUBLISHED PAGE instead of an\n * email: the same brand list, the same lookalike logic, the same tiers\n * (`signalsThatHold`). A fraud actor who could not mail a lure could still\n * host one, and the incident's sibling site published a page at the\n * document-share path `/reviewfile`.\n *\n * Five shapes:\n *\n * 1. **A lookalike link or embed** (`lookalike-link`, STRONG) — any `href`,\n * `src`, form action or URL in the page's authored content whose host\n * wears a brand without being the brand's domain.\n * 2. **A credential field** (`credential-field`, STRONG) — a field the\n * merchant AUTHORED that asks for a password, a card number or a\n * one-time code: its type, its autocomplete token, or its label,\n * placeholder or name. The platform's own sign-in, account and checkout\n * elements render their inputs in code, not in stored props, so they are\n * never read as a field here — which is exactly the sanctioned/unsanctioned\n * line: what the platform collects through its own components is\n * Aglyn's to protect, and what a page's author collects in a form of\n * their own is not something a site may ask a stranger for.\n * 3. **A brand's call to action** (`brand-action-page`, SOFT) — one element\n * whose text names a brand that is not the workspace's AND asks the reader\n * to sign in, verify, confirm or open a document, on a page that links\n * somewhere that is neither the workspace's, nor a brand's own, nor a\n * common social or maps link.\n * 4. **A brand's lure, page-wide** (`brand-lure-page`, SOFT, AGL-3447) — the\n * same brand and link anywhere on the page, beside an account or\n * document-share LURE anywhere on it (\"Secure Document Access Portal\"), or\n * a call to action that leaves the site worded as an action (\"Continue to\n * Document\"). The 2026-10-01 phishing page put Proofpoint's name in its\n * body, the lure in its heading and the action on its button: three\n * elements, none of which shape 3 reads on its own. What it does not count page-wide is\n * a bare \"sign in\" that stays on the site — a hotel that says \"find us on\n * Booking.com\" in one place and has a member sign-in in another is two\n * ordinary sentences.\n * 5. **A call to action that leaves, beside a lure** (`offsite-action-page`,\n * SOFT, AGL-3447) — the page's only call to action, or one worded as an\n * account or document action, sends visitors to a host that is not the\n * site's own, a listed brand's or a common link, on a page that carries an\n * account or document-share lure. No brand needed: a \"Secure Document\n * Access Portal\" whose one button leaves for the kit is the shape whatever\n * name it wears.\n *\n * Shapes 3-5 are soft, so they hold only for a workspace in its first\n * fortnight (`signalsThatHold`); an established workspace's page is held by\n * shapes 1 and 2 alone.\n *\n * The same reading applies to a site REDIRECT ({@link screenSiteRedirect}):\n * a rule that sends a path off the site is a page whose one call to action\n * fires on arrival, and its path is the only text it has.\n *\n * Pure: the caller hands over the composed node tree and the workspace's own\n * names and hosts, and decides what a hold does.\n *=========================================*/\n\nimport {\n ACCOUNT_LURE_PATTERNS,\n DOCUMENT_SHARE_LURE_PATTERNS,\n isWorkspaceOwnBrand,\n linkHostsIn,\n linkUrlsIn,\n isBrandsOwnDomain,\n lookalikeBrandForHost,\n PHISHING_LURE_PATTERNS,\n PHISHING_SCREEN_BRANDS,\n type PhishingScreenBrand,\n type PhishingScreenSignal,\n registrableDomain,\n isAnyOfficialBrandDomain,\n isCommonLinkDomain,\n squashScreenText,\n visibleTextOf,\n} from './outbound-phishing-screen'\n\nexport interface HostedPageScreenInput {\n /**\n * The page as it will be served — the composed tree, or any object that\n * holds nodes (`{ componentId, props }`) at any depth. Walked, never\n * trusted to have one shape: a screen, a layout's chrome, a grafted\n * component and a form all arrive together.\n */\n nodes: unknown\n /** The workspace's own names, as `screenOutboundEmail` reads them. */\n ownNames?: readonly (string | null | undefined)[]\n /** The workspace's own hosts — a link to one is never \"elsewhere\". */\n ownDomains?: readonly (string | null | undefined)[]\n}\n\nexport interface HostedPageScreenVerdict {\n signals: PhishingScreenSignal[]\n /**\n * Every host the page (or the redirect) links, embeds or posts to, lowercased — what the\n * link reputation lookup reads (AGL-3451), so the page is walked once.\n */\n linkHosts: string[]\n /**\n * The same links whole, as written (`linkUrlsIn`), for the lookup's\n * `'url'` mode (AGL-3459). Query strings are still on them:\n * `foreignLinksForReputation` drops them before anything is looked up or\n * stored.\n */\n linkUrls: string[]\n}\n\n/** A node as the walk finds it. */\ninterface WalkedNode {\n componentId: string\n props: Record<string, unknown>\n}\n\n/** The most a page's text is read to, so a huge page costs a bounded scan. */\nconst MAX_PAGE_TEXT = 2_000_000\n/** The most nodes the walk visits. */\nconst MAX_NODES = 20_000\n\n/** Props that describe a field the author defined, rather than content. */\nconst FIELD_PROP_KEYS = ['fieldType', 'fieldName', 'inputType', 'autoComplete', 'autocomplete']\n\n/** Autocomplete tokens (WHATWG) that name a credential. */\nconst CREDENTIAL_AUTOCOMPLETE: Record<string, 'password' | 'card' | 'otp'> = {\n 'current-password': 'password',\n 'new-password': 'password',\n 'cc-number': 'card',\n 'cc-csc': 'card',\n 'cc-exp': 'card',\n 'one-time-code': 'otp',\n}\n\n/** Field wording that asks for a credential, by kind. */\nconst CREDENTIAL_LABELS: ReadonlyArray<{ field: 'password' | 'card' | 'otp'; pattern: RegExp }> = [\n { field: 'password', pattern: /\\bpass\\s?(?:word|code|phrase)s?\\b|\\bpasswd\\b|\\bpin\\s*(?:code|number)?\\s*$/i },\n {\n field: 'card',\n pattern:\n /\\b(?:credit|debit)\\s*card\\b|\\bcard\\s*(?:number|no\\.?|#)|\\bcardnumber\\b|\\bcvv2?\\b|\\bcvc\\b|\\bcard\\s*security\\s*code\\b|\\bcard\\s*expir/i,\n },\n {\n field: 'otp',\n pattern:\n /\\bone[-\\s]?time\\s*(?:pass\\s?code|password|code|pin)\\b|\\botp\\b|\\bverification\\s*code\\b|\\b2fa\\b|\\btwo[-\\s]?factor\\b|\\bauthenticat(?:ion|or)\\s*code\\b|\\bsms\\s*code\\b/i,\n },\n]\n\n/** Raw HTML an author placed that holds a credential input. */\nconst HTML_CREDENTIAL_INPUTS: ReadonlyArray<{ field: 'password' | 'card' | 'otp'; pattern: RegExp }> = [\n { field: 'password', pattern: /<input\\b[^>]*\\btype\\s*=\\s*[\"']?password/i },\n { field: 'password', pattern: /<input\\b[^>]*\\b(?:name|autocomplete)\\s*=\\s*[\"']?(?:pass(?:word|wd)?|current-password|new-password)\\b/i },\n { field: 'card', pattern: /<input\\b[^>]*\\b(?:name|autocomplete)\\s*=\\s*[\"']?(?:card_?number|cc-number|cc-csc|cvv|cvc)\\b/i },\n { field: 'otp', pattern: /<input\\b[^>]*\\b(?:name|autocomplete)\\s*=\\s*[\"']?(?:otp|one-time-code|verification_?code)\\b/i },\n]\n\n/**\n * What an element asks its reader to DO with an account or a document —\n * counted beside a brand in the SAME element, or on a call to action that\n * leaves the site. The email lure phrasing counts too.\n */\nconst PAGE_ACTION_PATTERNS: readonly RegExp[] = [\n /\\b(?:sign|log)[\\s-]?in\\b/i,\n /\\bverify\\b/i,\n /\\bconfirm\\s+(?:your|the)\\b/i,\n /\\bunlock\\b/i,\n /\\benter\\s+your\\b/i,\n /\\b(?:view|review|open|download|access)\\s+(?:the\\s+|your\\s+)?(?:shared\\s+|secure\\s+)?(?:file|document|invoice|statement)s?\\b/i,\n /\\b(?:continue|proceed)\\s+to\\s+(?:the\\s+|your\\s+)?(?:document|file|account|sign[\\s-]?in|log[\\s-]?in)s?\\b/i,\n]\n\n/**\n * The lures a page is read for as a WHOLE (AGL-3447): account and\n * document-share wording. Not the marketplace, booking and parcel lures,\n * which a hotel's \"guest reviews\" and a shop's delivery page carry every day\n * and which still count in one element beside a brand.\n */\nconst PAGE_LURE_PATTERNS: readonly RegExp[] = [...ACCOUNT_LURE_PATTERNS, ...DOCUMENT_SHARE_LURE_PATTERNS]\n\n/**\n * A component that renders a call to action — a button or a link, however a\n * plugin names it (`muiButton`, `muiScreenLink`, `muiLinkBox`). It is one\n * when it carries a target: an `href`, or a `screenId` on the site itself.\n */\nconst CALL_TO_ACTION_COMPONENT = /button|fab|cta|link/i\n\n/** The props a call to action's visible words live in; the first found wins. */\nconst CALL_TO_ACTION_LABEL_KEYS = ['children', 'label', 'text', 'title', 'html']\n\n/** URLs in page text, so prose is read for a brand or a lure without them. */\nconst PROSE_URL_PATTERN = /(?:https?:)?\\/\\/[^\\s\"'<>]+|\\bwww\\.[^\\s\"'<>]+/gi\n\n/** One call to action: what it says, and the host it leaves for (null: it stays). */\ninterface CallToAction {\n label: string\n host: string | null\n}\n\n/** Every node in the tree, breadth-first in document order, bounded. */\nfunction walkNodes(root: unknown): WalkedNode[] {\n const found: WalkedNode[] = []\n const seen = new Set<unknown>()\n const stack: unknown[] = [root]\n for (let head = 0; head < stack.length && found.length < MAX_NODES; head += 1) {\n const value = stack[head]\n if (!value || typeof value !== 'object' || seen.has(value)) continue\n seen.add(value)\n const record = value as Record<string, unknown>\n if (typeof record['componentId'] === 'string') {\n const props = record['props']\n found.push({\n componentId: record['componentId'] as string,\n props: props && typeof props === 'object' ? (props as Record<string, unknown>) : {},\n })\n }\n for (const child of Array.isArray(value) ? value : Object.values(record)) {\n if (child && typeof child === 'object') stack.push(child)\n }\n }\n return found\n}\n\n/** Every string a node's props hold, one level of arrays and objects deep. */\nfunction propStrings(props: Record<string, unknown>, skip: ReadonlySet<string> = new Set()): string[] {\n const out: string[] = []\n const visit = (value: unknown, depth: number) => {\n if (typeof value === 'string') out.push(value)\n else if (depth < 3 && value && typeof value === 'object') {\n for (const entry of Array.isArray(value) ? value : Object.values(value as Record<string, unknown>)) {\n visit(entry, depth + 1)\n }\n }\n }\n for (const [key, value] of Object.entries(props)) {\n if (key === 'sx' || skip.has(key)) continue\n visit(value, 0)\n }\n return out\n}\n\n/** Does this node describe a field its author defined? */\nfunction isAuthoredField(node: WalkedNode): boolean {\n if (/field|input/i.test(node.componentId)) return true\n return FIELD_PROP_KEYS.some((key) => node.props[key] !== undefined)\n}\n\n/** The credential a field asks for, or null. */\nfunction credentialOf(node: WalkedNode): { field: 'password' | 'card' | 'otp'; label: string } | null {\n const text = (key: string) => (typeof node.props[key] === 'string' ? (node.props[key] as string) : '')\n const type = [text('fieldType'), text('type'), text('inputType')].join(' ').toLowerCase()\n const label = text('label') || text('placeholder') || text('fieldName') || text('name')\n if (/\\bpassword\\b/.test(type)) return { field: 'password', label: label || 'password' }\n const autocomplete = (text('autoComplete') || text('autocomplete')).toLowerCase().trim()\n const byToken = CREDENTIAL_AUTOCOMPLETE[autocomplete]\n if (byToken) return { field: byToken, label: label || autocomplete }\n const wording = [text('label'), text('placeholder'), text('fieldName'), text('name'), text('helperText')]\n .filter(Boolean)\n .join(' ')\n for (const { field, pattern } of CREDENTIAL_LABELS) {\n if (pattern.test(wording)) return { field, label: (label || wording).slice(0, 80) }\n }\n return null\n}\n\n/**\n * The credential an input asks for, read from the two attributes that decide\n * it in the browser — its `type` and its `autocomplete` token — or null.\n * For code that builds inputs rather than storing them as page props: the\n * marketplace verifier reads a plugin bundle's `{ type: 'password' }` and\n * `setAttribute('autocomplete', 'cc-number')` with this (AGL-3362).\n */\nexport function credentialForInputAttribute(\n attribute: string,\n value: string,\n): 'password' | 'card' | 'otp' | null {\n const name = String(attribute ?? '').toLowerCase()\n const token = String(value ?? '').trim().toLowerCase()\n if ((name === 'type' || name === 'inputtype') && token === 'password') return 'password'\n if (name === 'autocomplete') return CREDENTIAL_AUTOCOMPLETE[token] ?? null\n return null\n}\n\n/** The credential a piece of raw HTML asks for in an input of its own, or null. */\nexport function credentialInHtml(html: string): 'password' | 'card' | 'otp' | null {\n const text = String(html ?? '')\n if (!text.includes('<')) return null\n return HTML_CREDENTIAL_INPUTS.find(({ pattern }) => pattern.test(text))?.field ?? null\n}\n\n/**\n * Does a submitted field's NAME ask for a credential — a password, a card\n * number or security code, a one-time code? The same wording the page\n * screen holds a field for, read over a name as a form posts it\n * (`card_number`, `cardNumber`, `one-time-code`).\n *\n * For the one path that does not pass a publish: a public form endpoint\n * takes whatever field names a request carries, so a page the screen never\n * saw (third-party code, a hand-built request) could still post a password\n * to it. The endpoint drops such a field rather than store it.\n */\nexport function isCredentialFieldName(name: string): boolean {\n const words = String(name ?? '')\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .replace(/[_\\-.[\\]]+/g, ' ')\n .trim()\n if (!words) return false\n if (CREDENTIAL_AUTOCOMPLETE[words.toLowerCase().replace(/\\s+/g, '-')]) return true\n if (/^(?:pass|pwd|ccnum|cc\\s?num|cc\\s?number|cvv2?|cvc|csc)$/i.test(words)) return true\n return CREDENTIAL_LABELS.some(({ pattern }) => pattern.test(words))\n}\n\n/** URLs a page carries in a form Links-by-shape would miss: `//host/…`. */\nfunction protocolRelativeUrls(strings: readonly string[]): string {\n return strings\n .filter((value) => /^\\/\\/[a-z0-9-]+(?:\\.[a-z0-9-]+)+/i.test(value.trim()))\n .map((value) => `https:${value.trim()}`)\n .join('\\n')\n}\n\n/**\n * Screen one page as it will be served. The caller applies the tiers with\n * `signalsThatHold` and decides what a hold does.\n */\nexport function screenHostedPage(input: HostedPageScreenInput): HostedPageScreenVerdict {\n const signals: PhishingScreenSignal[] = []\n const nodes = walkNodes(input.nodes)\n const ownNames = squashScreenText((input.ownNames ?? []).filter(Boolean).join(' '))\n const ownHosts = (input.ownDomains ?? [])\n .map((domain) => String(domain ?? '').trim().toLowerCase().replace(/\\.+$/, ''))\n .filter((domain) => domain.includes('.'))\n const ownRegistrables = new Set(ownHosts.map(registrableDomain))\n const isOwnHost = (host: string) => ownHosts.some((own) => host === own || host.endsWith(`.${own}`))\n\n const nodeStrings = nodes.map((node) => propStrings(node.props))\n let pageText = nodeStrings.map((strings) => strings.join('\\n')).join('\\n')\n pageText = `${pageText}\\n${protocolRelativeUrls(nodeStrings.flat())}`.slice(0, MAX_PAGE_TEXT)\n\n // 1. A lookalike host anywhere the page links, embeds or posts to.\n const linkHosts = linkHostsIn(pageText)\n for (const host of linkHosts) {\n const brand = lookalikeBrandForHost(host)\n if (brand && !isBrandsOwnDomain(brand, ownRegistrables)) {\n signals.push({ code: 'lookalike-link', brand: brand.id, host })\n }\n }\n\n // 2. A credential field the page's author defined.\n const credentialSeen = new Set<string>()\n const noteCredential = (field: 'password' | 'card' | 'otp', label: string) => {\n if (credentialSeen.has(field)) return\n credentialSeen.add(field)\n signals.push({ code: 'credential-field', field, label: label.slice(0, 80) })\n }\n nodes.forEach((node, index) => {\n if (isAuthoredField(node)) {\n const credential = credentialOf(node)\n if (credential) noteCredential(credential.field, credential.label)\n }\n for (const html of nodeStrings[index]) {\n if (!html.includes('<')) continue\n for (const { field, pattern } of HTML_CREDENTIAL_INPUTS) {\n if (pattern.test(html)) noteCredential(field, 'an input in the page’s own HTML')\n }\n }\n })\n\n const isElsewhere = (host: string) => {\n if (isOwnHost(host)) return false\n const registrable = registrableDomain(host)\n return !isAnyOfficialBrandDomain(registrable) && !isCommonLinkDomain(registrable)\n }\n const foreign = linkHosts.find(isElsewhere)\n const actions = [...PAGE_ACTION_PATTERNS, ...PHISHING_LURE_PATTERNS]\n const named = new Set<string>()\n const isOthersBrand = (brand: PhishingScreenBrand) =>\n !named.has(brand.id) && !isWorkspaceOwnBrand(brand, ownNames, ownRegistrables)\n\n // 3. A brand's call to action, in one element, on a page that links away.\n if (foreign) {\n for (const strings of nodeStrings) {\n const text = proseOf(strings.join(' '))\n if (!text) continue\n const action = firstMatch(actions, text)\n if (!action) continue\n for (const brand of PHISHING_SCREEN_BRANDS) {\n if (isOthersBrand(brand) && brand.mention.test(text)) {\n named.add(brand.id)\n signals.push({ code: 'brand-action-page', brand: brand.id, action: action.slice(0, 80) })\n }\n }\n }\n }\n\n const prose = proseOf(pageText)\n const pageLure = firstMatch(PAGE_LURE_PATTERNS, prose)\n const ctas = callsToAction(nodes)\n const leaving = ctas.filter((cta): cta is CallToAction & { host: string } =>\n Boolean(cta.host && isElsewhere(cta.host)),\n )\n const wordedLeaving = leaving.find((cta) => firstMatch(actions, cta.label))\n\n // 4. A brand's lure, read over the page as a whole, on a page that links away.\n const brandLure = pageLure ?? (wordedLeaving ? firstMatch(actions, wordedLeaving.label) : undefined)\n if (foreign && brandLure) {\n const host = (wordedLeaving ?? leaving[0])?.host ?? foreign\n for (const brand of PHISHING_SCREEN_BRANDS) {\n if (isOthersBrand(brand) && brand.mention.test(prose)) {\n named.add(brand.id)\n signals.push({ code: 'brand-lure-page', brand: brand.id, lure: brandLure.slice(0, 120), host })\n }\n }\n }\n\n // 5. The page's call to action leaves the site, beside a lure. Its only\n // one, or the one worded as an account or document action.\n const primary = wordedLeaving ?? (ctas.length === 1 ? leaving[0] : undefined)\n if (primary && pageLure) {\n signals.push({\n code: 'offsite-action-page',\n action: (primary.label || primary.host).slice(0, 80),\n lure: pageLure.slice(0, 120),\n host: primary.host,\n })\n }\n\n return { signals, linkHosts, linkUrls: linkUrlsIn(pageText) }\n}\n\n/**\n * What a visitor reads in page text: its markup taken out the way the email\n * screen takes it out ({@link visibleTextOf}), so a custom HTML block or a\n * rich-text prop cannot split a brand or a lure letter by letter with tags\n * (AGL-3453), and its bare URLs taken out too. Links are still read from\n * the raw text, where an href lives.\n */\nfunction proseOf(text: string): string {\n return visibleTextOf(text).replace(PROSE_URL_PATTERN, ' ').replace(/\\s+/g, ' ').trim()\n}\n\n/** The first pattern's match in the text, or undefined. */\nfunction firstMatch(patterns: readonly RegExp[], text: string): string | undefined {\n if (!text) return undefined\n for (const pattern of patterns) {\n const found = text.match(pattern)?.[0]\n if (found) return found\n }\n return undefined\n}\n\n/** The words a call to action shows, from its label prop, markup taken out. */\nfunction callToActionLabel(props: Record<string, unknown>): string {\n for (const key of CALL_TO_ACTION_LABEL_KEYS) {\n const value = props[key]\n if (typeof value === 'string' && value.trim()) {\n return visibleTextOf(value).split('\\n')[0]\n }\n }\n return ''\n}\n\n/**\n * Every call to action on the page: each button or link that carries a\n * target. A `screenId` is a page of the site and takes precedence over an\n * `href`, as the button renders it; a relative, `mailto:` or `tel:` href has\n * no web host and stays too.\n */\nfunction callsToAction(nodes: readonly WalkedNode[]): CallToAction[] {\n const found: CallToAction[] = []\n for (const node of nodes) {\n if (!CALL_TO_ACTION_COMPONENT.test(node.componentId)) continue\n const screenId = typeof node.props['screenId'] === 'string' ? node.props['screenId'].trim() : ''\n const href = typeof node.props['href'] === 'string' ? node.props['href'].trim() : ''\n if (!screenId && !href) continue\n const absolute = href.startsWith('//') ? `https:${href}` : href\n found.push({\n label: callToActionLabel(node.props),\n host: screenId ? null : (linkHostsIn(absolute)[0] ?? null),\n })\n }\n return found\n}\n\nexport interface SiteRedirectScreenInput {\n /** The path the rule answers — `/secure-document` — or its pattern. */\n source: string\n /** Where it sends the visitor, after any `$n` substitution. */\n destination: string\n /** The workspace's own names, as `screenHostedPage` reads them. */\n ownNames?: readonly (string | null | undefined)[]\n /** The workspace's own hosts — a redirect to one never leaves. */\n ownDomains?: readonly (string | null | undefined)[]\n}\n\n/**\n * Screen one site redirect (AGL-3447): a rule that sends a path off the site\n * is a call to action that fires on arrival, so it is read with the page's\n * rules over the one text it has, its path.\n *\n * - `lookalike-link` (STRONG) — the destination wears a brand it is not.\n * - `offsite-redirect` (SOFT) — the destination is not the site's own, a\n * listed brand's or a common link, and the path reads as an account or\n * document-share lure (`/secure-document-access`).\n * - `brand-lure-link` (SOFT) — the same, and the path names a brand that is\n * not the workspace's (`/paypal-verify-your-account`), the email screen's\n * three-part lure.\n *\n * The caller applies the tiers with `signalsThatHold`, with the workspace's\n * age, and decides what a hold does.\n */\nexport function screenSiteRedirect(input: SiteRedirectScreenInput): HostedPageScreenVerdict {\n const signals: PhishingScreenSignal[] = []\n const destination = String(input.destination ?? '').trim()\n const absolute = destination.startsWith('//') ? `https:${destination}` : destination\n const hosts = linkHostsIn(absolute)\n const linkUrls = linkUrlsIn(absolute)\n if (!hosts.length) return { signals, linkHosts: hosts, linkUrls }\n const ownNames = squashScreenText((input.ownNames ?? []).filter(Boolean).join(' '))\n const ownHosts = (input.ownDomains ?? [])\n .map((domain) => String(domain ?? '').trim().toLowerCase().replace(/\\.+$/, ''))\n .filter((domain) => domain.includes('.'))\n const ownRegistrables = new Set(ownHosts.map(registrableDomain))\n\n for (const host of hosts) {\n const brand = lookalikeBrandForHost(host)\n if (brand && !isBrandsOwnDomain(brand, ownRegistrables)) {\n signals.push({ code: 'lookalike-link', brand: brand.id, host })\n }\n }\n\n // The first host is where the browser goes; the rest is a userinfo disguise.\n const host = hosts[0]\n const registrable = registrableDomain(host)\n const leaves =\n !ownHosts.some((own) => host === own || host.endsWith(`.${own}`)) &&\n !isAnyOfficialBrandDomain(registrable) &&\n !isCommonLinkDomain(registrable)\n if (!leaves) return { signals, linkHosts: hosts, linkUrls }\n\n const source = String(input.source ?? '').trim()\n const words = pathWords(source)\n const lure = firstMatch(PAGE_LURE_PATTERNS, words)\n if (lure) {\n signals.push({ code: 'offsite-redirect', source: source.slice(0, 120), lure: lure.slice(0, 120), host })\n }\n const anyLure = lure ?? firstMatch(PHISHING_LURE_PATTERNS, words)\n if (anyLure) {\n const brand = PHISHING_SCREEN_BRANDS.find(\n (candidate) =>\n candidate.mention.test(words) && !isWorkspaceOwnBrand(candidate, ownNames, ownRegistrables),\n )\n if (brand) {\n signals.push({ code: 'brand-lure-link', brand: brand.id, lure: anyLure.slice(0, 120), host })\n }\n }\n return { signals, linkHosts: hosts, linkUrls }\n}\n\n/** A path read as words: `/secure-document_access` → `secure document access`. */\nfunction pathWords(path: string): string {\n let decoded = path\n try {\n decoded = decodeURIComponent(path)\n } catch {\n // A malformed escape is read as typed.\n }\n return decoded.replace(/[^a-z0-9]+/gi, ' ').trim()\n}\n"],"names":["ACCOUNT_LURE_PATTERNS","DOCUMENT_SHARE_LURE_PATTERNS","isWorkspaceOwnBrand","linkHostsIn","linkUrlsIn","isBrandsOwnDomain","lookalikeBrandForHost","PHISHING_LURE_PATTERNS","PHISHING_SCREEN_BRANDS","registrableDomain","isAnyOfficialBrandDomain","isCommonLinkDomain","squashScreenText","visibleTextOf","MAX_PAGE_TEXT","MAX_NODES","FIELD_PROP_KEYS","CREDENTIAL_AUTOCOMPLETE","CREDENTIAL_LABELS","field","pattern","HTML_CREDENTIAL_INPUTS","PAGE_ACTION_PATTERNS","PAGE_LURE_PATTERNS","CALL_TO_ACTION_COMPONENT","CALL_TO_ACTION_LABEL_KEYS","PROSE_URL_PATTERN","walkNodes","root","found","seen","Set","stack","head","length","value","has","add","record","props","push","componentId","child","Array","isArray","Object","values","propStrings","skip","out","visit","depth","entry","key","entries","isAuthoredField","node","test","some","undefined","credentialOf","text","type","join","toLowerCase","label","autocomplete","trim","byToken","wording","filter","Boolean","slice","credentialForInputAttribute","attribute","name","String","token","credentialInHtml","html","includes","find","isCredentialFieldName","words","replace","protocolRelativeUrls","strings","map","screenHostedPage","input","signals","nodes","ownNames","ownHosts","ownDomains","domain","ownRegistrables","isOwnHost","host","own","endsWith","nodeStrings","pageText","flat","linkHosts","brand","code","id","credentialSeen","noteCredential","forEach","index","credential","isElsewhere","registrable","foreign","actions","named","isOthersBrand","proseOf","action","firstMatch","mention","prose","pageLure","ctas","callsToAction","leaving","cta","wordedLeaving","brandLure","lure","primary","linkUrls","patterns","match","callToActionLabel","split","screenId","href","absolute","startsWith","screenSiteRedirect","destination","hosts","leaves","source","pathWords","anyLure","candidate","path","decoded","decodeURIComponent"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/shared/util/email/src/lib/hosted-page-screen.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*==========================================\n * THE HOSTED PAGE SCREEN (AGL-3362).\n *\n * The outbound phishing screen, read over a PUBLISHED PAGE instead of an\n * email: the same brand list, the same lookalike logic, the same tiers\n * (`signalsThatHold`). A fraud actor who could not mail a lure could still\n * host one, and the incident's sibling site published a page at the\n * document-share path `/reviewfile`.\n *\n * Five shapes:\n *\n * 1. **A lookalike link or embed** (`lookalike-link`, STRONG) — any `href`,\n * `src`, form action or URL in the page's authored content whose host\n * wears a brand without being the brand's domain.\n * 2. **A credential field** (`credential-field`, STRONG) — a field the\n * merchant AUTHORED that asks for a password, a card number or a\n * one-time code: its type, its autocomplete token, or its label,\n * placeholder or name. The platform's own sign-in, account and checkout\n * elements render their inputs in code, not in stored props, so they are\n * never read as a field here — which is exactly the sanctioned/unsanctioned\n * line: what the platform collects through its own components is\n * Aglyn's to protect, and what a page's author collects in a form of\n * their own is not something a site may ask a stranger for.\n * 3. **A brand's call to action** (`brand-action-page`, SOFT) — one element\n * whose text names a brand that is not the workspace's AND asks the reader\n * to sign in, verify, confirm or open a document, on a page that links\n * somewhere that is neither the workspace's, nor a brand's own, nor a\n * common social or maps link.\n * 4. **A brand's lure, page-wide** (`brand-lure-page`, SOFT, AGL-3447) — the\n * same brand and link anywhere on the page, beside an account or\n * document-share LURE anywhere on it (\"Secure Document Access Portal\"), or\n * a call to action that leaves the site worded as an action (\"Continue to\n * Document\"). The 2026-10-01 phishing page put Proofpoint's name in its\n * body, the lure in its heading and the action on its button: three\n * elements, none of which shape 3 reads on its own. What it does not count page-wide is\n * a bare \"sign in\" that stays on the site — a hotel that says \"find us on\n * Booking.com\" in one place and has a member sign-in in another is two\n * ordinary sentences.\n * 5. **A call to action that leaves, beside a lure** (`offsite-action-page`,\n * SOFT, AGL-3447) — the page's only call to action, or one worded as an\n * account or document action, sends visitors to a host that is not the\n * site's own, a listed brand's or a common link, on a page that carries an\n * account or document-share lure. No brand needed: a \"Secure Document\n * Access Portal\" whose one button leaves for the kit is the shape whatever\n * name it wears.\n *\n * Shapes 3-5 are soft, so they hold only for a workspace in its first\n * fortnight (`signalsThatHold`); an established workspace's page is held by\n * shapes 1 and 2 alone.\n *\n * The same reading applies to a site REDIRECT ({@link screenSiteRedirect}):\n * a rule that sends a path off the site is a page whose one call to action\n * fires on arrival, and its path is the only text it has.\n *\n * Pure: the caller hands over the composed node tree and the workspace's own\n * names and hosts, and decides what a hold does.\n *=========================================*/\n\nimport {\n ACCOUNT_LURE_PATTERNS,\n DOCUMENT_SHARE_LURE_PATTERNS,\n isWorkspaceOwnBrand,\n linkHostsIn,\n linkUrlsIn,\n isBrandsOwnDomain,\n lookalikeBrandForHost,\n PHISHING_LURE_PATTERNS,\n PHISHING_SCREEN_BRANDS,\n type PhishingScreenBrand,\n type PhishingScreenSignal,\n registrableDomain,\n isAnyOfficialBrandDomain,\n isCommonLinkDomain,\n squashScreenText,\n visibleTextOf,\n} from './outbound-phishing-screen'\n\nexport interface HostedPageScreenInput {\n /**\n * The page as it will be served — the composed tree, or any object that\n * holds nodes (`{ componentId, props }`) at any depth. Walked, never\n * trusted to have one shape: a screen, a layout's chrome, a grafted\n * component and a form all arrive together.\n */\n nodes: unknown\n /** The workspace's own names, as `screenOutboundEmail` reads them. */\n ownNames?: readonly (string | null | undefined)[]\n /** The workspace's own hosts — a link to one is never \"elsewhere\". */\n ownDomains?: readonly (string | null | undefined)[]\n}\n\nexport interface HostedPageScreenVerdict {\n signals: PhishingScreenSignal[]\n /**\n * Every host the page (or the redirect) links, embeds or posts to, lowercased — what the\n * link reputation lookup reads (AGL-3451), so the page is walked once.\n */\n linkHosts: string[]\n /**\n * The same links whole, as written (`linkUrlsIn`), for the lookup's\n * `'url'` mode (AGL-3459). Query strings are still on them:\n * `foreignLinksForReputation` drops them before anything is looked up or\n * stored.\n */\n linkUrls: string[]\n}\n\n/** A node as the walk finds it. */\ninterface WalkedNode {\n componentId: string\n props: Record<string, unknown>\n /** Its stored `$id`, when it has one. */\n id?: string\n /** Its stored `parentId`, when it has one (a flat node map). */\n parentId?: string\n /** The nearest node it is NESTED in, by walk index (a nested tree). */\n parentIndex?: number\n}\n\n/** The most a page's text is read to, so a huge page costs a bounded scan. */\nconst MAX_PAGE_TEXT = 2_000_000\n/** The most nodes the walk visits. */\nconst MAX_NODES = 20_000\n\n/**\n * MUI's separate form elements that take an answer (`libs/plugins/mui`\n * form-controls.tsx). Each is a field the author defined, and none carries\n * its own words: those sit BESIDE it, in a Form Label inside the same Form\n * Control or in the Choice Label holding a Checkbox or Radio.\n */\nconst FIELD_ELEMENT_COMPONENTS = new Set([\n 'muiInput',\n 'muiSelect',\n 'muiCheckbox',\n 'muiRadioGroup',\n 'muiRadio',\n 'muiRating',\n])\nconst FORM_CONTROL_COMPONENT = 'muiFormControl'\nconst FORM_LABEL_COMPONENT = 'muiFormLabel'\nconst CHOICE_LABEL_COMPONENT = 'muiFormControlLabel'\n\n/** Props that describe a field the author defined, rather than content. */\nconst FIELD_PROP_KEYS = ['fieldType', 'fieldName', 'inputType', 'autoComplete', 'autocomplete']\n\n/** Autocomplete tokens (WHATWG) that name a credential. */\nconst CREDENTIAL_AUTOCOMPLETE: Record<string, 'password' | 'card' | 'otp'> = {\n 'current-password': 'password',\n 'new-password': 'password',\n 'cc-number': 'card',\n 'cc-csc': 'card',\n 'cc-exp': 'card',\n 'one-time-code': 'otp',\n}\n\n/** Field wording that asks for a credential, by kind. */\nconst CREDENTIAL_LABELS: ReadonlyArray<{ field: 'password' | 'card' | 'otp'; pattern: RegExp }> = [\n { field: 'password', pattern: /\\bpass\\s?(?:word|code|phrase)s?\\b|\\bpasswd\\b|\\bpin\\s*(?:code|number)?\\s*$/i },\n {\n field: 'card',\n pattern:\n /\\b(?:credit|debit)\\s*card\\b|\\bcard\\s*(?:number|no\\.?|#)|\\bcardnumber\\b|\\bcvv2?\\b|\\bcvc\\b|\\bcard\\s*security\\s*code\\b|\\bcard\\s*expir/i,\n },\n {\n field: 'otp',\n pattern:\n /\\bone[-\\s]?time\\s*(?:pass\\s?code|password|code|pin)\\b|\\botp\\b|\\bverification\\s*code\\b|\\b2fa\\b|\\btwo[-\\s]?factor\\b|\\bauthenticat(?:ion|or)\\s*code\\b|\\bsms\\s*code\\b/i,\n },\n]\n\n/** Raw HTML an author placed that holds a credential input. */\nconst HTML_CREDENTIAL_INPUTS: ReadonlyArray<{ field: 'password' | 'card' | 'otp'; pattern: RegExp }> = [\n { field: 'password', pattern: /<input\\b[^>]*\\btype\\s*=\\s*[\"']?password/i },\n { field: 'password', pattern: /<input\\b[^>]*\\b(?:name|autocomplete)\\s*=\\s*[\"']?(?:pass(?:word|wd)?|current-password|new-password)\\b/i },\n { field: 'card', pattern: /<input\\b[^>]*\\b(?:name|autocomplete)\\s*=\\s*[\"']?(?:card_?number|cc-number|cc-csc|cvv|cvc)\\b/i },\n { field: 'otp', pattern: /<input\\b[^>]*\\b(?:name|autocomplete)\\s*=\\s*[\"']?(?:otp|one-time-code|verification_?code)\\b/i },\n]\n\n/**\n * What an element asks its reader to DO with an account or a document —\n * counted beside a brand in the SAME element, or on a call to action that\n * leaves the site. The email lure phrasing counts too.\n */\nconst PAGE_ACTION_PATTERNS: readonly RegExp[] = [\n /\\b(?:sign|log)[\\s-]?in\\b/i,\n /\\bverify\\b/i,\n /\\bconfirm\\s+(?:your|the)\\b/i,\n /\\bunlock\\b/i,\n /\\benter\\s+your\\b/i,\n /\\b(?:view|review|open|download|access)\\s+(?:the\\s+|your\\s+)?(?:shared\\s+|secure\\s+)?(?:file|document|invoice|statement)s?\\b/i,\n /\\b(?:continue|proceed)\\s+to\\s+(?:the\\s+|your\\s+)?(?:document|file|account|sign[\\s-]?in|log[\\s-]?in)s?\\b/i,\n]\n\n/**\n * The lures a page is read for as a WHOLE (AGL-3447): account and\n * document-share wording. Not the marketplace, booking and parcel lures,\n * which a hotel's \"guest reviews\" and a shop's delivery page carry every day\n * and which still count in one element beside a brand.\n */\nconst PAGE_LURE_PATTERNS: readonly RegExp[] = [...ACCOUNT_LURE_PATTERNS, ...DOCUMENT_SHARE_LURE_PATTERNS]\n\n/**\n * A component that renders a call to action — a button or a link, however a\n * plugin names it (`muiButton`, `muiScreenLink`, `muiLinkBox`). It is one\n * when it carries a target: an `href`, or a `screenId` on the site itself.\n */\nconst CALL_TO_ACTION_COMPONENT = /button|fab|cta|link/i\n\n/** The props a call to action's visible words live in; the first found wins. */\nconst CALL_TO_ACTION_LABEL_KEYS = ['children', 'label', 'text', 'title', 'html']\n\n/** URLs in page text, so prose is read for a brand or a lure without them. */\nconst PROSE_URL_PATTERN = /(?:https?:)?\\/\\/[^\\s\"'<>]+|\\bwww\\.[^\\s\"'<>]+/gi\n\n/** One call to action: what it says, and the host it leaves for (null: it stays). */\ninterface CallToAction {\n label: string\n host: string | null\n}\n\n/** Every node in the tree, breadth-first in document order, bounded. */\nfunction walkNodes(root: unknown): WalkedNode[] {\n const found: WalkedNode[] = []\n const seen = new Set<unknown>()\n // Each queued value carries the walk index of the node it is nested in, so\n // a nested tree knows its parents without stored ids.\n const stack: Array<{ value: unknown; parentIndex?: number }> = [{ value: root }]\n for (let head = 0; head < stack.length && found.length < MAX_NODES; head += 1) {\n const { value, parentIndex } = stack[head] as { value: unknown; parentIndex?: number }\n if (!value || typeof value !== 'object' || seen.has(value)) continue\n seen.add(value)\n const record = value as Record<string, unknown>\n let enclosing = parentIndex\n if (typeof record['componentId'] === 'string') {\n const props = record['props']\n enclosing = found.length\n found.push({\n componentId: record['componentId'] as string,\n props: props && typeof props === 'object' ? (props as Record<string, unknown>) : {},\n ...(typeof record['$id'] === 'string' ? { id: record['$id'] } : {}),\n ...(typeof record['parentId'] === 'string' ? { parentId: record['parentId'] } : {}),\n ...(parentIndex !== undefined ? { parentIndex } : {}),\n })\n }\n for (const child of Array.isArray(value) ? value : Object.values(record)) {\n if (child && typeof child === 'object') stack.push({ value: child, parentIndex: enclosing })\n }\n }\n return found\n}\n\n/** Every string a node's props hold, one level of arrays and objects deep. */\nfunction propStrings(props: Record<string, unknown>, skip: ReadonlySet<string> = new Set()): string[] {\n const out: string[] = []\n const visit = (value: unknown, depth: number) => {\n if (typeof value === 'string') out.push(value)\n else if (depth < 3 && value && typeof value === 'object') {\n for (const entry of Array.isArray(value) ? value : Object.values(value as Record<string, unknown>)) {\n visit(entry, depth + 1)\n }\n }\n }\n for (const [key, value] of Object.entries(props)) {\n if (key === 'sx' || skip.has(key)) continue\n visit(value, 0)\n }\n return out\n}\n\n/** Does this node describe a field its author defined? */\nfunction isAuthoredField(node: WalkedNode): boolean {\n if (FIELD_ELEMENT_COMPONENTS.has(node.componentId)) return true\n if (/field|input/i.test(node.componentId)) return true\n return FIELD_PROP_KEYS.some((key) => node.props[key] !== undefined)\n}\n\n/**\n * The words BESIDE one of MUI's separate form elements, which carries none of\n * its own: the Choice Label holding a Checkbox or Radio, and the Form Label in\n * the Form Control around it. Read through stored `parentId`s (a flat node\n * map) or through nesting (a tree), whichever the page has.\n */\nfunction siblingLabelOf(nodes: readonly WalkedNode[], index: number): string {\n const node = nodes[index]\n if (!node || !FIELD_ELEMENT_COMPONENTS.has(node.componentId)) return ''\n const byId = new Map<string, number>()\n nodes.forEach((one, at) => {\n if (one.id && !byId.has(one.id)) byId.set(one.id, at)\n })\n const parentOf = (at: number): number | undefined => {\n const one = nodes[at]\n const stored = one?.parentId !== undefined ? byId.get(one.parentId) : undefined\n return stored !== undefined && stored !== at ? stored : one?.parentIndex\n }\n const textOf = (one: WalkedNode | undefined, key: string) =>\n typeof one?.props[key] === 'string' ? (one.props[key] as string).trim() : ''\n const words: string[] = []\n let at = parentOf(index)\n for (let depth = 0; at !== undefined && depth < 8; depth += 1) {\n const ancestor = nodes[at]\n if (ancestor?.componentId === CHOICE_LABEL_COMPONENT && textOf(ancestor, 'label')) {\n words.push(textOf(ancestor, 'label'))\n }\n if (ancestor?.componentId === FORM_CONTROL_COMPONENT) {\n const control = at\n const formLabel = nodes.find(\n (one, oneAt) => one.componentId === FORM_LABEL_COMPONENT && parentOf(oneAt) === control,\n )\n if (textOf(formLabel, 'children')) words.unshift(textOf(formLabel, 'children'))\n break\n }\n at = parentOf(at)\n }\n return words.join(' ')\n}\n\n/** The credential a field asks for, or null. */\nfunction credentialOf(\n node: WalkedNode,\n besideLabel = '',\n): { field: 'password' | 'card' | 'otp'; label: string } | null {\n const text = (key: string) => (typeof node.props[key] === 'string' ? (node.props[key] as string) : '')\n const type = [text('fieldType'), text('type'), text('inputType')].join(' ').toLowerCase()\n const label =\n text('label') || besideLabel || text('ariaLabel') || text('placeholder') || text('fieldName') || text('name')\n if (/\\bpassword\\b/.test(type)) return { field: 'password', label: label || 'password' }\n const autocomplete = (text('autoComplete') || text('autocomplete')).toLowerCase().trim()\n const byToken = CREDENTIAL_AUTOCOMPLETE[autocomplete]\n if (byToken) return { field: byToken, label: label || autocomplete }\n const wording = [\n text('label'),\n besideLabel,\n text('ariaLabel'),\n text('placeholder'),\n text('fieldName'),\n text('name'),\n text('helperText'),\n ]\n .filter(Boolean)\n .join(' ')\n for (const { field, pattern } of CREDENTIAL_LABELS) {\n if (pattern.test(wording)) return { field, label: (label || wording).slice(0, 80) }\n }\n return null\n}\n\n/**\n * The credential an input asks for, read from the two attributes that decide\n * it in the browser — its `type` and its `autocomplete` token — or null.\n * For code that builds inputs rather than storing them as page props: the\n * marketplace verifier reads a plugin bundle's `{ type: 'password' }` and\n * `setAttribute('autocomplete', 'cc-number')` with this (AGL-3362).\n */\nexport function credentialForInputAttribute(\n attribute: string,\n value: string,\n): 'password' | 'card' | 'otp' | null {\n const name = String(attribute ?? '').toLowerCase()\n const token = String(value ?? '').trim().toLowerCase()\n if ((name === 'type' || name === 'inputtype') && token === 'password') return 'password'\n if (name === 'autocomplete') return CREDENTIAL_AUTOCOMPLETE[token] ?? null\n return null\n}\n\n/** The credential a piece of raw HTML asks for in an input of its own, or null. */\nexport function credentialInHtml(html: string): 'password' | 'card' | 'otp' | null {\n const text = String(html ?? '')\n if (!text.includes('<')) return null\n return HTML_CREDENTIAL_INPUTS.find(({ pattern }) => pattern.test(text))?.field ?? null\n}\n\n/**\n * Does a submitted field's NAME ask for a credential — a password, a card\n * number or security code, a one-time code? The same wording the page\n * screen holds a field for, read over a name as a form posts it\n * (`card_number`, `cardNumber`, `one-time-code`).\n *\n * For the one path that does not pass a publish: a public form endpoint\n * takes whatever field names a request carries, so a page the screen never\n * saw (third-party code, a hand-built request) could still post a password\n * to it. The endpoint drops such a field rather than store it.\n */\nexport function isCredentialFieldName(name: string): boolean {\n const words = String(name ?? '')\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .replace(/[_\\-.[\\]]+/g, ' ')\n .trim()\n if (!words) return false\n if (CREDENTIAL_AUTOCOMPLETE[words.toLowerCase().replace(/\\s+/g, '-')]) return true\n if (/^(?:pass|pwd|ccnum|cc\\s?num|cc\\s?number|cvv2?|cvc|csc)$/i.test(words)) return true\n return CREDENTIAL_LABELS.some(({ pattern }) => pattern.test(words))\n}\n\n/** URLs a page carries in a form Links-by-shape would miss: `//host/…`. */\nfunction protocolRelativeUrls(strings: readonly string[]): string {\n return strings\n .filter((value) => /^\\/\\/[a-z0-9-]+(?:\\.[a-z0-9-]+)+/i.test(value.trim()))\n .map((value) => `https:${value.trim()}`)\n .join('\\n')\n}\n\n/**\n * Screen one page as it will be served. The caller applies the tiers with\n * `signalsThatHold` and decides what a hold does.\n */\nexport function screenHostedPage(input: HostedPageScreenInput): HostedPageScreenVerdict {\n const signals: PhishingScreenSignal[] = []\n const nodes = walkNodes(input.nodes)\n const ownNames = squashScreenText((input.ownNames ?? []).filter(Boolean).join(' '))\n const ownHosts = (input.ownDomains ?? [])\n .map((domain) => String(domain ?? '').trim().toLowerCase().replace(/\\.+$/, ''))\n .filter((domain) => domain.includes('.'))\n const ownRegistrables = new Set(ownHosts.map(registrableDomain))\n const isOwnHost = (host: string) => ownHosts.some((own) => host === own || host.endsWith(`.${own}`))\n\n const nodeStrings = nodes.map((node) => propStrings(node.props))\n let pageText = nodeStrings.map((strings) => strings.join('\\n')).join('\\n')\n pageText = `${pageText}\\n${protocolRelativeUrls(nodeStrings.flat())}`.slice(0, MAX_PAGE_TEXT)\n\n // 1. A lookalike host anywhere the page links, embeds or posts to.\n const linkHosts = linkHostsIn(pageText)\n for (const host of linkHosts) {\n const brand = lookalikeBrandForHost(host)\n if (brand && !isBrandsOwnDomain(brand, ownRegistrables)) {\n signals.push({ code: 'lookalike-link', brand: brand.id, host })\n }\n }\n\n // 2. A credential field the page's author defined.\n const credentialSeen = new Set<string>()\n const noteCredential = (field: 'password' | 'card' | 'otp', label: string) => {\n if (credentialSeen.has(field)) return\n credentialSeen.add(field)\n signals.push({ code: 'credential-field', field, label: label.slice(0, 80) })\n }\n nodes.forEach((node, index) => {\n if (isAuthoredField(node)) {\n const credential = credentialOf(node, siblingLabelOf(nodes, index))\n if (credential) noteCredential(credential.field, credential.label)\n }\n for (const html of nodeStrings[index]) {\n if (!html.includes('<')) continue\n for (const { field, pattern } of HTML_CREDENTIAL_INPUTS) {\n if (pattern.test(html)) noteCredential(field, 'an input in the page’s own HTML')\n }\n }\n })\n\n const isElsewhere = (host: string) => {\n if (isOwnHost(host)) return false\n const registrable = registrableDomain(host)\n return !isAnyOfficialBrandDomain(registrable) && !isCommonLinkDomain(registrable)\n }\n const foreign = linkHosts.find(isElsewhere)\n const actions = [...PAGE_ACTION_PATTERNS, ...PHISHING_LURE_PATTERNS]\n const named = new Set<string>()\n const isOthersBrand = (brand: PhishingScreenBrand) =>\n !named.has(brand.id) && !isWorkspaceOwnBrand(brand, ownNames, ownRegistrables)\n\n // 3. A brand's call to action, in one element, on a page that links away.\n if (foreign) {\n for (const strings of nodeStrings) {\n const text = proseOf(strings.join(' '))\n if (!text) continue\n const action = firstMatch(actions, text)\n if (!action) continue\n for (const brand of PHISHING_SCREEN_BRANDS) {\n if (isOthersBrand(brand) && brand.mention.test(text)) {\n named.add(brand.id)\n signals.push({ code: 'brand-action-page', brand: brand.id, action: action.slice(0, 80) })\n }\n }\n }\n }\n\n const prose = proseOf(pageText)\n const pageLure = firstMatch(PAGE_LURE_PATTERNS, prose)\n const ctas = callsToAction(nodes)\n const leaving = ctas.filter((cta): cta is CallToAction & { host: string } =>\n Boolean(cta.host && isElsewhere(cta.host)),\n )\n const wordedLeaving = leaving.find((cta) => firstMatch(actions, cta.label))\n\n // 4. A brand's lure, read over the page as a whole, on a page that links away.\n const brandLure = pageLure ?? (wordedLeaving ? firstMatch(actions, wordedLeaving.label) : undefined)\n if (foreign && brandLure) {\n const host = (wordedLeaving ?? leaving[0])?.host ?? foreign\n for (const brand of PHISHING_SCREEN_BRANDS) {\n if (isOthersBrand(brand) && brand.mention.test(prose)) {\n named.add(brand.id)\n signals.push({ code: 'brand-lure-page', brand: brand.id, lure: brandLure.slice(0, 120), host })\n }\n }\n }\n\n // 5. The page's call to action leaves the site, beside a lure. Its only\n // one, or the one worded as an account or document action.\n const primary = wordedLeaving ?? (ctas.length === 1 ? leaving[0] : undefined)\n if (primary && pageLure) {\n signals.push({\n code: 'offsite-action-page',\n action: (primary.label || primary.host).slice(0, 80),\n lure: pageLure.slice(0, 120),\n host: primary.host,\n })\n }\n\n return { signals, linkHosts, linkUrls: linkUrlsIn(pageText) }\n}\n\n/**\n * What a visitor reads in page text: its markup taken out the way the email\n * screen takes it out ({@link visibleTextOf}), so a custom HTML block or a\n * rich-text prop cannot split a brand or a lure letter by letter with tags\n * (AGL-3453), and its bare URLs taken out too. Links are still read from\n * the raw text, where an href lives.\n */\nfunction proseOf(text: string): string {\n return visibleTextOf(text).replace(PROSE_URL_PATTERN, ' ').replace(/\\s+/g, ' ').trim()\n}\n\n/** The first pattern's match in the text, or undefined. */\nfunction firstMatch(patterns: readonly RegExp[], text: string): string | undefined {\n if (!text) return undefined\n for (const pattern of patterns) {\n const found = text.match(pattern)?.[0]\n if (found) return found\n }\n return undefined\n}\n\n/** The words a call to action shows, from its label prop, markup taken out. */\nfunction callToActionLabel(props: Record<string, unknown>): string {\n for (const key of CALL_TO_ACTION_LABEL_KEYS) {\n const value = props[key]\n if (typeof value === 'string' && value.trim()) {\n return visibleTextOf(value).split('\\n')[0]\n }\n }\n return ''\n}\n\n/**\n * Every call to action on the page: each button or link that carries a\n * target. A `screenId` is a page of the site and takes precedence over an\n * `href`, as the button renders it; a relative, `mailto:` or `tel:` href has\n * no web host and stays too.\n */\nfunction callsToAction(nodes: readonly WalkedNode[]): CallToAction[] {\n const found: CallToAction[] = []\n for (const node of nodes) {\n if (!CALL_TO_ACTION_COMPONENT.test(node.componentId)) continue\n const screenId = typeof node.props['screenId'] === 'string' ? node.props['screenId'].trim() : ''\n const href = typeof node.props['href'] === 'string' ? node.props['href'].trim() : ''\n if (!screenId && !href) continue\n const absolute = href.startsWith('//') ? `https:${href}` : href\n found.push({\n label: callToActionLabel(node.props),\n host: screenId ? null : (linkHostsIn(absolute)[0] ?? null),\n })\n }\n return found\n}\n\nexport interface SiteRedirectScreenInput {\n /** The path the rule answers — `/secure-document` — or its pattern. */\n source: string\n /** Where it sends the visitor, after any `$n` substitution. */\n destination: string\n /** The workspace's own names, as `screenHostedPage` reads them. */\n ownNames?: readonly (string | null | undefined)[]\n /** The workspace's own hosts — a redirect to one never leaves. */\n ownDomains?: readonly (string | null | undefined)[]\n}\n\n/**\n * Screen one site redirect (AGL-3447): a rule that sends a path off the site\n * is a call to action that fires on arrival, so it is read with the page's\n * rules over the one text it has, its path.\n *\n * - `lookalike-link` (STRONG) — the destination wears a brand it is not.\n * - `offsite-redirect` (SOFT) — the destination is not the site's own, a\n * listed brand's or a common link, and the path reads as an account or\n * document-share lure (`/secure-document-access`).\n * - `brand-lure-link` (SOFT) — the same, and the path names a brand that is\n * not the workspace's (`/paypal-verify-your-account`), the email screen's\n * three-part lure.\n *\n * The caller applies the tiers with `signalsThatHold`, with the workspace's\n * age, and decides what a hold does.\n */\nexport function screenSiteRedirect(input: SiteRedirectScreenInput): HostedPageScreenVerdict {\n const signals: PhishingScreenSignal[] = []\n const destination = String(input.destination ?? '').trim()\n const absolute = destination.startsWith('//') ? `https:${destination}` : destination\n const hosts = linkHostsIn(absolute)\n const linkUrls = linkUrlsIn(absolute)\n if (!hosts.length) return { signals, linkHosts: hosts, linkUrls }\n const ownNames = squashScreenText((input.ownNames ?? []).filter(Boolean).join(' '))\n const ownHosts = (input.ownDomains ?? [])\n .map((domain) => String(domain ?? '').trim().toLowerCase().replace(/\\.+$/, ''))\n .filter((domain) => domain.includes('.'))\n const ownRegistrables = new Set(ownHosts.map(registrableDomain))\n\n for (const host of hosts) {\n const brand = lookalikeBrandForHost(host)\n if (brand && !isBrandsOwnDomain(brand, ownRegistrables)) {\n signals.push({ code: 'lookalike-link', brand: brand.id, host })\n }\n }\n\n // The first host is where the browser goes; the rest is a userinfo disguise.\n const host = hosts[0]\n const registrable = registrableDomain(host)\n const leaves =\n !ownHosts.some((own) => host === own || host.endsWith(`.${own}`)) &&\n !isAnyOfficialBrandDomain(registrable) &&\n !isCommonLinkDomain(registrable)\n if (!leaves) return { signals, linkHosts: hosts, linkUrls }\n\n const source = String(input.source ?? '').trim()\n const words = pathWords(source)\n const lure = firstMatch(PAGE_LURE_PATTERNS, words)\n if (lure) {\n signals.push({ code: 'offsite-redirect', source: source.slice(0, 120), lure: lure.slice(0, 120), host })\n }\n const anyLure = lure ?? firstMatch(PHISHING_LURE_PATTERNS, words)\n if (anyLure) {\n const brand = PHISHING_SCREEN_BRANDS.find(\n (candidate) =>\n candidate.mention.test(words) && !isWorkspaceOwnBrand(candidate, ownNames, ownRegistrables),\n )\n if (brand) {\n signals.push({ code: 'brand-lure-link', brand: brand.id, lure: anyLure.slice(0, 120), host })\n }\n }\n return { signals, linkHosts: hosts, linkUrls }\n}\n\n/** A path read as words: `/secure-document_access` → `secure document access`. */\nfunction pathWords(path: string): string {\n let decoded = path\n try {\n decoded = decodeURIComponent(path)\n } catch {\n // A malformed escape is read as typed.\n }\n return decoded.replace(/[^a-z0-9]+/gi, ' ').trim()\n}\n"],"names":["ACCOUNT_LURE_PATTERNS","DOCUMENT_SHARE_LURE_PATTERNS","isWorkspaceOwnBrand","linkHostsIn","linkUrlsIn","isBrandsOwnDomain","lookalikeBrandForHost","PHISHING_LURE_PATTERNS","PHISHING_SCREEN_BRANDS","registrableDomain","isAnyOfficialBrandDomain","isCommonLinkDomain","squashScreenText","visibleTextOf","MAX_PAGE_TEXT","MAX_NODES","FIELD_ELEMENT_COMPONENTS","Set","FORM_CONTROL_COMPONENT","FORM_LABEL_COMPONENT","CHOICE_LABEL_COMPONENT","FIELD_PROP_KEYS","CREDENTIAL_AUTOCOMPLETE","CREDENTIAL_LABELS","field","pattern","HTML_CREDENTIAL_INPUTS","PAGE_ACTION_PATTERNS","PAGE_LURE_PATTERNS","CALL_TO_ACTION_COMPONENT","CALL_TO_ACTION_LABEL_KEYS","PROSE_URL_PATTERN","walkNodes","root","found","seen","stack","value","head","length","parentIndex","has","add","record","enclosing","props","push","componentId","id","parentId","undefined","child","Array","isArray","Object","values","propStrings","skip","out","visit","depth","entry","key","entries","isAuthoredField","node","test","some","siblingLabelOf","nodes","index","byId","Map","forEach","one","at","set","parentOf","stored","get","textOf","trim","words","ancestor","control","formLabel","find","oneAt","unshift","join","credentialOf","besideLabel","text","type","toLowerCase","label","autocomplete","byToken","wording","filter","Boolean","slice","credentialForInputAttribute","attribute","name","String","token","credentialInHtml","html","includes","isCredentialFieldName","replace","protocolRelativeUrls","strings","map","screenHostedPage","input","signals","ownNames","ownHosts","ownDomains","domain","ownRegistrables","isOwnHost","host","own","endsWith","nodeStrings","pageText","flat","linkHosts","brand","code","credentialSeen","noteCredential","credential","isElsewhere","registrable","foreign","actions","named","isOthersBrand","proseOf","action","firstMatch","mention","prose","pageLure","ctas","callsToAction","leaving","cta","wordedLeaving","brandLure","lure","primary","linkUrls","patterns","match","callToActionLabel","split","screenId","href","absolute","startsWith","screenSiteRedirect","destination","hosts","leaves","source","pathWords","anyLure","candidate","path","decoded","decodeURIComponent"],"mappings":"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"}
@@ -970,6 +970,66 @@
970
970
  ],
971
971
  "footerReason": "You’re receiving this because you started a checkout with {{host.businessName}}."
972
972
  },
973
+ {
974
+ "key": "review-request",
975
+ "name": "Review request",
976
+ "description": "Asks a buyer to review what they bought, a set number of days after the order is fulfilled. One per order; each product has its own single-use link. Turn it off or change the delay in Settings → Reviews.",
977
+ "requiresFeature": "productReviews",
978
+ "pluginId": "commerce",
979
+ "plugin": "Commerce",
980
+ "control": "besigner",
981
+ "defaultSubject": "How was your order from {{host.businessName}}?",
982
+ "mergeTokens": [
983
+ {
984
+ "name": "order.number",
985
+ "description": "The order number, as the store shows it",
986
+ "sample": "#1042"
987
+ },
988
+ {
989
+ "name": "review.products",
990
+ "description": "Each product to review, one per line, with its own review link",
991
+ "sample": "House Blend: https://shop.example.com/products/house-blend?review=…"
992
+ },
993
+ {
994
+ "name": "review.url",
995
+ "description": "The review link for the first product",
996
+ "sample": "https://shop.example.com/products/house-blend?review=…"
997
+ },
998
+ {
999
+ "name": "review.productName",
1000
+ "description": "The first product’s name",
1001
+ "sample": "House Blend"
1002
+ }
1003
+ ],
1004
+ "defaultBody": [
1005
+ {
1006
+ "block": "text",
1007
+ "text": "How did we do?",
1008
+ "variant": "heading"
1009
+ },
1010
+ {
1011
+ "block": "text",
1012
+ "text": "Thanks again for your order {{order.number}} from {{host.businessName}}. Would you tell other shoppers what you think? Each link opens the product with a review form.",
1013
+ "variant": "body"
1014
+ },
1015
+ {
1016
+ "block": "text",
1017
+ "text": "{{review.products}}",
1018
+ "variant": "body"
1019
+ },
1020
+ {
1021
+ "block": "button",
1022
+ "label": "Write a review",
1023
+ "href": "{{review.url}}"
1024
+ },
1025
+ {
1026
+ "block": "text",
1027
+ "text": "Each link works once, for 60 days.",
1028
+ "variant": "caption"
1029
+ }
1030
+ ],
1031
+ "footerReason": "You’re receiving this because you placed an order with {{host.businessName}}."
1032
+ },
973
1033
  {
974
1034
  "key": "member-post",
975
1035
  "name": "New member post",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/shared/util/email/src/lib/tenant-emails.generated.ts"],"sourcesContent":["/**\n * GENERATED FILE — do not edit. Regenerate with:\n * node tools/scripts/generate-plugin-manifests.mjs\n *\n * The emails sites send their own customers (AGL-3080): what each plugin's\n * `tenantEmails` entry returned when this file was generated, in config\n * order. `TENANT_EMAILS` in `tenant-email-catalog.ts` is this list.\n * Source of truth: plugins.config.json and the entries it names.\n */\n\nimport type { TenantEmailEntry } from './tenant-email-catalog'\n\nexport const PLUGIN_TENANT_EMAILS: readonly TenantEmailEntry[] = [\n {\n \"key\": \"booking-confirmed\",\n \"name\": \"Booking confirmed\",\n \"description\": \"Confirms a booking to the customer, for paid and free services.\",\n \"pluginId\": \"bookings\",\n \"plugin\": \"Bookings\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Booking confirmed: {{service.name}}\",\n \"mergeTokens\": [\n {\n \"name\": \"name\",\n \"description\": \"The customer's name\",\n \"sample\": \"Alex\"\n },\n {\n \"name\": \"service.name\",\n \"description\": \"The booked service\",\n \"sample\": \"Consultation\"\n },\n {\n \"name\": \"when\",\n \"description\": \"Formatted date and time of the booking\",\n \"sample\": \"Monday, June 1, 2026 at 9:00 AM\"\n },\n {\n \"name\": \"timezone\",\n \"description\": \"Timezone the time is shown in\",\n \"sample\": \"America/Chicago\"\n },\n {\n \"name\": \"booking.payment\",\n \"description\": \"What a paid booking charged, as a sentence; empty for a free booking\",\n \"sample\": \"You paid $95.00.\"\n },\n {\n \"name\": \"booking.ref\",\n \"description\": \"Booking reference id\",\n \"sample\": \"bk_123\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Booking confirmed\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Hi {{name}}, your booking with {{host.businessName}} is confirmed: \\\"{{service.name}}\\\" on {{when}} ({{timezone}}).\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{booking.payment}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Reference: {{booking.ref}}\",\n \"variant\": \"caption\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you booked with {{host.businessName}}.\"\n },\n {\n \"key\": \"booking-reminder\",\n \"name\": \"Booking reminder\",\n \"description\": \"Reminds the customer of an upcoming booking.\",\n \"pluginId\": \"bookings\",\n \"plugin\": \"Bookings\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Reminder: {{service.name}} is coming up\",\n \"mergeTokens\": [\n {\n \"name\": \"name\",\n \"description\": \"The customer's name\",\n \"sample\": \"Alex\"\n },\n {\n \"name\": \"service.name\",\n \"description\": \"The booked service\",\n \"sample\": \"Consultation\"\n },\n {\n \"name\": \"when\",\n \"description\": \"Formatted date and time of the booking\",\n \"sample\": \"Tuesday, June 2, 2026 at 9:00 AM\"\n },\n {\n \"name\": \"timezone\",\n \"description\": \"Timezone the time is shown in\",\n \"sample\": \"America/Chicago\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Hi {{name}}, this is a reminder of your booking with {{host.businessName}}: \\\"{{service.name}}\\\" on {{when}} ({{timezone}}).\",\n \"variant\": \"body\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you have a booking with {{host.businessName}}.\"\n },\n {\n \"key\": \"booking-rescheduled\",\n \"name\": \"Booking rescheduled\",\n \"description\": \"Tells the customer their booking moved to a new time.\",\n \"pluginId\": \"bookings\",\n \"plugin\": \"Bookings\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"New time for {{service.name}}\",\n \"mergeTokens\": [\n {\n \"name\": \"name\",\n \"description\": \"The customer's name\",\n \"sample\": \"Alex\"\n },\n {\n \"name\": \"service.name\",\n \"description\": \"The booked service\",\n \"sample\": \"Consultation\"\n },\n {\n \"name\": \"when\",\n \"description\": \"The new date and time of the booking\",\n \"sample\": \"Wednesday, June 3, 2026 at 10:30 AM\"\n },\n {\n \"name\": \"previousWhen\",\n \"description\": \"The date and time the booking moved from\",\n \"sample\": \"Monday, June 1, 2026 at 9:00 AM\"\n },\n {\n \"name\": \"timezone\",\n \"description\": \"Timezone the times are shown in\",\n \"sample\": \"America/Chicago\"\n },\n {\n \"name\": \"booking.ref\",\n \"description\": \"Booking reference id\",\n \"sample\": \"bk_123\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Your booking has a new time\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Hi {{name}}, your booking with {{host.businessName}} for \\\"{{service.name}}\\\" is now on {{when}} ({{timezone}}). It was on {{previousWhen}}.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Reference: {{booking.ref}}\",\n \"variant\": \"caption\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you have a booking with {{host.businessName}}.\"\n },\n {\n \"key\": \"order-receipt\",\n \"name\": \"Order receipt\",\n \"description\": \"Sent to the buyer after a successful order or checkout.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Receipt for your order\",\n \"mergeTokens\": [\n {\n \"name\": \"order.summary\",\n \"description\": \"The ordered items, one per line (with any license/download links)\",\n \"sample\": \"House Blend × 2 — $24.00\"\n },\n {\n \"name\": \"order.total\",\n \"description\": \"Order total\",\n \"sample\": \"$24.00\"\n },\n {\n \"name\": \"order.ref\",\n \"description\": \"Order reference id\",\n \"sample\": \"cs_test_123\"\n },\n {\n \"name\": \"store.receiptFooter\",\n \"description\": \"The Receipt footer from the store settings; empty when none is set\",\n \"sample\": \"Returns are accepted within 30 days.\"\n },\n {\n \"name\": \"order.number\",\n \"description\": \"The order number, as the store shows it\",\n \"sample\": \"#1042\"\n },\n {\n \"name\": \"order.statusUrl\",\n \"description\": \"Link to the order status page: status, shipments and tracking. Private to the buyer; it needs no account\",\n \"sample\": \"https://shop.example.com/order-status?o=ord_123&t=…\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Thanks for your purchase!\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Here is the receipt for your order from {{host.businessName}}.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{order.summary}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Total charged: {{order.total}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{store.receiptFooter}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"View your order\",\n \"href\": \"{{order.statusUrl}}\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Order reference: {{order.ref}}\",\n \"variant\": \"caption\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you placed an order with {{host.businessName}}.\"\n },\n {\n \"key\": \"order-shipped\",\n \"name\": \"Order shipped\",\n \"description\": \"Tells the buyer part or all of their order is on its way, with the carrier and a tracking link when the shipment has one. Sent once per shipment.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Your order {{order.number}} has shipped\",\n \"mergeTokens\": [\n {\n \"name\": \"order.number\",\n \"description\": \"The order number, as the store shows it\",\n \"sample\": \"#1042\"\n },\n {\n \"name\": \"shipment.summary\",\n \"description\": \"The items in this shipment, one per line\",\n \"sample\": \"2× House Blend\"\n },\n {\n \"name\": \"shipment.carrier\",\n \"description\": \"The carrier; empty when none was recorded\",\n \"sample\": \"UPS\"\n },\n {\n \"name\": \"shipment.trackingNumber\",\n \"description\": \"The tracking number; empty when none was recorded\",\n \"sample\": \"1Z999AA10123456784\"\n },\n {\n \"name\": \"shipment.tracking\",\n \"description\": \"A sentence with the carrier, tracking number and tracking link; empty when the shipment has no tracking number\",\n \"sample\": \"Tracking: UPS 1Z999AA10123456784 — https://www.ups.com/track?tracknum=1Z999AA10123456784\"\n },\n {\n \"name\": \"shipment.remaining\",\n \"description\": \"A sentence saying how many items are still to ship; empty when this shipment completes the order\",\n \"sample\": \"1 more item will ship separately.\"\n },\n {\n \"name\": \"order.statusUrl\",\n \"description\": \"Link to the order status page: status, shipments and tracking. Private to the buyer; it needs no account\",\n \"sample\": \"https://shop.example.com/order-status?o=ord_123&t=…\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Your order is on its way\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{host.businessName}} shipped items from order {{order.number}}.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{shipment.summary}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{shipment.tracking}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{shipment.remaining}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"Track your order\",\n \"href\": \"{{order.statusUrl}}\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you placed an order with {{host.businessName}}.\"\n },\n {\n \"key\": \"order-delivered\",\n \"name\": \"Order delivered\",\n \"description\": \"Tells the buyer their order was marked delivered.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Your order {{order.number}} was delivered\",\n \"mergeTokens\": [\n {\n \"name\": \"order.number\",\n \"description\": \"The order number, as the store shows it\",\n \"sample\": \"#1042\"\n },\n {\n \"name\": \"order.summary\",\n \"description\": \"The ordered items, one per line\",\n \"sample\": \"2× House Blend\"\n },\n {\n \"name\": \"order.statusUrl\",\n \"description\": \"Link to the order status page: status, shipments and tracking. Private to the buyer; it needs no account\",\n \"sample\": \"https://shop.example.com/order-status?o=ord_123&t=…\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Your order was delivered\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{host.businessName}} marked order {{order.number}} as delivered.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{order.summary}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"View your order\",\n \"href\": \"{{order.statusUrl}}\"\n },\n {\n \"block\": \"text\",\n \"text\": \"If something is missing or not right, reply to this email or contact {{host.businessName}}.\",\n \"variant\": \"caption\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you placed an order with {{host.businessName}}.\"\n },\n {\n \"key\": \"order-refunded\",\n \"name\": \"Order refunded\",\n \"description\": \"Tells the buyer a refund was issued, for how much and, when the store refunded named items, which ones. Sent once per refund.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Refund for your order {{order.number}}\",\n \"mergeTokens\": [\n {\n \"name\": \"order.number\",\n \"description\": \"The order number, as the store shows it\",\n \"sample\": \"#1042\"\n },\n {\n \"name\": \"refund.amount\",\n \"description\": \"The amount of this refund\",\n \"sample\": \"$12.00\"\n },\n {\n \"name\": \"refund.summary\",\n \"description\": \"The refunded items, one per line; empty when the refund was by amount only\",\n \"sample\": \"1× House Blend\"\n },\n {\n \"name\": \"refund.note\",\n \"description\": \"Whether the order is now fully refunded, as a sentence\",\n \"sample\": \"Your order has been refunded in full.\"\n },\n {\n \"name\": \"order.statusUrl\",\n \"description\": \"Link to the order status page: status, shipments and tracking. Private to the buyer; it needs no account\",\n \"sample\": \"https://shop.example.com/order-status?o=ord_123&t=…\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Your refund is on its way\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{host.businessName}} refunded {{refund.amount}} on order {{order.number}}.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{refund.summary}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{refund.note}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Refunds go back to the original payment method and usually appear within 5 to 10 business days, depending on your bank.\",\n \"variant\": \"caption\"\n },\n {\n \"block\": \"button\",\n \"label\": \"View your order\",\n \"href\": \"{{order.statusUrl}}\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you placed an order with {{host.businessName}}.\"\n },\n {\n \"key\": \"order-cancelled\",\n \"name\": \"Order canceled\",\n \"description\": \"Tells the buyer their order was canceled.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Your order {{order.number}} was canceled\",\n \"mergeTokens\": [\n {\n \"name\": \"order.number\",\n \"description\": \"The order number, as the store shows it\",\n \"sample\": \"#1042\"\n },\n {\n \"name\": \"order.summary\",\n \"description\": \"The ordered items, one per line\",\n \"sample\": \"2× House Blend\"\n },\n {\n \"name\": \"cancel.note\",\n \"description\": \"What happens to the payment, as a sentence; empty when nothing was paid\",\n \"sample\": \"Any payment you made will be refunded, and you will get a separate email when it is.\"\n },\n {\n \"name\": \"order.statusUrl\",\n \"description\": \"Link to the order status page: status, shipments and tracking. Private to the buyer; it needs no account\",\n \"sample\": \"https://shop.example.com/order-status?o=ord_123&t=…\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Your order was canceled\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{host.businessName}} canceled order {{order.number}}.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{order.summary}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{cancel.note}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"View your order\",\n \"href\": \"{{order.statusUrl}}\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you placed an order with {{host.businessName}}.\"\n },\n {\n \"key\": \"order-ready-for-pickup\",\n \"name\": \"Order ready for pickup\",\n \"description\": \"Tells the buyer their pickup order is ready, where to collect it, the pickup hours and any arrival instructions.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Your order {{order.number}} is ready for pickup\",\n \"mergeTokens\": [\n {\n \"name\": \"order.number\",\n \"description\": \"The order number, as the store shows it\",\n \"sample\": \"#1042\"\n },\n {\n \"name\": \"order.summary\",\n \"description\": \"The ordered items, one per line\",\n \"sample\": \"2× House Blend\"\n },\n {\n \"name\": \"pickup.location\",\n \"description\": \"The pickup location’s name and address\",\n \"sample\": \"Main Street shop, 12 Main St, Springfield\"\n },\n {\n \"name\": \"pickup.hours\",\n \"description\": \"The location’s pickup hours; empty when it lists none\",\n \"sample\": \"Pickup hours:\\nMo-Fr 09:00-17:00\"\n },\n {\n \"name\": \"pickup.instructions\",\n \"description\": \"What to do on arrival, as the store wrote it; empty when it wrote none\",\n \"sample\": \"Ring the bell at the side door.\"\n },\n {\n \"name\": \"order.statusUrl\",\n \"description\": \"Link to the order status page: status, shipments and tracking. Private to the buyer; it needs no account\",\n \"sample\": \"https://shop.example.com/order-status?o=ord_123&t=…\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Your order is ready for pickup\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Order {{order.number}} is ready at {{pickup.location}}.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{order.summary}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{pickup.hours}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{pickup.instructions}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Bring your order number, {{order.number}}.\",\n \"variant\": \"caption\"\n },\n {\n \"block\": \"button\",\n \"label\": \"View your order\",\n \"href\": \"{{order.statusUrl}}\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you placed an order with {{host.businessName}}.\"\n },\n {\n \"key\": \"order-picked-up\",\n \"name\": \"Order picked up\",\n \"description\": \"Confirms to the buyer that their pickup order was collected.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"You picked up order {{order.number}}\",\n \"mergeTokens\": [\n {\n \"name\": \"order.number\",\n \"description\": \"The order number, as the store shows it\",\n \"sample\": \"#1042\"\n },\n {\n \"name\": \"order.summary\",\n \"description\": \"The ordered items, one per line\",\n \"sample\": \"2× House Blend\"\n },\n {\n \"name\": \"pickup.location\",\n \"description\": \"The pickup location’s name and address\",\n \"sample\": \"Main Street shop, 12 Main St, Springfield\"\n },\n {\n \"name\": \"order.statusUrl\",\n \"description\": \"Link to the order status page: status, shipments and tracking. Private to the buyer; it needs no account\",\n \"sample\": \"https://shop.example.com/order-status?o=ord_123&t=…\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Thanks for picking up your order\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Order {{order.number}} was picked up from {{pickup.location}}.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{order.summary}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"If something is missing or not right, reply to this email or contact {{host.businessName}}.\",\n \"variant\": \"caption\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you placed an order with {{host.businessName}}.\"\n },\n {\n \"key\": \"order-out-for-delivery\",\n \"name\": \"Order out for delivery\",\n \"description\": \"Tells the buyer the store’s own driver is on the way with their local delivery, in the window they booked.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Your order {{order.number}} is out for delivery\",\n \"mergeTokens\": [\n {\n \"name\": \"order.number\",\n \"description\": \"The order number, as the store shows it\",\n \"sample\": \"#1042\"\n },\n {\n \"name\": \"order.summary\",\n \"description\": \"The ordered items, one per line\",\n \"sample\": \"2× House Blend\"\n },\n {\n \"name\": \"delivery.window\",\n \"description\": \"The delivery window the buyer booked\",\n \"sample\": \"Tue, Oct 14, 9:00 AM – 12:00 PM\"\n },\n {\n \"name\": \"order.statusUrl\",\n \"description\": \"Link to the order status page: status, shipments and tracking. Private to the buyer; it needs no account\",\n \"sample\": \"https://shop.example.com/order-status?o=ord_123&t=…\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Your order is on its way\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{host.businessName}} is out delivering order {{order.number}}. Your window: {{delivery.window}}.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{order.summary}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"View your order\",\n \"href\": \"{{order.statusUrl}}\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you placed an order with {{host.businessName}}.\"\n },\n {\n \"key\": \"sale-notification\",\n \"name\": \"New sale\",\n \"description\": \"Notifies the seller when an order is placed.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"New order on {{site.name}}\",\n \"mergeTokens\": [\n {\n \"name\": \"site.name\",\n \"description\": \"The site the sale happened on\",\n \"sample\": \"Northwind Coffee\"\n },\n {\n \"name\": \"order.summary\",\n \"description\": \"The ordered items\",\n \"sample\": \"House Blend — $12.00\"\n },\n {\n \"name\": \"order.total\",\n \"description\": \"Order total\",\n \"sample\": \"$12.00\"\n },\n {\n \"name\": \"buyer.email\",\n \"description\": \"The buyer's email\",\n \"sample\": \"buyer@example.com\"\n },\n {\n \"name\": \"order.ref\",\n \"description\": \"Order reference id\",\n \"sample\": \"cs_test_123\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"You made a sale\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"A new order came in on {{site.name}}.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{order.summary}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Total: {{order.total}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Buyer: {{buyer.email}} · Order {{order.ref}}\",\n \"variant\": \"caption\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you manage {{host.businessName}}.\"\n },\n {\n \"key\": \"reservation-confirmed\",\n \"name\": \"Reservation confirmed\",\n \"description\": \"Confirms a paid reservation to the customer.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Reservation confirmed\",\n \"mergeTokens\": [\n {\n \"name\": \"reservation.checkIn\",\n \"description\": \"Check-in date\",\n \"sample\": \"Mon, 01 Jun 2026\"\n },\n {\n \"name\": \"reservation.nights\",\n \"description\": \"Number of nights\",\n \"sample\": \"2\"\n },\n {\n \"name\": \"reservation.paid\",\n \"description\": \"What the guest was charged today, lodging tax included; a charge that carried lodging tax names it, as in “$254.40, including $14.40 lodging tax”\",\n \"sample\": \"$240.00\"\n },\n {\n \"name\": \"reservation.balance\",\n \"description\": \"What is still owed for the stay and where it is paid, as a sentence; empty when the stay is paid in full\",\n \"sample\": \"Still to pay: $360.00, at the property. It has not been charged.\"\n },\n {\n \"name\": \"reservation.ref\",\n \"description\": \"Reservation reference id\",\n \"sample\": \"resv_123\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Reservation confirmed\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Your stay at {{host.businessName}} is confirmed.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Check-in: {{reservation.checkIn}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Nights: {{reservation.nights}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Paid today: {{reservation.paid}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{reservation.balance}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Reference: {{reservation.ref}}\",\n \"variant\": \"caption\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you made a reservation with {{host.businessName}}.\"\n },\n {\n \"key\": \"gift-card\",\n \"name\": \"Gift card delivery\",\n \"description\": \"Delivers a purchased gift card to its recipient.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Your gift card for {{host.businessName}}\",\n \"mergeTokens\": [\n {\n \"name\": \"giftcard.code\",\n \"description\": \"The gift card code\",\n \"sample\": \"GC-ABC123DEF456\"\n },\n {\n \"name\": \"giftcard.value\",\n \"description\": \"The gift card value\",\n \"sample\": \"$25.00\"\n },\n {\n \"name\": \"giftcard.note\",\n \"description\": \"The note written when the card was issued by hand; empty for a card bought at checkout\",\n \"sample\": \"Happy birthday, Sam!\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Your gift card\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"You have a {{giftcard.value}} gift card for {{host.businessName}}.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{giftcard.note}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Gift card code: {{giftcard.code}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Enter the code at checkout on {{host.url}} to use its balance.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"Visit {{host.businessName}}\",\n \"href\": \"{{host.url}}\"\n }\n ],\n \"footerReason\": \"You’re receiving this because a gift card from {{host.businessName}} was sent to this address.\"\n },\n {\n \"key\": \"supplier-fulfillment\",\n \"name\": \"Supplier fulfillment\",\n \"description\": \"Tells a dropship supplier there is a new order to fulfill.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"fixed\"\n },\n {\n \"key\": \"back-in-stock\",\n \"name\": \"Back in stock\",\n \"description\": \"Tells a customer a product they watched is available again.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Back in stock: {{product.name}}\",\n \"mergeTokens\": [\n {\n \"name\": \"product.name\",\n \"description\": \"The product that restocked\",\n \"sample\": \"House Blend\"\n },\n {\n \"name\": \"product.url\",\n \"description\": \"Link to the product page\",\n \"sample\": \"https://shop.example.com/products/house-blend\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"{{product.name}} is back in stock at {{host.businessName}}.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"View product\",\n \"href\": \"{{product.url}}\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you asked {{host.businessName}} to tell you when this was back.\"\n },\n {\n \"key\": \"abandoned-cart\",\n \"name\": \"Abandoned cart\",\n \"description\": \"Reminds a shopper of items left in their cart.\",\n \"requiresFeature\": \"abandonedCart\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"You left something in your cart\",\n \"mergeTokens\": [\n {\n \"name\": \"cart.url\",\n \"description\": \"Link back to the shopper cart\",\n \"sample\": \"https://shop.example.com/cart\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"You left items in your cart at {{host.businessName}}. Pick up where you left off.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"Return to cart\",\n \"href\": \"{{cart.url}}\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Nothing in your cart is held for you.\",\n \"variant\": \"caption\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you started a checkout with {{host.businessName}}.\"\n },\n {\n \"key\": \"member-post\",\n \"name\": \"New member post\",\n \"description\": \"Notifies members when new members-only content is posted.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"{{post.title}}\",\n \"mergeTokens\": [\n {\n \"name\": \"post.title\",\n \"description\": \"The post’s title\",\n \"sample\": \"This month in the studio\"\n },\n {\n \"name\": \"post.body\",\n \"description\": \"The post, as its author wrote it\",\n \"sample\": \"Three new pieces are up for members first.\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"{{post.title}}\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{post.body}}\",\n \"variant\": \"body\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you’re a member of {{host.businessName}}.\"\n },\n {\n \"key\": \"member-password-reset\",\n \"name\": \"Member password reset\",\n \"description\": \"Resets a site member's password — a store-member account, separate from a console login. Sent when the member asks, and when a site administrator starts a reset for them.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Reset your {{site.name}} password\",\n \"mergeTokens\": [\n {\n \"name\": \"site.name\",\n \"description\": \"The site’s name\",\n \"sample\": \"Northwind Coffee\"\n },\n {\n \"name\": \"reset.intro\",\n \"description\": \"Who started the reset\",\n \"sample\": \"Someone asked to reset the password for your Northwind Coffee account.\"\n },\n {\n \"name\": \"resetUrl\",\n \"description\": \"The one-time reset link\",\n \"sample\": \"https://shop.example.com/recover?token=…\"\n },\n {\n \"name\": \"reset.note\",\n \"description\": \"How long the link lasts, and what to do if unexpected\",\n \"sample\": \"The link works once and expires in 1 hour. If you did not ask for this, you can safely ignore this email — your password is unchanged.\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Reset your password\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{reset.intro}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"Set a new password\",\n \"href\": \"{{resetUrl}}\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{reset.note}}\",\n \"variant\": \"caption\"\n }\n ],\n \"footerReason\": \"You’re receiving this because a password reset was started for your {{host.businessName}} account.\"\n },\n {\n \"key\": \"member-password-changed\",\n \"name\": \"Member password changed\",\n \"description\": \"Tells a site member that a site administrator set a new password on their account.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Your {{site.name}} password was changed\",\n \"mergeTokens\": [\n {\n \"name\": \"site.name\",\n \"description\": \"The site’s name\",\n \"sample\": \"Northwind Coffee\"\n },\n {\n \"name\": \"signInUrl\",\n \"description\": \"Where the member signs back in\",\n \"sample\": \"https://shop.example.com\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Your password was changed\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"An administrator of {{site.name}} set a new password on your account and signed you out on every device. To sign back in, get the new password from them, or choose \\\"Forgot password?\\\" on the sign-in page to set your own.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"Sign in\",\n \"href\": \"{{signInUrl}}\"\n },\n {\n \"block\": \"text\",\n \"text\": \"If you did not expect this, contact the site owner.\",\n \"variant\": \"caption\"\n }\n ],\n \"footerReason\": \"You’re receiving this because the password on your {{host.businessName}} account was changed.\"\n },\n {\n \"key\": \"newsletter-confirmation\",\n \"name\": \"Subscription confirmation\",\n \"description\": \"Asks someone who signed up for a site’s emails to confirm the address before anything is sent to it.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Confirm your subscription\",\n \"mergeTokens\": [\n {\n \"name\": \"stream.name\",\n \"description\": \"What they signed up for\",\n \"sample\": \"Newsletter\"\n },\n {\n \"name\": \"confirmUrl\",\n \"description\": \"The confirmation link\",\n \"sample\": \"https://shop.example.com/subscribe/confirm?token=…\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Confirm your subscription\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Please confirm that you want to get {{stream.name}} emails from {{host.businessName}} at this address.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"Confirm my subscription\",\n \"href\": \"{{confirmUrl}}\"\n },\n {\n \"block\": \"text\",\n \"text\": \"The link works for three days. If you did not sign up, ignore this message — nothing will be sent.\",\n \"variant\": \"caption\"\n }\n ],\n \"footerReason\": \"You’re receiving this because this address was entered on {{host.businessName}}’s signup form.\"\n },\n {\n \"key\": \"return-requested\",\n \"name\": \"Return requested\",\n \"description\": \"Tells the store a buyer asked to return items, with what and why, so someone can approve or decline it.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Return requested for order {{order.number}}\",\n \"mergeTokens\": [\n {\n \"name\": \"order.number\",\n \"description\": \"The order number, as the store shows it\",\n \"sample\": \"#1042\"\n },\n {\n \"name\": \"return.items\",\n \"description\": \"The items being returned, one per line, with the reason\",\n \"sample\": \"1× House Blend — Arrived damaged\"\n },\n {\n \"name\": \"buyer.email\",\n \"description\": \"The buyer’s email\",\n \"sample\": \"buyer@example.com\"\n },\n {\n \"name\": \"return.customerNote\",\n \"description\": \"What the buyer wrote; empty when they wrote nothing\",\n \"sample\": \"The lid was cracked.\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"A buyer asked for a return\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{buyer.email}} wants to return items from order {{order.number}}.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{return.items}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{return.customerNote}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Approve or decline it under Products → Returns.\",\n \"variant\": \"caption\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you manage {{host.businessName}}.\"\n },\n {\n \"key\": \"return-approved\",\n \"name\": \"Return approved\",\n \"description\": \"Tells the buyer their return was approved and how to send the items back, with the return label when the store attached one.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Your return for order {{order.number}} is approved\",\n \"mergeTokens\": [\n {\n \"name\": \"order.number\",\n \"description\": \"The order number, as the store shows it\",\n \"sample\": \"#1042\"\n },\n {\n \"name\": \"return.items\",\n \"description\": \"The items being returned, one per line, with the reason\",\n \"sample\": \"1× House Blend — Arrived damaged\"\n },\n {\n \"name\": \"return.note\",\n \"description\": \"The store’s note to the buyer on this return; empty when none\",\n \"sample\": \"Please include the original packaging.\"\n },\n {\n \"name\": \"return.label\",\n \"description\": \"A sentence with the return label link and its tracking; empty when the store attached no label\",\n \"sample\": \"Print your return label: https://labels.example.com/r/123\"\n },\n {\n \"name\": \"order.statusUrl\",\n \"description\": \"Link to the order status page: status, shipments and tracking. Private to the buyer; it needs no account\",\n \"sample\": \"https://shop.example.com/order-status?o=ord_123&t=…\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Your return is approved\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{host.businessName}} approved the return of:\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{return.items}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{return.note}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{return.label}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"View your order\",\n \"href\": \"{{order.statusUrl}}\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you placed an order with {{host.businessName}}.\"\n },\n {\n \"key\": \"return-declined\",\n \"name\": \"Return declined\",\n \"description\": \"Tells the buyer their return was declined, with the store’s note.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"About your return for order {{order.number}}\",\n \"mergeTokens\": [\n {\n \"name\": \"order.number\",\n \"description\": \"The order number, as the store shows it\",\n \"sample\": \"#1042\"\n },\n {\n \"name\": \"return.items\",\n \"description\": \"The items being returned, one per line, with the reason\",\n \"sample\": \"1× House Blend — Arrived damaged\"\n },\n {\n \"name\": \"return.note\",\n \"description\": \"The store’s note to the buyer on this return; empty when none\",\n \"sample\": \"Please include the original packaging.\"\n },\n {\n \"name\": \"order.statusUrl\",\n \"description\": \"Link to the order status page: status, shipments and tracking. Private to the buyer; it needs no account\",\n \"sample\": \"https://shop.example.com/order-status?o=ord_123&t=…\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"We could not accept this return\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{host.businessName}} declined the return of:\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{return.items}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{return.note}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"View your order\",\n \"href\": \"{{order.statusUrl}}\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you placed an order with {{host.businessName}}.\"\n },\n {\n \"key\": \"return-refunded\",\n \"name\": \"Return refunded\",\n \"description\": \"Tells the buyer their return was received and refunded, and how much.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Your refund for order {{order.number}}\",\n \"mergeTokens\": [\n {\n \"name\": \"order.number\",\n \"description\": \"The order number, as the store shows it\",\n \"sample\": \"#1042\"\n },\n {\n \"name\": \"return.items\",\n \"description\": \"The items being returned, one per line, with the reason\",\n \"sample\": \"1× House Blend — Arrived damaged\"\n },\n {\n \"name\": \"return.refundTotal\",\n \"description\": \"The amount refunded for this return\",\n \"sample\": \"$24.00\"\n },\n {\n \"name\": \"order.statusUrl\",\n \"description\": \"Link to the order status page: status, shipments and tracking. Private to the buyer; it needs no account\",\n \"sample\": \"https://shop.example.com/order-status?o=ord_123&t=…\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Your return is refunded\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{host.businessName}} received your return and refunded {{return.refundTotal}} to your original payment method.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{return.items}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"View your order\",\n \"href\": \"{{order.statusUrl}}\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you placed an order with {{host.businessName}}.\"\n },\n {\n \"key\": \"campaign\",\n \"name\": \"Campaign broadcast\",\n \"description\": \"A campaign sent to the site's subscriber list.\",\n \"pluginId\": \"marketing\",\n \"plugin\": \"Marketing\",\n \"control\": \"external\",\n \"authoredIn\": \"Marketing → Campaigns\",\n \"footerReason\": \"You’re receiving this email from {{host.businessName}}.\"\n },\n {\n \"key\": \"workflow-email\",\n \"name\": \"Workflow email step\",\n \"description\": \"An email sent by a workflow's send-email action when its trigger fires.\",\n \"pluginId\": \"workflows\",\n \"plugin\": \"Workflows\",\n \"control\": \"external\",\n \"authoredIn\": \"the workflow that sends it\",\n \"footerReason\": \"You’re receiving this because you’re in touch with {{host.businessName}}.\"\n },\n {\n \"key\": \"loyalty-points-earned\",\n \"name\": \"Rewards points earned\",\n \"description\": \"Tells a customer the points an order earned them, their balance and their rewards code.\",\n \"pluginId\": \"loyalty\",\n \"plugin\": \"Rewards\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"You earned {{loyalty.points}} points\",\n \"mergeTokens\": [\n {\n \"name\": \"name\",\n \"description\": \"The member's name, when the store knows it\",\n \"sample\": \"Alex\"\n },\n {\n \"name\": \"loyalty.balance\",\n \"description\": \"Points the member holds now\",\n \"sample\": \"1,250\"\n },\n {\n \"name\": \"loyalty.value\",\n \"description\": \"What those points are worth\",\n \"sample\": \"$12.50\"\n },\n {\n \"name\": \"loyalty.credit\",\n \"description\": \"Store credit the member holds\",\n \"sample\": \"$10.00\"\n },\n {\n \"name\": \"loyalty.code\",\n \"description\": \"The member’s rewards code, to spend their balance\",\n \"sample\": \"RW-7K3P-Q9XZ-2M4D\"\n },\n {\n \"name\": \"loyalty.referral\",\n \"description\": \"A sentence with the member’s referral code and what it gives; empty when referrals are off\",\n \"sample\": \"Share your referral code RF-7K3P9X: a friend gets $10.00 off their first order, and you get $10.00 in store credit.\"\n },\n {\n \"name\": \"loyalty.points\",\n \"description\": \"Points this order earned\",\n \"sample\": \"45\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"You earned {{loyalty.points}} points\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Thanks for your order with {{host.businessName}}.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"You have {{loyalty.balance}} points (worth {{loyalty.value}}) and {{loyalty.credit}} in store credit.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Your rewards code is {{loyalty.code}}. Enter it at checkout on {{host.url}}, or give it at the register, to spend your balance.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{loyalty.referral}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"Shop {{host.businessName}}\",\n \"href\": \"{{host.url}}\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you earned rewards with an order from {{host.businessName}}.\"\n },\n {\n \"key\": \"loyalty-store-credit\",\n \"name\": \"Store credit given\",\n \"description\": \"Tells a customer the store gave them store credit, with their rewards code to spend it.\",\n \"pluginId\": \"loyalty\",\n \"plugin\": \"Rewards\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"You have {{loyalty.amount}} in store credit\",\n \"mergeTokens\": [\n {\n \"name\": \"name\",\n \"description\": \"The member's name, when the store knows it\",\n \"sample\": \"Alex\"\n },\n {\n \"name\": \"loyalty.balance\",\n \"description\": \"Points the member holds now\",\n \"sample\": \"1,250\"\n },\n {\n \"name\": \"loyalty.value\",\n \"description\": \"What those points are worth\",\n \"sample\": \"$12.50\"\n },\n {\n \"name\": \"loyalty.credit\",\n \"description\": \"Store credit the member holds\",\n \"sample\": \"$10.00\"\n },\n {\n \"name\": \"loyalty.code\",\n \"description\": \"The member’s rewards code, to spend their balance\",\n \"sample\": \"RW-7K3P-Q9XZ-2M4D\"\n },\n {\n \"name\": \"loyalty.referral\",\n \"description\": \"A sentence with the member’s referral code and what it gives; empty when referrals are off\",\n \"sample\": \"Share your referral code RF-7K3P9X: a friend gets $10.00 off their first order, and you get $10.00 in store credit.\"\n },\n {\n \"name\": \"loyalty.amount\",\n \"description\": \"The credit given\",\n \"sample\": \"$10.00\"\n },\n {\n \"name\": \"loyalty.note\",\n \"description\": \"The note the store wrote; empty when none\",\n \"sample\": \"Sorry about the delay!\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"You have {{loyalty.amount}} in store credit\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{loyalty.note}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"You have {{loyalty.balance}} points (worth {{loyalty.value}}) and {{loyalty.credit}} in store credit.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Your rewards code is {{loyalty.code}}. Enter it at checkout on {{host.url}}, or give it at the register, to spend your balance.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"Shop {{host.businessName}}\",\n \"href\": \"{{host.url}}\"\n }\n ],\n \"footerReason\": \"You’re receiving this because {{host.businessName}} gave you store credit.\"\n },\n {\n \"key\": \"loyalty-referral-reward\",\n \"name\": \"Referral reward\",\n \"description\": \"Tells a customer a friend’s first order earned them store credit.\",\n \"pluginId\": \"loyalty\",\n \"plugin\": \"Rewards\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"A friend’s first order earned you {{loyalty.amount}}\",\n \"mergeTokens\": [\n {\n \"name\": \"name\",\n \"description\": \"The member's name, when the store knows it\",\n \"sample\": \"Alex\"\n },\n {\n \"name\": \"loyalty.balance\",\n \"description\": \"Points the member holds now\",\n \"sample\": \"1,250\"\n },\n {\n \"name\": \"loyalty.value\",\n \"description\": \"What those points are worth\",\n \"sample\": \"$12.50\"\n },\n {\n \"name\": \"loyalty.credit\",\n \"description\": \"Store credit the member holds\",\n \"sample\": \"$10.00\"\n },\n {\n \"name\": \"loyalty.code\",\n \"description\": \"The member’s rewards code, to spend their balance\",\n \"sample\": \"RW-7K3P-Q9XZ-2M4D\"\n },\n {\n \"name\": \"loyalty.referral\",\n \"description\": \"A sentence with the member’s referral code and what it gives; empty when referrals are off\",\n \"sample\": \"Share your referral code RF-7K3P9X: a friend gets $10.00 off their first order, and you get $10.00 in store credit.\"\n },\n {\n \"name\": \"loyalty.amount\",\n \"description\": \"The reward\",\n \"sample\": \"$10.00\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Your referral paid off\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"A friend used your referral code at {{host.businessName}}, so you have {{loyalty.amount}} more in store credit.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"You have {{loyalty.balance}} points (worth {{loyalty.value}}) and {{loyalty.credit}} in store credit.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Your rewards code is {{loyalty.code}}. Enter it at checkout on {{host.url}}, or give it at the register, to spend your balance.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"Shop {{host.businessName}}\",\n \"href\": \"{{host.url}}\"\n }\n ],\n \"footerReason\": \"You’re receiving this because a friend used your referral code at {{host.businessName}}.\"\n }\n]\n"],"names":["PLUGIN_TENANT_EMAILS"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/shared/util/email/src/lib/tenant-emails.generated.ts"],"sourcesContent":["/**\n * GENERATED FILE — do not edit. Regenerate with:\n * node tools/scripts/generate-plugin-manifests.mjs\n *\n * The emails sites send their own customers (AGL-3080): what each plugin's\n * `tenantEmails` entry returned when this file was generated, in config\n * order. `TENANT_EMAILS` in `tenant-email-catalog.ts` is this list.\n * Source of truth: plugins.config.json and the entries it names.\n */\n\nimport type { TenantEmailEntry } from './tenant-email-catalog'\n\nexport const PLUGIN_TENANT_EMAILS: readonly TenantEmailEntry[] = [\n {\n \"key\": \"booking-confirmed\",\n \"name\": \"Booking confirmed\",\n \"description\": \"Confirms a booking to the customer, for paid and free services.\",\n \"pluginId\": \"bookings\",\n \"plugin\": \"Bookings\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Booking confirmed: {{service.name}}\",\n \"mergeTokens\": [\n {\n \"name\": \"name\",\n \"description\": \"The customer's name\",\n \"sample\": \"Alex\"\n },\n {\n \"name\": \"service.name\",\n \"description\": \"The booked service\",\n \"sample\": \"Consultation\"\n },\n {\n \"name\": \"when\",\n \"description\": \"Formatted date and time of the booking\",\n \"sample\": \"Monday, June 1, 2026 at 9:00 AM\"\n },\n {\n \"name\": \"timezone\",\n \"description\": \"Timezone the time is shown in\",\n \"sample\": \"America/Chicago\"\n },\n {\n \"name\": \"booking.payment\",\n \"description\": \"What a paid booking charged, as a sentence; empty for a free booking\",\n \"sample\": \"You paid $95.00.\"\n },\n {\n \"name\": \"booking.ref\",\n \"description\": \"Booking reference id\",\n \"sample\": \"bk_123\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Booking confirmed\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Hi {{name}}, your booking with {{host.businessName}} is confirmed: \\\"{{service.name}}\\\" on {{when}} ({{timezone}}).\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{booking.payment}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Reference: {{booking.ref}}\",\n \"variant\": \"caption\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you booked with {{host.businessName}}.\"\n },\n {\n \"key\": \"booking-reminder\",\n \"name\": \"Booking reminder\",\n \"description\": \"Reminds the customer of an upcoming booking.\",\n \"pluginId\": \"bookings\",\n \"plugin\": \"Bookings\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Reminder: {{service.name}} is coming up\",\n \"mergeTokens\": [\n {\n \"name\": \"name\",\n \"description\": \"The customer's name\",\n \"sample\": \"Alex\"\n },\n {\n \"name\": \"service.name\",\n \"description\": \"The booked service\",\n \"sample\": \"Consultation\"\n },\n {\n \"name\": \"when\",\n \"description\": \"Formatted date and time of the booking\",\n \"sample\": \"Tuesday, June 2, 2026 at 9:00 AM\"\n },\n {\n \"name\": \"timezone\",\n \"description\": \"Timezone the time is shown in\",\n \"sample\": \"America/Chicago\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Hi {{name}}, this is a reminder of your booking with {{host.businessName}}: \\\"{{service.name}}\\\" on {{when}} ({{timezone}}).\",\n \"variant\": \"body\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you have a booking with {{host.businessName}}.\"\n },\n {\n \"key\": \"booking-rescheduled\",\n \"name\": \"Booking rescheduled\",\n \"description\": \"Tells the customer their booking moved to a new time.\",\n \"pluginId\": \"bookings\",\n \"plugin\": \"Bookings\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"New time for {{service.name}}\",\n \"mergeTokens\": [\n {\n \"name\": \"name\",\n \"description\": \"The customer's name\",\n \"sample\": \"Alex\"\n },\n {\n \"name\": \"service.name\",\n \"description\": \"The booked service\",\n \"sample\": \"Consultation\"\n },\n {\n \"name\": \"when\",\n \"description\": \"The new date and time of the booking\",\n \"sample\": \"Wednesday, June 3, 2026 at 10:30 AM\"\n },\n {\n \"name\": \"previousWhen\",\n \"description\": \"The date and time the booking moved from\",\n \"sample\": \"Monday, June 1, 2026 at 9:00 AM\"\n },\n {\n \"name\": \"timezone\",\n \"description\": \"Timezone the times are shown in\",\n \"sample\": \"America/Chicago\"\n },\n {\n \"name\": \"booking.ref\",\n \"description\": \"Booking reference id\",\n \"sample\": \"bk_123\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Your booking has a new time\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Hi {{name}}, your booking with {{host.businessName}} for \\\"{{service.name}}\\\" is now on {{when}} ({{timezone}}). It was on {{previousWhen}}.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Reference: {{booking.ref}}\",\n \"variant\": \"caption\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you have a booking with {{host.businessName}}.\"\n },\n {\n \"key\": \"order-receipt\",\n \"name\": \"Order receipt\",\n \"description\": \"Sent to the buyer after a successful order or checkout.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Receipt for your order\",\n \"mergeTokens\": [\n {\n \"name\": \"order.summary\",\n \"description\": \"The ordered items, one per line (with any license/download links)\",\n \"sample\": \"House Blend × 2 — $24.00\"\n },\n {\n \"name\": \"order.total\",\n \"description\": \"Order total\",\n \"sample\": \"$24.00\"\n },\n {\n \"name\": \"order.ref\",\n \"description\": \"Order reference id\",\n \"sample\": \"cs_test_123\"\n },\n {\n \"name\": \"store.receiptFooter\",\n \"description\": \"The Receipt footer from the store settings; empty when none is set\",\n \"sample\": \"Returns are accepted within 30 days.\"\n },\n {\n \"name\": \"order.number\",\n \"description\": \"The order number, as the store shows it\",\n \"sample\": \"#1042\"\n },\n {\n \"name\": \"order.statusUrl\",\n \"description\": \"Link to the order status page: status, shipments and tracking. Private to the buyer; it needs no account\",\n \"sample\": \"https://shop.example.com/order-status?o=ord_123&t=…\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Thanks for your purchase!\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Here is the receipt for your order from {{host.businessName}}.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{order.summary}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Total charged: {{order.total}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{store.receiptFooter}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"View your order\",\n \"href\": \"{{order.statusUrl}}\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Order reference: {{order.ref}}\",\n \"variant\": \"caption\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you placed an order with {{host.businessName}}.\"\n },\n {\n \"key\": \"order-shipped\",\n \"name\": \"Order shipped\",\n \"description\": \"Tells the buyer part or all of their order is on its way, with the carrier and a tracking link when the shipment has one. Sent once per shipment.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Your order {{order.number}} has shipped\",\n \"mergeTokens\": [\n {\n \"name\": \"order.number\",\n \"description\": \"The order number, as the store shows it\",\n \"sample\": \"#1042\"\n },\n {\n \"name\": \"shipment.summary\",\n \"description\": \"The items in this shipment, one per line\",\n \"sample\": \"2× House Blend\"\n },\n {\n \"name\": \"shipment.carrier\",\n \"description\": \"The carrier; empty when none was recorded\",\n \"sample\": \"UPS\"\n },\n {\n \"name\": \"shipment.trackingNumber\",\n \"description\": \"The tracking number; empty when none was recorded\",\n \"sample\": \"1Z999AA10123456784\"\n },\n {\n \"name\": \"shipment.tracking\",\n \"description\": \"A sentence with the carrier, tracking number and tracking link; empty when the shipment has no tracking number\",\n \"sample\": \"Tracking: UPS 1Z999AA10123456784 — https://www.ups.com/track?tracknum=1Z999AA10123456784\"\n },\n {\n \"name\": \"shipment.remaining\",\n \"description\": \"A sentence saying how many items are still to ship; empty when this shipment completes the order\",\n \"sample\": \"1 more item will ship separately.\"\n },\n {\n \"name\": \"order.statusUrl\",\n \"description\": \"Link to the order status page: status, shipments and tracking. Private to the buyer; it needs no account\",\n \"sample\": \"https://shop.example.com/order-status?o=ord_123&t=…\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Your order is on its way\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{host.businessName}} shipped items from order {{order.number}}.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{shipment.summary}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{shipment.tracking}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{shipment.remaining}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"Track your order\",\n \"href\": \"{{order.statusUrl}}\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you placed an order with {{host.businessName}}.\"\n },\n {\n \"key\": \"order-delivered\",\n \"name\": \"Order delivered\",\n \"description\": \"Tells the buyer their order was marked delivered.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Your order {{order.number}} was delivered\",\n \"mergeTokens\": [\n {\n \"name\": \"order.number\",\n \"description\": \"The order number, as the store shows it\",\n \"sample\": \"#1042\"\n },\n {\n \"name\": \"order.summary\",\n \"description\": \"The ordered items, one per line\",\n \"sample\": \"2× House Blend\"\n },\n {\n \"name\": \"order.statusUrl\",\n \"description\": \"Link to the order status page: status, shipments and tracking. Private to the buyer; it needs no account\",\n \"sample\": \"https://shop.example.com/order-status?o=ord_123&t=…\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Your order was delivered\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{host.businessName}} marked order {{order.number}} as delivered.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{order.summary}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"View your order\",\n \"href\": \"{{order.statusUrl}}\"\n },\n {\n \"block\": \"text\",\n \"text\": \"If something is missing or not right, reply to this email or contact {{host.businessName}}.\",\n \"variant\": \"caption\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you placed an order with {{host.businessName}}.\"\n },\n {\n \"key\": \"order-refunded\",\n \"name\": \"Order refunded\",\n \"description\": \"Tells the buyer a refund was issued, for how much and, when the store refunded named items, which ones. Sent once per refund.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Refund for your order {{order.number}}\",\n \"mergeTokens\": [\n {\n \"name\": \"order.number\",\n \"description\": \"The order number, as the store shows it\",\n \"sample\": \"#1042\"\n },\n {\n \"name\": \"refund.amount\",\n \"description\": \"The amount of this refund\",\n \"sample\": \"$12.00\"\n },\n {\n \"name\": \"refund.summary\",\n \"description\": \"The refunded items, one per line; empty when the refund was by amount only\",\n \"sample\": \"1× House Blend\"\n },\n {\n \"name\": \"refund.note\",\n \"description\": \"Whether the order is now fully refunded, as a sentence\",\n \"sample\": \"Your order has been refunded in full.\"\n },\n {\n \"name\": \"order.statusUrl\",\n \"description\": \"Link to the order status page: status, shipments and tracking. Private to the buyer; it needs no account\",\n \"sample\": \"https://shop.example.com/order-status?o=ord_123&t=…\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Your refund is on its way\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{host.businessName}} refunded {{refund.amount}} on order {{order.number}}.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{refund.summary}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{refund.note}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Refunds go back to the original payment method and usually appear within 5 to 10 business days, depending on your bank.\",\n \"variant\": \"caption\"\n },\n {\n \"block\": \"button\",\n \"label\": \"View your order\",\n \"href\": \"{{order.statusUrl}}\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you placed an order with {{host.businessName}}.\"\n },\n {\n \"key\": \"order-cancelled\",\n \"name\": \"Order canceled\",\n \"description\": \"Tells the buyer their order was canceled.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Your order {{order.number}} was canceled\",\n \"mergeTokens\": [\n {\n \"name\": \"order.number\",\n \"description\": \"The order number, as the store shows it\",\n \"sample\": \"#1042\"\n },\n {\n \"name\": \"order.summary\",\n \"description\": \"The ordered items, one per line\",\n \"sample\": \"2× House Blend\"\n },\n {\n \"name\": \"cancel.note\",\n \"description\": \"What happens to the payment, as a sentence; empty when nothing was paid\",\n \"sample\": \"Any payment you made will be refunded, and you will get a separate email when it is.\"\n },\n {\n \"name\": \"order.statusUrl\",\n \"description\": \"Link to the order status page: status, shipments and tracking. Private to the buyer; it needs no account\",\n \"sample\": \"https://shop.example.com/order-status?o=ord_123&t=…\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Your order was canceled\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{host.businessName}} canceled order {{order.number}}.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{order.summary}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{cancel.note}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"View your order\",\n \"href\": \"{{order.statusUrl}}\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you placed an order with {{host.businessName}}.\"\n },\n {\n \"key\": \"order-ready-for-pickup\",\n \"name\": \"Order ready for pickup\",\n \"description\": \"Tells the buyer their pickup order is ready, where to collect it, the pickup hours and any arrival instructions.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Your order {{order.number}} is ready for pickup\",\n \"mergeTokens\": [\n {\n \"name\": \"order.number\",\n \"description\": \"The order number, as the store shows it\",\n \"sample\": \"#1042\"\n },\n {\n \"name\": \"order.summary\",\n \"description\": \"The ordered items, one per line\",\n \"sample\": \"2× House Blend\"\n },\n {\n \"name\": \"pickup.location\",\n \"description\": \"The pickup location’s name and address\",\n \"sample\": \"Main Street shop, 12 Main St, Springfield\"\n },\n {\n \"name\": \"pickup.hours\",\n \"description\": \"The location’s pickup hours; empty when it lists none\",\n \"sample\": \"Pickup hours:\\nMo-Fr 09:00-17:00\"\n },\n {\n \"name\": \"pickup.instructions\",\n \"description\": \"What to do on arrival, as the store wrote it; empty when it wrote none\",\n \"sample\": \"Ring the bell at the side door.\"\n },\n {\n \"name\": \"order.statusUrl\",\n \"description\": \"Link to the order status page: status, shipments and tracking. Private to the buyer; it needs no account\",\n \"sample\": \"https://shop.example.com/order-status?o=ord_123&t=…\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Your order is ready for pickup\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Order {{order.number}} is ready at {{pickup.location}}.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{order.summary}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{pickup.hours}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{pickup.instructions}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Bring your order number, {{order.number}}.\",\n \"variant\": \"caption\"\n },\n {\n \"block\": \"button\",\n \"label\": \"View your order\",\n \"href\": \"{{order.statusUrl}}\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you placed an order with {{host.businessName}}.\"\n },\n {\n \"key\": \"order-picked-up\",\n \"name\": \"Order picked up\",\n \"description\": \"Confirms to the buyer that their pickup order was collected.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"You picked up order {{order.number}}\",\n \"mergeTokens\": [\n {\n \"name\": \"order.number\",\n \"description\": \"The order number, as the store shows it\",\n \"sample\": \"#1042\"\n },\n {\n \"name\": \"order.summary\",\n \"description\": \"The ordered items, one per line\",\n \"sample\": \"2× House Blend\"\n },\n {\n \"name\": \"pickup.location\",\n \"description\": \"The pickup location’s name and address\",\n \"sample\": \"Main Street shop, 12 Main St, Springfield\"\n },\n {\n \"name\": \"order.statusUrl\",\n \"description\": \"Link to the order status page: status, shipments and tracking. Private to the buyer; it needs no account\",\n \"sample\": \"https://shop.example.com/order-status?o=ord_123&t=…\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Thanks for picking up your order\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Order {{order.number}} was picked up from {{pickup.location}}.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{order.summary}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"If something is missing or not right, reply to this email or contact {{host.businessName}}.\",\n \"variant\": \"caption\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you placed an order with {{host.businessName}}.\"\n },\n {\n \"key\": \"order-out-for-delivery\",\n \"name\": \"Order out for delivery\",\n \"description\": \"Tells the buyer the store’s own driver is on the way with their local delivery, in the window they booked.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Your order {{order.number}} is out for delivery\",\n \"mergeTokens\": [\n {\n \"name\": \"order.number\",\n \"description\": \"The order number, as the store shows it\",\n \"sample\": \"#1042\"\n },\n {\n \"name\": \"order.summary\",\n \"description\": \"The ordered items, one per line\",\n \"sample\": \"2× House Blend\"\n },\n {\n \"name\": \"delivery.window\",\n \"description\": \"The delivery window the buyer booked\",\n \"sample\": \"Tue, Oct 14, 9:00 AM – 12:00 PM\"\n },\n {\n \"name\": \"order.statusUrl\",\n \"description\": \"Link to the order status page: status, shipments and tracking. Private to the buyer; it needs no account\",\n \"sample\": \"https://shop.example.com/order-status?o=ord_123&t=…\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Your order is on its way\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{host.businessName}} is out delivering order {{order.number}}. Your window: {{delivery.window}}.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{order.summary}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"View your order\",\n \"href\": \"{{order.statusUrl}}\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you placed an order with {{host.businessName}}.\"\n },\n {\n \"key\": \"sale-notification\",\n \"name\": \"New sale\",\n \"description\": \"Notifies the seller when an order is placed.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"New order on {{site.name}}\",\n \"mergeTokens\": [\n {\n \"name\": \"site.name\",\n \"description\": \"The site the sale happened on\",\n \"sample\": \"Northwind Coffee\"\n },\n {\n \"name\": \"order.summary\",\n \"description\": \"The ordered items\",\n \"sample\": \"House Blend — $12.00\"\n },\n {\n \"name\": \"order.total\",\n \"description\": \"Order total\",\n \"sample\": \"$12.00\"\n },\n {\n \"name\": \"buyer.email\",\n \"description\": \"The buyer's email\",\n \"sample\": \"buyer@example.com\"\n },\n {\n \"name\": \"order.ref\",\n \"description\": \"Order reference id\",\n \"sample\": \"cs_test_123\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"You made a sale\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"A new order came in on {{site.name}}.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{order.summary}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Total: {{order.total}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Buyer: {{buyer.email}} · Order {{order.ref}}\",\n \"variant\": \"caption\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you manage {{host.businessName}}.\"\n },\n {\n \"key\": \"reservation-confirmed\",\n \"name\": \"Reservation confirmed\",\n \"description\": \"Confirms a paid reservation to the customer.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Reservation confirmed\",\n \"mergeTokens\": [\n {\n \"name\": \"reservation.checkIn\",\n \"description\": \"Check-in date\",\n \"sample\": \"Mon, 01 Jun 2026\"\n },\n {\n \"name\": \"reservation.nights\",\n \"description\": \"Number of nights\",\n \"sample\": \"2\"\n },\n {\n \"name\": \"reservation.paid\",\n \"description\": \"What the guest was charged today, lodging tax included; a charge that carried lodging tax names it, as in “$254.40, including $14.40 lodging tax”\",\n \"sample\": \"$240.00\"\n },\n {\n \"name\": \"reservation.balance\",\n \"description\": \"What is still owed for the stay and where it is paid, as a sentence; empty when the stay is paid in full\",\n \"sample\": \"Still to pay: $360.00, at the property. It has not been charged.\"\n },\n {\n \"name\": \"reservation.ref\",\n \"description\": \"Reservation reference id\",\n \"sample\": \"resv_123\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Reservation confirmed\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Your stay at {{host.businessName}} is confirmed.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Check-in: {{reservation.checkIn}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Nights: {{reservation.nights}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Paid today: {{reservation.paid}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{reservation.balance}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Reference: {{reservation.ref}}\",\n \"variant\": \"caption\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you made a reservation with {{host.businessName}}.\"\n },\n {\n \"key\": \"gift-card\",\n \"name\": \"Gift card delivery\",\n \"description\": \"Delivers a purchased gift card to its recipient.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Your gift card for {{host.businessName}}\",\n \"mergeTokens\": [\n {\n \"name\": \"giftcard.code\",\n \"description\": \"The gift card code\",\n \"sample\": \"GC-ABC123DEF456\"\n },\n {\n \"name\": \"giftcard.value\",\n \"description\": \"The gift card value\",\n \"sample\": \"$25.00\"\n },\n {\n \"name\": \"giftcard.note\",\n \"description\": \"The note written when the card was issued by hand; empty for a card bought at checkout\",\n \"sample\": \"Happy birthday, Sam!\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Your gift card\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"You have a {{giftcard.value}} gift card for {{host.businessName}}.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{giftcard.note}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Gift card code: {{giftcard.code}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Enter the code at checkout on {{host.url}} to use its balance.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"Visit {{host.businessName}}\",\n \"href\": \"{{host.url}}\"\n }\n ],\n \"footerReason\": \"You’re receiving this because a gift card from {{host.businessName}} was sent to this address.\"\n },\n {\n \"key\": \"supplier-fulfillment\",\n \"name\": \"Supplier fulfillment\",\n \"description\": \"Tells a dropship supplier there is a new order to fulfill.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"fixed\"\n },\n {\n \"key\": \"back-in-stock\",\n \"name\": \"Back in stock\",\n \"description\": \"Tells a customer a product they watched is available again.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Back in stock: {{product.name}}\",\n \"mergeTokens\": [\n {\n \"name\": \"product.name\",\n \"description\": \"The product that restocked\",\n \"sample\": \"House Blend\"\n },\n {\n \"name\": \"product.url\",\n \"description\": \"Link to the product page\",\n \"sample\": \"https://shop.example.com/products/house-blend\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"{{product.name}} is back in stock at {{host.businessName}}.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"View product\",\n \"href\": \"{{product.url}}\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you asked {{host.businessName}} to tell you when this was back.\"\n },\n {\n \"key\": \"abandoned-cart\",\n \"name\": \"Abandoned cart\",\n \"description\": \"Reminds a shopper of items left in their cart.\",\n \"requiresFeature\": \"abandonedCart\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"You left something in your cart\",\n \"mergeTokens\": [\n {\n \"name\": \"cart.url\",\n \"description\": \"Link back to the shopper cart\",\n \"sample\": \"https://shop.example.com/cart\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"You left items in your cart at {{host.businessName}}. Pick up where you left off.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"Return to cart\",\n \"href\": \"{{cart.url}}\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Nothing in your cart is held for you.\",\n \"variant\": \"caption\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you started a checkout with {{host.businessName}}.\"\n },\n {\n \"key\": \"review-request\",\n \"name\": \"Review request\",\n \"description\": \"Asks a buyer to review what they bought, a set number of days after the order is fulfilled. One per order; each product has its own single-use link. Turn it off or change the delay in Settings → Reviews.\",\n \"requiresFeature\": \"productReviews\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"How was your order from {{host.businessName}}?\",\n \"mergeTokens\": [\n {\n \"name\": \"order.number\",\n \"description\": \"The order number, as the store shows it\",\n \"sample\": \"#1042\"\n },\n {\n \"name\": \"review.products\",\n \"description\": \"Each product to review, one per line, with its own review link\",\n \"sample\": \"House Blend: https://shop.example.com/products/house-blend?review=…\"\n },\n {\n \"name\": \"review.url\",\n \"description\": \"The review link for the first product\",\n \"sample\": \"https://shop.example.com/products/house-blend?review=…\"\n },\n {\n \"name\": \"review.productName\",\n \"description\": \"The first product’s name\",\n \"sample\": \"House Blend\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"How did we do?\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Thanks again for your order {{order.number}} from {{host.businessName}}. Would you tell other shoppers what you think? Each link opens the product with a review form.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{review.products}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"Write a review\",\n \"href\": \"{{review.url}}\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Each link works once, for 60 days.\",\n \"variant\": \"caption\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you placed an order with {{host.businessName}}.\"\n },\n {\n \"key\": \"member-post\",\n \"name\": \"New member post\",\n \"description\": \"Notifies members when new members-only content is posted.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"{{post.title}}\",\n \"mergeTokens\": [\n {\n \"name\": \"post.title\",\n \"description\": \"The post’s title\",\n \"sample\": \"This month in the studio\"\n },\n {\n \"name\": \"post.body\",\n \"description\": \"The post, as its author wrote it\",\n \"sample\": \"Three new pieces are up for members first.\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"{{post.title}}\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{post.body}}\",\n \"variant\": \"body\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you’re a member of {{host.businessName}}.\"\n },\n {\n \"key\": \"member-password-reset\",\n \"name\": \"Member password reset\",\n \"description\": \"Resets a site member's password — a store-member account, separate from a console login. Sent when the member asks, and when a site administrator starts a reset for them.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Reset your {{site.name}} password\",\n \"mergeTokens\": [\n {\n \"name\": \"site.name\",\n \"description\": \"The site’s name\",\n \"sample\": \"Northwind Coffee\"\n },\n {\n \"name\": \"reset.intro\",\n \"description\": \"Who started the reset\",\n \"sample\": \"Someone asked to reset the password for your Northwind Coffee account.\"\n },\n {\n \"name\": \"resetUrl\",\n \"description\": \"The one-time reset link\",\n \"sample\": \"https://shop.example.com/recover?token=…\"\n },\n {\n \"name\": \"reset.note\",\n \"description\": \"How long the link lasts, and what to do if unexpected\",\n \"sample\": \"The link works once and expires in 1 hour. If you did not ask for this, you can safely ignore this email — your password is unchanged.\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Reset your password\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{reset.intro}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"Set a new password\",\n \"href\": \"{{resetUrl}}\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{reset.note}}\",\n \"variant\": \"caption\"\n }\n ],\n \"footerReason\": \"You’re receiving this because a password reset was started for your {{host.businessName}} account.\"\n },\n {\n \"key\": \"member-password-changed\",\n \"name\": \"Member password changed\",\n \"description\": \"Tells a site member that a site administrator set a new password on their account.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Your {{site.name}} password was changed\",\n \"mergeTokens\": [\n {\n \"name\": \"site.name\",\n \"description\": \"The site’s name\",\n \"sample\": \"Northwind Coffee\"\n },\n {\n \"name\": \"signInUrl\",\n \"description\": \"Where the member signs back in\",\n \"sample\": \"https://shop.example.com\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Your password was changed\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"An administrator of {{site.name}} set a new password on your account and signed you out on every device. To sign back in, get the new password from them, or choose \\\"Forgot password?\\\" on the sign-in page to set your own.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"Sign in\",\n \"href\": \"{{signInUrl}}\"\n },\n {\n \"block\": \"text\",\n \"text\": \"If you did not expect this, contact the site owner.\",\n \"variant\": \"caption\"\n }\n ],\n \"footerReason\": \"You’re receiving this because the password on your {{host.businessName}} account was changed.\"\n },\n {\n \"key\": \"newsletter-confirmation\",\n \"name\": \"Subscription confirmation\",\n \"description\": \"Asks someone who signed up for a site’s emails to confirm the address before anything is sent to it.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Confirm your subscription\",\n \"mergeTokens\": [\n {\n \"name\": \"stream.name\",\n \"description\": \"What they signed up for\",\n \"sample\": \"Newsletter\"\n },\n {\n \"name\": \"confirmUrl\",\n \"description\": \"The confirmation link\",\n \"sample\": \"https://shop.example.com/subscribe/confirm?token=…\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Confirm your subscription\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Please confirm that you want to get {{stream.name}} emails from {{host.businessName}} at this address.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"Confirm my subscription\",\n \"href\": \"{{confirmUrl}}\"\n },\n {\n \"block\": \"text\",\n \"text\": \"The link works for three days. If you did not sign up, ignore this message — nothing will be sent.\",\n \"variant\": \"caption\"\n }\n ],\n \"footerReason\": \"You’re receiving this because this address was entered on {{host.businessName}}’s signup form.\"\n },\n {\n \"key\": \"return-requested\",\n \"name\": \"Return requested\",\n \"description\": \"Tells the store a buyer asked to return items, with what and why, so someone can approve or decline it.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Return requested for order {{order.number}}\",\n \"mergeTokens\": [\n {\n \"name\": \"order.number\",\n \"description\": \"The order number, as the store shows it\",\n \"sample\": \"#1042\"\n },\n {\n \"name\": \"return.items\",\n \"description\": \"The items being returned, one per line, with the reason\",\n \"sample\": \"1× House Blend — Arrived damaged\"\n },\n {\n \"name\": \"buyer.email\",\n \"description\": \"The buyer’s email\",\n \"sample\": \"buyer@example.com\"\n },\n {\n \"name\": \"return.customerNote\",\n \"description\": \"What the buyer wrote; empty when they wrote nothing\",\n \"sample\": \"The lid was cracked.\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"A buyer asked for a return\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{buyer.email}} wants to return items from order {{order.number}}.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{return.items}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{return.customerNote}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Approve or decline it under Products → Returns.\",\n \"variant\": \"caption\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you manage {{host.businessName}}.\"\n },\n {\n \"key\": \"return-approved\",\n \"name\": \"Return approved\",\n \"description\": \"Tells the buyer their return was approved and how to send the items back, with the return label when the store attached one.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Your return for order {{order.number}} is approved\",\n \"mergeTokens\": [\n {\n \"name\": \"order.number\",\n \"description\": \"The order number, as the store shows it\",\n \"sample\": \"#1042\"\n },\n {\n \"name\": \"return.items\",\n \"description\": \"The items being returned, one per line, with the reason\",\n \"sample\": \"1× House Blend — Arrived damaged\"\n },\n {\n \"name\": \"return.note\",\n \"description\": \"The store’s note to the buyer on this return; empty when none\",\n \"sample\": \"Please include the original packaging.\"\n },\n {\n \"name\": \"return.label\",\n \"description\": \"A sentence with the return label link and its tracking; empty when the store attached no label\",\n \"sample\": \"Print your return label: https://labels.example.com/r/123\"\n },\n {\n \"name\": \"order.statusUrl\",\n \"description\": \"Link to the order status page: status, shipments and tracking. Private to the buyer; it needs no account\",\n \"sample\": \"https://shop.example.com/order-status?o=ord_123&t=…\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Your return is approved\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{host.businessName}} approved the return of:\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{return.items}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{return.note}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{return.label}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"View your order\",\n \"href\": \"{{order.statusUrl}}\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you placed an order with {{host.businessName}}.\"\n },\n {\n \"key\": \"return-declined\",\n \"name\": \"Return declined\",\n \"description\": \"Tells the buyer their return was declined, with the store’s note.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"About your return for order {{order.number}}\",\n \"mergeTokens\": [\n {\n \"name\": \"order.number\",\n \"description\": \"The order number, as the store shows it\",\n \"sample\": \"#1042\"\n },\n {\n \"name\": \"return.items\",\n \"description\": \"The items being returned, one per line, with the reason\",\n \"sample\": \"1× House Blend — Arrived damaged\"\n },\n {\n \"name\": \"return.note\",\n \"description\": \"The store’s note to the buyer on this return; empty when none\",\n \"sample\": \"Please include the original packaging.\"\n },\n {\n \"name\": \"order.statusUrl\",\n \"description\": \"Link to the order status page: status, shipments and tracking. Private to the buyer; it needs no account\",\n \"sample\": \"https://shop.example.com/order-status?o=ord_123&t=…\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"We could not accept this return\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{host.businessName}} declined the return of:\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{return.items}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{return.note}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"View your order\",\n \"href\": \"{{order.statusUrl}}\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you placed an order with {{host.businessName}}.\"\n },\n {\n \"key\": \"return-refunded\",\n \"name\": \"Return refunded\",\n \"description\": \"Tells the buyer their return was received and refunded, and how much.\",\n \"pluginId\": \"commerce\",\n \"plugin\": \"Commerce\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"Your refund for order {{order.number}}\",\n \"mergeTokens\": [\n {\n \"name\": \"order.number\",\n \"description\": \"The order number, as the store shows it\",\n \"sample\": \"#1042\"\n },\n {\n \"name\": \"return.items\",\n \"description\": \"The items being returned, one per line, with the reason\",\n \"sample\": \"1× House Blend — Arrived damaged\"\n },\n {\n \"name\": \"return.refundTotal\",\n \"description\": \"The amount refunded for this return\",\n \"sample\": \"$24.00\"\n },\n {\n \"name\": \"order.statusUrl\",\n \"description\": \"Link to the order status page: status, shipments and tracking. Private to the buyer; it needs no account\",\n \"sample\": \"https://shop.example.com/order-status?o=ord_123&t=…\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Your return is refunded\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{host.businessName}} received your return and refunded {{return.refundTotal}} to your original payment method.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{return.items}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"View your order\",\n \"href\": \"{{order.statusUrl}}\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you placed an order with {{host.businessName}}.\"\n },\n {\n \"key\": \"campaign\",\n \"name\": \"Campaign broadcast\",\n \"description\": \"A campaign sent to the site's subscriber list.\",\n \"pluginId\": \"marketing\",\n \"plugin\": \"Marketing\",\n \"control\": \"external\",\n \"authoredIn\": \"Marketing → Campaigns\",\n \"footerReason\": \"You’re receiving this email from {{host.businessName}}.\"\n },\n {\n \"key\": \"workflow-email\",\n \"name\": \"Workflow email step\",\n \"description\": \"An email sent by a workflow's send-email action when its trigger fires.\",\n \"pluginId\": \"workflows\",\n \"plugin\": \"Workflows\",\n \"control\": \"external\",\n \"authoredIn\": \"the workflow that sends it\",\n \"footerReason\": \"You’re receiving this because you’re in touch with {{host.businessName}}.\"\n },\n {\n \"key\": \"loyalty-points-earned\",\n \"name\": \"Rewards points earned\",\n \"description\": \"Tells a customer the points an order earned them, their balance and their rewards code.\",\n \"pluginId\": \"loyalty\",\n \"plugin\": \"Rewards\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"You earned {{loyalty.points}} points\",\n \"mergeTokens\": [\n {\n \"name\": \"name\",\n \"description\": \"The member's name, when the store knows it\",\n \"sample\": \"Alex\"\n },\n {\n \"name\": \"loyalty.balance\",\n \"description\": \"Points the member holds now\",\n \"sample\": \"1,250\"\n },\n {\n \"name\": \"loyalty.value\",\n \"description\": \"What those points are worth\",\n \"sample\": \"$12.50\"\n },\n {\n \"name\": \"loyalty.credit\",\n \"description\": \"Store credit the member holds\",\n \"sample\": \"$10.00\"\n },\n {\n \"name\": \"loyalty.code\",\n \"description\": \"The member’s rewards code, to spend their balance\",\n \"sample\": \"RW-7K3P-Q9XZ-2M4D\"\n },\n {\n \"name\": \"loyalty.referral\",\n \"description\": \"A sentence with the member’s referral code and what it gives; empty when referrals are off\",\n \"sample\": \"Share your referral code RF-7K3P9X: a friend gets $10.00 off their first order, and you get $10.00 in store credit.\"\n },\n {\n \"name\": \"loyalty.points\",\n \"description\": \"Points this order earned\",\n \"sample\": \"45\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"You earned {{loyalty.points}} points\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Thanks for your order with {{host.businessName}}.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"You have {{loyalty.balance}} points (worth {{loyalty.value}}) and {{loyalty.credit}} in store credit.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Your rewards code is {{loyalty.code}}. Enter it at checkout on {{host.url}}, or give it at the register, to spend your balance.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{loyalty.referral}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"Shop {{host.businessName}}\",\n \"href\": \"{{host.url}}\"\n }\n ],\n \"footerReason\": \"You’re receiving this because you earned rewards with an order from {{host.businessName}}.\"\n },\n {\n \"key\": \"loyalty-store-credit\",\n \"name\": \"Store credit given\",\n \"description\": \"Tells a customer the store gave them store credit, with their rewards code to spend it.\",\n \"pluginId\": \"loyalty\",\n \"plugin\": \"Rewards\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"You have {{loyalty.amount}} in store credit\",\n \"mergeTokens\": [\n {\n \"name\": \"name\",\n \"description\": \"The member's name, when the store knows it\",\n \"sample\": \"Alex\"\n },\n {\n \"name\": \"loyalty.balance\",\n \"description\": \"Points the member holds now\",\n \"sample\": \"1,250\"\n },\n {\n \"name\": \"loyalty.value\",\n \"description\": \"What those points are worth\",\n \"sample\": \"$12.50\"\n },\n {\n \"name\": \"loyalty.credit\",\n \"description\": \"Store credit the member holds\",\n \"sample\": \"$10.00\"\n },\n {\n \"name\": \"loyalty.code\",\n \"description\": \"The member’s rewards code, to spend their balance\",\n \"sample\": \"RW-7K3P-Q9XZ-2M4D\"\n },\n {\n \"name\": \"loyalty.referral\",\n \"description\": \"A sentence with the member’s referral code and what it gives; empty when referrals are off\",\n \"sample\": \"Share your referral code RF-7K3P9X: a friend gets $10.00 off their first order, and you get $10.00 in store credit.\"\n },\n {\n \"name\": \"loyalty.amount\",\n \"description\": \"The credit given\",\n \"sample\": \"$10.00\"\n },\n {\n \"name\": \"loyalty.note\",\n \"description\": \"The note the store wrote; empty when none\",\n \"sample\": \"Sorry about the delay!\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"You have {{loyalty.amount}} in store credit\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"{{loyalty.note}}\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"You have {{loyalty.balance}} points (worth {{loyalty.value}}) and {{loyalty.credit}} in store credit.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Your rewards code is {{loyalty.code}}. Enter it at checkout on {{host.url}}, or give it at the register, to spend your balance.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"Shop {{host.businessName}}\",\n \"href\": \"{{host.url}}\"\n }\n ],\n \"footerReason\": \"You’re receiving this because {{host.businessName}} gave you store credit.\"\n },\n {\n \"key\": \"loyalty-referral-reward\",\n \"name\": \"Referral reward\",\n \"description\": \"Tells a customer a friend’s first order earned them store credit.\",\n \"pluginId\": \"loyalty\",\n \"plugin\": \"Rewards\",\n \"control\": \"besigner\",\n \"defaultSubject\": \"A friend’s first order earned you {{loyalty.amount}}\",\n \"mergeTokens\": [\n {\n \"name\": \"name\",\n \"description\": \"The member's name, when the store knows it\",\n \"sample\": \"Alex\"\n },\n {\n \"name\": \"loyalty.balance\",\n \"description\": \"Points the member holds now\",\n \"sample\": \"1,250\"\n },\n {\n \"name\": \"loyalty.value\",\n \"description\": \"What those points are worth\",\n \"sample\": \"$12.50\"\n },\n {\n \"name\": \"loyalty.credit\",\n \"description\": \"Store credit the member holds\",\n \"sample\": \"$10.00\"\n },\n {\n \"name\": \"loyalty.code\",\n \"description\": \"The member’s rewards code, to spend their balance\",\n \"sample\": \"RW-7K3P-Q9XZ-2M4D\"\n },\n {\n \"name\": \"loyalty.referral\",\n \"description\": \"A sentence with the member’s referral code and what it gives; empty when referrals are off\",\n \"sample\": \"Share your referral code RF-7K3P9X: a friend gets $10.00 off their first order, and you get $10.00 in store credit.\"\n },\n {\n \"name\": \"loyalty.amount\",\n \"description\": \"The reward\",\n \"sample\": \"$10.00\"\n }\n ],\n \"defaultBody\": [\n {\n \"block\": \"text\",\n \"text\": \"Your referral paid off\",\n \"variant\": \"heading\"\n },\n {\n \"block\": \"text\",\n \"text\": \"A friend used your referral code at {{host.businessName}}, so you have {{loyalty.amount}} more in store credit.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"You have {{loyalty.balance}} points (worth {{loyalty.value}}) and {{loyalty.credit}} in store credit.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"text\",\n \"text\": \"Your rewards code is {{loyalty.code}}. Enter it at checkout on {{host.url}}, or give it at the register, to spend your balance.\",\n \"variant\": \"body\"\n },\n {\n \"block\": \"button\",\n \"label\": \"Shop {{host.businessName}}\",\n \"href\": \"{{host.url}}\"\n }\n ],\n \"footerReason\": \"You’re receiving this because a friend used your referral code at {{host.businessName}}.\"\n }\n]\n"],"names":["PLUGIN_TENANT_EMAILS"],"mappings":"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"}