@aglyn/aglyn 1.0.0-beta.203 → 1.0.0-beta.205

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/package.json +11 -11
  2. package/src/lib/app-utils/activity-presenter.d.ts +21 -0
  3. package/src/lib/app-utils/activity-presenter.js +22 -0
  4. package/src/lib/app-utils/activity-presenter.js.map +1 -1
  5. package/src/lib/app-utils/console-routes.d.ts +4 -0
  6. package/src/lib/app-utils/console-routes.js +4 -0
  7. package/src/lib/app-utils/console-routes.js.map +1 -1
  8. package/src/lib/app-utils/csv-import.d.ts +12 -0
  9. package/src/lib/app-utils/csv-import.js +13 -3
  10. package/src/lib/app-utils/csv-import.js.map +1 -1
  11. package/src/lib/app-utils/docs-help.generated.d.ts +1 -1
  12. package/src/lib/app-utils/docs-help.generated.js +2 -0
  13. package/src/lib/app-utils/docs-help.generated.js.map +1 -1
  14. package/src/lib/app-utils/docs-index.generated.js +78 -8
  15. package/src/lib/app-utils/docs-index.generated.js.map +1 -1
  16. package/src/lib/app-utils/health-report.js +10 -0
  17. package/src/lib/app-utils/health-report.js.map +1 -1
  18. package/src/lib/app-utils/notifications.d.ts +13 -1
  19. package/src/lib/app-utils/notifications.js +20 -1
  20. package/src/lib/app-utils/notifications.js.map +1 -1
  21. package/src/lib/app-utils/recipient-email-render.d.ts +21 -1
  22. package/src/lib/app-utils/recipient-email-render.js +34 -5
  23. package/src/lib/app-utils/recipient-email-render.js.map +1 -1
  24. package/src/lib/app-utils/server.d.ts +1 -1
  25. package/src/lib/app-utils/server.js +1 -1
  26. package/src/lib/app-utils/server.js.map +1 -1
  27. package/src/lib/app-utils/site-email-chrome.d.ts +52 -0
  28. package/src/lib/app-utils/site-email-chrome.js +82 -0
  29. package/src/lib/app-utils/site-email-chrome.js.map +1 -0
  30. package/src/lib/app-utils/system-email-chrome.d.ts +6 -0
  31. package/src/lib/app-utils/system-email-chrome.js.map +1 -1
  32. package/src/lib/plugin-manager/plugin-contact-capture.d.ts +11 -0
  33. package/src/lib/plugin-manager/plugin-contact-capture.js.map +1 -1
  34. package/src/lib/plugin-manager/plugin-record-email-state.d.ts +7 -0
  35. package/src/lib/plugin-manager/plugin-record-email-state.js.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/csv-import.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 * READING A SPREADSHEET INTO A CRM COLLECTION — the part that does not care\n * which collection (AGL-2621).\n *\n * The contact import (AGL-2602) settled how a file becomes records: a\n * header row is matched against an alias table to PROPOSE a column → field\n * mapping, the operator corrects it, one line is mapped to a raw row of\n * verbatim strings, and the server judges each row and tallies what it did.\n * A companies file goes through the same three stages with a different\n * field list, so the stages live here, parametric over the field union, and\n * the CRM plugin's contact and company importers each supply their vocabulary\n * — the fields, their aliases, their labels — and nothing else.\n *\n * ## A custom target is `custom:<key>`\n *\n * A column may also map to a custom field the merchant defined, which has\n * no standard name. The target is one string rather than a discriminated\n * object because it is the VALUE of a select control and travels through a\n * form; the prefix is what tells the two apart on the way back.\n *\n * ## The ceilings are shared numbers\n *\n * One file, one request and one preview are bounded the same way for every\n * collection, because the bounds are about the browser's memory and a\n * console request's budget, not about what the rows hold.\n */\n\n/**\n * The most rows one uploaded file may carry.\n *\n * A bound on the work one import represents and on the memory the browser\n * holds while mapping it — five thousand rows of a wide export is already\n * several megabytes of cells in a tab. A larger list is split and imported\n * in pieces; the drawer says so on the file, before a single row is sent.\n */\nexport const CSV_IMPORT_MAX_ROWS = 5_000\n\n/**\n * Rows per request.\n *\n * Each row costs the server a lookup and a write, plus whatever a name it\n * has not seen in this request costs to resolve; two hundred of those sit\n * comfortably inside a console request's budget and give the progress bar\n * twenty-five steps on the largest file the drawer accepts.\n */\nexport const CSV_IMPORT_CHUNK_SIZE = 200\n\n/**\n * The most bytes one request may carry, checked before the body is judged.\n *\n * Two hundred rows of a wide export with long values is well under a\n * megabyte; two megabytes is the line past which a request is not a chunk\n * of a file but something else being pushed through this door.\n */\nexport const CSV_IMPORT_MAX_BODY_BYTES = 2_000_000\n\n/** How many mapped rows the drawer shows before the operator commits. */\nexport const CSV_IMPORT_PREVIEW_ROWS = 10\n\n/** The prefix a custom-field target id carries. */\nconst CUSTOM_TARGET_PREFIX = 'custom:'\n\n/** The custom-field target id for a definition key. */\nexport function customImportTarget(key: string): `custom:${string}` {\n return `${CUSTOM_TARGET_PREFIX}${key}`\n}\n\n/** The definition key a custom target names, or `null` for a standard field. */\nexport function customImportTargetKey(target: string): string | null {\n return target.startsWith(CUSTOM_TARGET_PREFIX)\n ? target.slice(CUSTOM_TARGET_PREFIX.length)\n : null\n}\n\n/**\n * Normalizes a header cell for alias matching.\n *\n * Underscores, dots and hyphens all become one space, so `e-mail`,\n * `email_address` and `Email Address` compare as the words they are. The\n * alias tables are written in the human spelling and pass through the same\n * function, so a table stays readable and the comparison stays exact.\n */\nexport function importHeaderKey(value: unknown): string {\n return String(value ?? '')\n .trim()\n .toLowerCase()\n .replace(/[_.\\-/]+/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim()\n}\n\n/** An alias table, normalized once through {@link importHeaderKey}. */\nexport function importAliasKeys<F extends string>(\n fields: readonly F[],\n aliases: Record<F, readonly string[]>,\n): Record<F, ReadonlySet<string>> {\n const keys = {} as Record<F, ReadonlySet<string>>\n for (const field of fields) {\n keys[field] = new Set(aliases[field].map(importHeaderKey))\n }\n return keys\n}\n\n/**\n * A proposed mapping from a file's header row.\n *\n * Custom fields win over standard aliases when a header matches a\n * definition's label or key exactly — a merchant who defined a field called\n * \"Status\" meant THEIR status, not our lifecycle stage. Each target is\n * taken at most once, by the first column that claims it, so a file with two\n * columns named \"Email\" maps the first and leaves the second for the\n * operator to decide.\n */\nexport function guessImportMapping<F extends string>(\n columns: readonly string[],\n fields: readonly F[],\n aliasKeys: Record<F, ReadonlySet<string>>,\n): Record<number, F>\nexport function guessImportMapping<F extends string>(\n columns: readonly string[],\n fields: readonly F[],\n aliasKeys: Record<F, ReadonlySet<string>>,\n customFields: readonly { key: string; label: string }[],\n): Record<number, F | `custom:${string}`>\nexport function guessImportMapping<F extends string>(\n columns: readonly string[],\n fields: readonly F[],\n aliasKeys: Record<F, ReadonlySet<string>>,\n customFields: readonly { key: string; label: string }[] = [],\n): Record<number, F | `custom:${string}`> {\n const mapping: Record<number, F | `custom:${string}`> = {}\n const taken = new Set<string>()\n const customByHeader = new Map<string, string>()\n for (const field of customFields) {\n customByHeader.set(importHeaderKey(field.label), field.key)\n customByHeader.set(importHeaderKey(field.key), field.key)\n }\n columns.forEach((column, index) => {\n const key = importHeaderKey(column)\n if (!key) return\n const customKey = customByHeader.get(key)\n if (customKey) {\n const target = customImportTarget(customKey)\n if (!taken.has(target)) {\n mapping[index] = target\n taken.add(target)\n }\n return\n }\n for (const field of fields) {\n if (taken.has(field)) continue\n if (aliasKeys[field].has(key)) {\n mapping[index] = field\n taken.add(field)\n return\n }\n }\n })\n return mapping\n}\n\n/**\n * One parsed line under a mapping: the cells the mapping selected, under the\n * field they were mapped to, verbatim, with custom values gathered under\n * `custom` by definition key. Empty cells are left absent.\n */\nexport function mapImportRow<F extends string>(\n cells: readonly string[],\n mapping: Record<number, F | `custom:${string}`>,\n): Partial<Record<F, string>> & { custom?: Record<string, string> } {\n const standard: Partial<Record<F, string>> = {}\n const custom: Record<string, string> = {}\n for (const [indexText, target] of Object.entries(mapping)) {\n const value = String(cells[Number(indexText)] ?? '')\n if (!value.trim()) continue\n const customKey = customImportTargetKey(target)\n if (customKey) {\n custom[customKey] = value\n } else {\n standard[target as F] = value\n }\n }\n return Object.keys(custom).length ? { ...standard, custom } : standard\n}\n\n/**\n * A tag cell as the tags it names.\n *\n * `|` or `,` separated — the CSV export writes `|`, most other products\n * write `,` — lowercased and trimmed like a record's own tag field, so an\n * imported `VIP` and a typed `vip` are one tag and not two.\n */\nexport function parseImportTags(value: unknown, max: number): string[] {\n const parts = Array.isArray(value)\n ? value.map((entry) => String(entry ?? ''))\n : String(value ?? '').split(/[|,]/)\n return [\n ...new Set(parts.map((tag) => tag.trim().toLowerCase()).filter(Boolean)),\n ].slice(0, max)\n}\n\n/**\n * A yes/no cell as a boolean, or `null` when it is neither.\n *\n * The affirmatives are the ones consent and checkbox columns actually\n * carry; the negatives are listed so that an explicit `no` is a `false`\n * rather than an unreadable value that gets reported as dropped.\n */\nexport function parseImportFlag(value: unknown): boolean | null {\n if (typeof value === 'boolean') return value\n if (typeof value === 'number') return value === 1 ? true : value === 0 ? false : null\n const text = String(value ?? '')\n .trim()\n .toLowerCase()\n if (['yes', 'y', 'true', '1', 'on', 'subscribed', 'opted in', 'opted-in'].includes(text)) {\n return true\n }\n if (['no', 'n', 'false', '0', 'off', 'unsubscribed', ''].includes(text)) {\n return false\n }\n return null\n}\n\n/** A cell as trimmed text capped at `max`, or `undefined` when blank. */\nexport function importTextValue(value: unknown, max: number): string | undefined {\n const text = String(value ?? '')\n .trim()\n .slice(0, max)\n return text || undefined\n}\n\n/** A cell the file carried that could not be read as the field it was mapped to. */\nexport interface ImportDroppedValue {\n /** The standard field name, or `custom:<key>`. */\n field: string\n value: string\n}\n\n/** A CSV cell, quoted only when it has to be. */\nexport function csvCell(value: unknown): string {\n const text = String(value ?? '')\n return /[\",\\r\\n]/.test(text) ? `\"${text.replace(/\"/g, '\"\"')}\"` : text\n}\n\n/**\n * A whole CSV — the header, then one line per row — through {@link csvCell}.\n *\n * The one serializer for every CSV the console writes: each CRM section's\n * export, the skipped-rows file an import hands back and each report\n * table's export.\n * Quoting is decided in {@link csvCell} and nowhere else — a cell holding a\n * comma, a quote or a line break is wrapped in quotes with its quotes\n * doubled, no other cell is touched, and a `null` or `undefined` cell is\n * empty rather than the word — so that a report's file and a contacts file\n * open the same way in a spreadsheet.\n */\nexport function csvDocument(\n header: readonly unknown[],\n rows: readonly (readonly unknown[])[],\n): string {\n return [header, ...rows]\n .map((line) => line.map(csvCell).join(','))\n .join('\\n')\n}\n\n/**\n * The skipped rows as a file the operator can fix and re-import.\n *\n * The original columns verbatim plus a trailing `Skipped because` column,\n * so the file round-trips: correct the cell, delete the last column or\n * leave it unmapped, and import it again.\n */\nexport function importSkippedCsv<R extends string>(\n columns: readonly string[],\n entries: readonly { cells: readonly string[]; reason: R }[],\n labels: Record<R, string>,\n): string {\n return csvDocument(\n [...columns, 'Skipped because'],\n entries.map((entry) => [\n ...columns.map((_column, index) => entry.cells[index] ?? ''),\n labels[entry.reason] ?? entry.reason,\n ]),\n )\n}\n\n/**\n * One row the server did not store, by its index in the request. Each\n * collection names the row back to the operator in its own field — an\n * address, a company name — beside these two.\n */\nexport interface ImportSkippedRow<R extends string> {\n index: number\n reason: R\n}\n\n/**\n * What one request did, in the shape every collection's import reports.\n * The drawer sums these across a file; a collection with more to say\n * extends the shape and folds its own field beside {@link mergeImportResults}.\n */\nexport interface ImportChunkResult<S extends ImportSkippedRow<string>> {\n received: number\n created: number\n merged: number\n skipped: S[]\n /** Unreadable cells, tallied by field. */\n dropped: Record<string, number>\n /** Owner addresses that matched no member of the organization. */\n ownersUnresolved: string[]\n}\n\n/** An empty tally, for the drawer to fold chunk results into. */\nexport function emptyImportResult<\n S extends ImportSkippedRow<string>,\n>(): ImportChunkResult<S> {\n return {\n received: 0,\n created: 0,\n merged: 0,\n skipped: [],\n dropped: {},\n ownersUnresolved: [],\n }\n}\n\n/**\n * Two results as one.\n *\n * `offset` is where the chunk started in the file, so a skipped row's index\n * comes back as its position in the FILE rather than in the request — which\n * is what the download of skipped rows has to look up.\n */\nexport function mergeImportResults<S extends ImportSkippedRow<string>>(\n total: ImportChunkResult<S>,\n chunk: Partial<ImportChunkResult<S>>,\n offset = 0,\n): ImportChunkResult<S> {\n const dropped = { ...total.dropped }\n for (const [field, count] of Object.entries(chunk.dropped ?? {})) {\n dropped[field] = (dropped[field] ?? 0) + Number(count ?? 0)\n }\n return {\n received: total.received + Number(chunk.received ?? 0),\n created: total.created + Number(chunk.created ?? 0),\n merged: total.merged + Number(chunk.merged ?? 0),\n skipped: [\n ...total.skipped,\n ...(chunk.skipped ?? []).map((entry) => ({\n ...entry,\n index: Number(entry.index) + offset,\n })),\n ],\n dropped,\n ownersUnresolved: [\n ...new Set([...total.ownersUnresolved, ...(chunk.ownersUnresolved ?? [])]),\n ],\n }\n}\n"],"names":["CSV_IMPORT_MAX_ROWS","CSV_IMPORT_CHUNK_SIZE","CSV_IMPORT_MAX_BODY_BYTES","CSV_IMPORT_PREVIEW_ROWS","CUSTOM_TARGET_PREFIX","customImportTarget","key","customImportTargetKey","target","startsWith","slice","length","importHeaderKey","value","String","trim","toLowerCase","replace","importAliasKeys","fields","aliases","keys","field","Set","map","guessImportMapping","columns","aliasKeys","customFields","mapping","taken","customByHeader","Map","set","label","forEach","column","index","customKey","get","has","add","mapImportRow","cells","standard","custom","indexText","Object","entries","Number","parseImportTags","max","parts","Array","isArray","entry","split","tag","filter","Boolean","parseImportFlag","text","includes","importTextValue","undefined","csvCell","test","csvDocument","header","rows","line","join","importSkippedCsv","labels","_column","reason","emptyImportResult","received","created","merged","skipped","dropped","ownersUnresolved","mergeImportResults","total","chunk","offset","count"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/csv-import.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 * READING A SPREADSHEET INTO A CRM COLLECTION — the part that does not care\n * which collection (AGL-2621).\n *\n * The contact import (AGL-2602) settled how a file becomes records: a\n * header row is matched against an alias table to PROPOSE a column → field\n * mapping, the operator corrects it, one line is mapped to a raw row of\n * verbatim strings, and the server judges each row and tallies what it did.\n * A companies file goes through the same three stages with a different\n * field list, so the stages live here, parametric over the field union, and\n * the CRM plugin's contact and company importers each supply their vocabulary\n * — the fields, their aliases, their labels — and nothing else.\n *\n * ## A custom target is `custom:<key>`\n *\n * A column may also map to a custom field the merchant defined, which has\n * no standard name. The target is one string rather than a discriminated\n * object because it is the VALUE of a select control and travels through a\n * form; the prefix is what tells the two apart on the way back.\n *\n * ## The ceilings are shared numbers\n *\n * One file, one request and one preview are bounded the same way for every\n * collection, because the bounds are about the browser's memory and a\n * console request's budget, not about what the rows hold.\n */\n\n/**\n * The most rows one uploaded file may carry.\n *\n * A bound on the work one import represents and on the memory the browser\n * holds while mapping it — five thousand rows of a wide export is already\n * several megabytes of cells in a tab. A larger list is split and imported\n * in pieces; the drawer says so on the file, before a single row is sent.\n */\nexport const CSV_IMPORT_MAX_ROWS = 5_000\n\n/**\n * Rows per request.\n *\n * Each row costs the server a lookup and a write, plus whatever a name it\n * has not seen in this request costs to resolve; two hundred of those sit\n * comfortably inside a console request's budget and give the progress bar\n * twenty-five steps on the largest file the drawer accepts.\n */\nexport const CSV_IMPORT_CHUNK_SIZE = 200\n\n/**\n * The most bytes one request may carry, checked before the body is judged.\n *\n * Two hundred rows of a wide export with long values is well under a\n * megabyte; two megabytes is the line past which a request is not a chunk\n * of a file but something else being pushed through this door.\n */\nexport const CSV_IMPORT_MAX_BODY_BYTES = 2_000_000\n\n/** How many mapped rows the drawer shows before the operator commits. */\nexport const CSV_IMPORT_PREVIEW_ROWS = 10\n\n/** The prefix a custom-field target id carries. */\nconst CUSTOM_TARGET_PREFIX = 'custom:'\n\n/** The custom-field target id for a definition key. */\nexport function customImportTarget(key: string): `custom:${string}` {\n return `${CUSTOM_TARGET_PREFIX}${key}`\n}\n\n/** The definition key a custom target names, or `null` for a standard field. */\nexport function customImportTargetKey(target: string): string | null {\n return target.startsWith(CUSTOM_TARGET_PREFIX)\n ? target.slice(CUSTOM_TARGET_PREFIX.length)\n : null\n}\n\n/**\n * Normalizes a header cell for alias matching.\n *\n * Underscores, dots and hyphens all become one space, so `e-mail`,\n * `email_address` and `Email Address` compare as the words they are. The\n * alias tables are written in the human spelling and pass through the same\n * function, so a table stays readable and the comparison stays exact.\n */\nexport function importHeaderKey(value: unknown): string {\n return String(value ?? '')\n .trim()\n .toLowerCase()\n .replace(/[_.\\-/]+/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim()\n}\n\n/** An alias table, normalized once through {@link importHeaderKey}. */\nexport function importAliasKeys<F extends string>(\n fields: readonly F[],\n aliases: Record<F, readonly string[]>,\n): Record<F, ReadonlySet<string>> {\n const keys = {} as Record<F, ReadonlySet<string>>\n for (const field of fields) {\n keys[field] = new Set(aliases[field].map(importHeaderKey))\n }\n return keys\n}\n\n/**\n * A proposed mapping from a file's header row.\n *\n * Custom fields win over standard aliases when a header matches a\n * definition's label or key exactly — a merchant who defined a field called\n * \"Status\" meant THEIR status, not our lifecycle stage. Each target is\n * taken at most once, by the first column that claims it, so a file with two\n * columns named \"Email\" maps the first and leaves the second for the\n * operator to decide.\n */\nexport function guessImportMapping<F extends string>(\n columns: readonly string[],\n fields: readonly F[],\n aliasKeys: Record<F, ReadonlySet<string>>,\n): Record<number, F>\nexport function guessImportMapping<F extends string>(\n columns: readonly string[],\n fields: readonly F[],\n aliasKeys: Record<F, ReadonlySet<string>>,\n customFields: readonly { key: string; label: string }[],\n): Record<number, F | `custom:${string}`>\nexport function guessImportMapping<F extends string>(\n columns: readonly string[],\n fields: readonly F[],\n aliasKeys: Record<F, ReadonlySet<string>>,\n customFields: readonly { key: string; label: string }[] = [],\n): Record<number, F | `custom:${string}`> {\n const mapping: Record<number, F | `custom:${string}`> = {}\n const taken = new Set<string>()\n const customByHeader = new Map<string, string>()\n for (const field of customFields) {\n customByHeader.set(importHeaderKey(field.label), field.key)\n customByHeader.set(importHeaderKey(field.key), field.key)\n }\n columns.forEach((column, index) => {\n const key = importHeaderKey(column)\n if (!key) return\n const customKey = customByHeader.get(key)\n if (customKey) {\n const target = customImportTarget(customKey)\n if (!taken.has(target)) {\n mapping[index] = target\n taken.add(target)\n }\n return\n }\n for (const field of fields) {\n if (taken.has(field)) continue\n if (aliasKeys[field].has(key)) {\n mapping[index] = field\n taken.add(field)\n return\n }\n }\n })\n return mapping\n}\n\n/**\n * One parsed line under a mapping: the cells the mapping selected, under the\n * field they were mapped to, verbatim, with custom values gathered under\n * `custom` by definition key. Empty cells are left absent.\n */\nexport function mapImportRow<F extends string>(\n cells: readonly string[],\n mapping: Record<number, F | `custom:${string}`>,\n): Partial<Record<F, string>> & { custom?: Record<string, string> } {\n const standard: Partial<Record<F, string>> = {}\n const custom: Record<string, string> = {}\n for (const [indexText, target] of Object.entries(mapping)) {\n const value = String(cells[Number(indexText)] ?? '')\n if (!value.trim()) continue\n const customKey = customImportTargetKey(target)\n if (customKey) {\n custom[customKey] = value\n } else {\n standard[target as F] = value\n }\n }\n return Object.keys(custom).length ? { ...standard, custom } : standard\n}\n\n/**\n * A tag cell as the tags it names.\n *\n * `|` or `,` separated — the CSV export writes `|`, most other products\n * write `,` — lowercased and trimmed like a record's own tag field, so an\n * imported `VIP` and a typed `vip` are one tag and not two.\n */\nexport function parseImportTags(value: unknown, max: number): string[] {\n const parts = Array.isArray(value)\n ? value.map((entry) => String(entry ?? ''))\n : String(value ?? '').split(/[|,]/)\n return [\n ...new Set(parts.map((tag) => tag.trim().toLowerCase()).filter(Boolean)),\n ].slice(0, max)\n}\n\n/**\n * A yes/no cell as a boolean, or `null` when it is neither.\n *\n * The affirmatives are the ones consent and checkbox columns actually\n * carry; the negatives are listed so that an explicit `no` is a `false`\n * rather than an unreadable value that gets reported as dropped.\n */\nexport function parseImportFlag(value: unknown): boolean | null {\n if (typeof value === 'boolean') return value\n if (typeof value === 'number') return value === 1 ? true : value === 0 ? false : null\n const text = String(value ?? '')\n .trim()\n .toLowerCase()\n if (['yes', 'y', 'true', '1', 'on', 'subscribed', 'opted in', 'opted-in'].includes(text)) {\n return true\n }\n if (['no', 'n', 'false', '0', 'off', 'unsubscribed', ''].includes(text)) {\n return false\n }\n return null\n}\n\n/** A cell as trimmed text capped at `max`, or `undefined` when blank. */\nexport function importTextValue(value: unknown, max: number): string | undefined {\n const text = String(value ?? '')\n .trim()\n .slice(0, max)\n return text || undefined\n}\n\n/** A cell the file carried that could not be read as the field it was mapped to. */\nexport interface ImportDroppedValue {\n /** The standard field name, or `custom:<key>`. */\n field: string\n value: string\n}\n\n/** A CSV cell, quoted only when it has to be. */\nexport function csvCell(value: unknown): string {\n const text = String(value ?? '')\n return /[\",\\r\\n]/.test(text) ? `\"${text.replace(/\"/g, '\"\"')}\"` : text\n}\n\n/**\n * A whole CSV — the header, then one line per row — through {@link csvCell}.\n *\n * The one serializer for every CSV the console writes: each CRM section's\n * export, the skipped-rows file an import hands back and each report\n * table's export.\n * Quoting is decided in {@link csvCell} and nowhere else — a cell holding a\n * comma, a quote or a line break is wrapped in quotes with its quotes\n * doubled, no other cell is touched, and a `null` or `undefined` cell is\n * empty rather than the word — so that a report's file and a contacts file\n * open the same way in a spreadsheet.\n */\nexport function csvDocument(\n header: readonly unknown[],\n rows: readonly (readonly unknown[])[],\n): string {\n return [header, ...rows]\n .map((line) => line.map(csvCell).join(','))\n .join('\\n')\n}\n\n/**\n * The skipped rows as a file the operator can fix and re-import.\n *\n * The original columns verbatim plus a trailing `Skipped because` column,\n * so the file round-trips: correct the cell, delete the last column or\n * leave it unmapped, and import it again.\n */\nexport function importSkippedCsv<R extends string>(\n columns: readonly string[],\n entries: readonly { cells: readonly string[]; reason: R }[],\n labels: Record<R, string>,\n): string {\n return csvDocument(\n [...columns, 'Skipped because'],\n entries.map((entry) => [\n ...columns.map((_column, index) => entry.cells[index] ?? ''),\n labels[entry.reason] ?? entry.reason,\n ]),\n )\n}\n\n/**\n * One row the server did not store, by its index in the request. Each\n * collection names the row back to the operator in its own field — an\n * address, a company name — beside these two.\n */\nexport interface ImportSkippedRow<R extends string> {\n index: number\n reason: R\n}\n\n/**\n * What one request did, in the shape every collection's import reports.\n * The drawer sums these across a file; a collection with more to say\n * extends the shape and folds its own field beside {@link mergeImportResults}.\n */\nexport interface ImportChunkResult<S extends ImportSkippedRow<string>> {\n received: number\n created: number\n merged: number\n skipped: S[]\n /** Unreadable cells, tallied by field. */\n dropped: Record<string, number>\n /** Owner addresses that matched no member of the organization. */\n ownersUnresolved: string[]\n /**\n * Of the rows stored, how many carry an address whose domain has no mail\n * server (AGL-3328) — records that now read \"Would bounce\". Absent for a\n * collection whose rows carry no address, and for a request whose check\n * did not answer in time.\n */\n noMailServer?: number\n}\n\n/** An empty tally, for the drawer to fold chunk results into. */\nexport function emptyImportResult<\n S extends ImportSkippedRow<string>,\n>(): ImportChunkResult<S> {\n return {\n received: 0,\n created: 0,\n merged: 0,\n skipped: [],\n dropped: {},\n ownersUnresolved: [],\n }\n}\n\n/**\n * Two results as one.\n *\n * `offset` is where the chunk started in the file, so a skipped row's index\n * comes back as its position in the FILE rather than in the request — which\n * is what the download of skipped rows has to look up.\n */\nexport function mergeImportResults<S extends ImportSkippedRow<string>>(\n total: ImportChunkResult<S>,\n chunk: Partial<ImportChunkResult<S>>,\n offset = 0,\n): ImportChunkResult<S> {\n const dropped = { ...total.dropped }\n for (const [field, count] of Object.entries(chunk.dropped ?? {})) {\n dropped[field] = (dropped[field] ?? 0) + Number(count ?? 0)\n }\n return {\n received: total.received + Number(chunk.received ?? 0),\n created: total.created + Number(chunk.created ?? 0),\n merged: total.merged + Number(chunk.merged ?? 0),\n skipped: [\n ...total.skipped,\n ...(chunk.skipped ?? []).map((entry) => ({\n ...entry,\n index: Number(entry.index) + offset,\n })),\n ],\n dropped,\n ownersUnresolved: [\n ...new Set([...total.ownersUnresolved, ...(chunk.ownersUnresolved ?? [])]),\n ],\n ...(total.noMailServer !== undefined || chunk.noMailServer !== undefined\n ? { noMailServer: Number(total.noMailServer ?? 0) + Number(chunk.noMailServer ?? 0) }\n : {}),\n }\n}\n\n/**\n * What an import's result says about the addresses that would bounce\n * (AGL-3328), or `null` when none does.\n */\nexport function importNoMailServerSentence(count: number | null | undefined): string | null {\n const n = Math.max(0, Math.floor(Number(count) || 0))\n if (!n) return null\n return n === 1\n ? '1 address has no mail server. Its record reads \"Would bounce\", and campaigns and sequences skip it.'\n : `${n.toLocaleString('en-US')} addresses have no mail server. Their records read \"Would bounce\", ` +\n 'and campaigns and sequences skip them.'\n}\n"],"names":["CSV_IMPORT_MAX_ROWS","CSV_IMPORT_CHUNK_SIZE","CSV_IMPORT_MAX_BODY_BYTES","CSV_IMPORT_PREVIEW_ROWS","CUSTOM_TARGET_PREFIX","customImportTarget","key","customImportTargetKey","target","startsWith","slice","length","importHeaderKey","value","String","trim","toLowerCase","replace","importAliasKeys","fields","aliases","keys","field","Set","map","guessImportMapping","columns","aliasKeys","customFields","mapping","taken","customByHeader","Map","set","label","forEach","column","index","customKey","get","has","add","mapImportRow","cells","standard","custom","indexText","Object","entries","Number","parseImportTags","max","parts","Array","isArray","entry","split","tag","filter","Boolean","parseImportFlag","text","includes","importTextValue","undefined","csvCell","test","csvDocument","header","rows","line","join","importSkippedCsv","labels","_column","reason","emptyImportResult","received","created","merged","skipped","dropped","ownersUnresolved","mergeImportResults","total","chunk","offset","count","noMailServer","importNoMailServerSentence","n","Math","floor","toLocaleString"],"mappings":"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"}
@@ -276,7 +276,7 @@ export declare const PLUGIN_DOCS: {
276
276
  };
277
277
  export type PluginDocsKey = keyof typeof PLUGIN_DOCS;
278
278
  export declare const PLUGIN_DOCS_ANCHORS: {
279
- readonly abuseReports: readonly ["#where-reports-come-from", "#held-outbound-email", "#what-is-screened", "#tiers", "#deciding-a-held-row", "#names-and-domains", "#stripe-fraud-signals", "#seller-fraud-pattern", "#card-testing-velocity", "#marketplace", "#triage-by-severity", "#csam", "#which-lever", "#statuses", "#disclosure", "#dmca", "#counter-notices", "#counter-notice-clock", "#counter-notice-steps", "#repeat-infringers", "#repeat-infringer-threshold", "#known-gaps", "#related"];
279
+ readonly abuseReports: readonly ["#fraud-and-risk-alerts-by-email", "#where-reports-come-from", "#held-outbound-email", "#what-is-screened", "#tiers", "#deciding-a-held-row", "#names-and-domains", "#stripe-fraud-signals", "#seller-fraud-pattern", "#card-testing-velocity", "#marketplace", "#risk-notices", "#triage-by-severity", "#csam", "#which-lever", "#statuses", "#disclosure", "#dmca", "#counter-notices", "#counter-notice-clock", "#counter-notice-steps", "#repeat-infringers", "#repeat-infringer-threshold", "#known-gaps", "#related"];
280
280
  readonly actionsBuilder: readonly ["#create-an-action", "#recipes", "#describe-it", "#triggers", "#crm-events", "#only-run-when-a-field-matches", "#chain-multiple-conditions-andor", "#steps", "#crm-steps", "#step-conditions", "#sequences", "#run-history", "#what-is-and-isnt-recorded", "#interactions-from-the-besigner", "#when-to-use-which", "#related"];
281
281
  readonly aglynAssist: readonly ["#what-it-can-do", "#aglyn-ai", "#answers-for-beginners-and-developers", "#offers-to-open-a-page", "#edits-in-the-besigner", "#where-an-answer-came-from", "#answers-straight-from-the-documentation", "#message-limits", "#feedback", "#privacy"];
282
282
  readonly aiAutomations: readonly ["#draft", "#explain", "#why-a-run-failed", "#what-is-sent", "#who-can-use-it", "#related"];
@@ -273,6 +273,7 @@ export const PLUGIN_DOCS = {
273
273
  };
274
274
  export const PLUGIN_DOCS_ANCHORS = {
275
275
  abuseReports: [
276
+ '#fraud-and-risk-alerts-by-email',
276
277
  '#where-reports-come-from',
277
278
  '#held-outbound-email',
278
279
  '#what-is-screened',
@@ -283,6 +284,7 @@ export const PLUGIN_DOCS_ANCHORS = {
283
284
  '#seller-fraud-pattern',
284
285
  '#card-testing-velocity',
285
286
  '#marketplace',
287
+ '#risk-notices',
286
288
  '#triage-by-severity',
287
289
  '#csam',
288
290
  '#which-lever',
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/docs-help.generated.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// GENERATED FILE — do not edit. Regenerate with:\n// node tools/scripts/generate-docs-help.mjs\n// Source of truth: apps/docs/docs frontmatter + headings (AGL-602).\n\n// The first-party plugin consoles live in `libs/plugins/*` and cannot import\n// the console's constants, so they carry their own generated subset of the\n// docs help registry — the topics their cards actually link to, and no others.\n\nexport interface PluginDocsTopic {\n /** Docs-site path, e.g. `/commerce-and-bookings/commerce/overview`. */\n path: string\n /** Docs page title. */\n title: string\n /** Verbatim docs frontmatter description — the tooltip excerpt. */\n excerpt: string\n}\n\nexport const PLUGIN_DOCS = {\n abuseReports: {\n path: '/staff-console/abuse-reports',\n title: 'Abuse reports',\n excerpt: 'The public abuse-report queue — where outside reports land, how to triage by severity, which lever answers which report, and the CSAM and DMCA paths that are not takedown buttons.',\n },\n actionsBuilder: {\n path: '/marketing-and-automation/workflows-and-actions/actions-builder',\n title: 'Actions builder',\n excerpt: 'Map a single event to a single action without building a full workflow.',\n },\n aglynAssist: {\n path: '/getting-started/aglyn-assist',\n title: 'Aglyn Assist',\n excerpt: 'Ask the built-in AI helper how to do anything in Aglyn — it answers from these docs and links you straight to the right page.',\n },\n aiAutomations: {\n path: '/ai/automations-with-ai',\n title: 'Explain automations with AI',\n excerpt: 'Aglyn AI can explain an automation you already have and tell you why one of its runs failed. Drafting an automation from a description is not available yet.',\n },\n aiCrm: {\n path: '/ai/crm-by-ai',\n title: 'The AI CRM built into Aglyn',\n excerpt: 'An AI CRM for small business: a short summary and a suggested next step on a contact, company, deal or lead, a one-to-one email drafted into the composer, and an import\\'s columns matched to fields.',\n },\n aiExperiments: {\n path: '/ai/ab-tests-with-ai',\n title: 'A/B tests by AI: write variants, read the result',\n excerpt: 'Have Aglyn AI write two to four variants for a screen, section or email experiment, and put a finished test into plain language — with the verdict decided from the counts before the model is asked anything.',\n },\n aiMonitoring: {\n path: '/staff-console/ai-monitoring',\n title: 'AI monitoring',\n excerpt: 'How staff watch one organization\\'s AI usage — the add-on, credit pool, overage, refusals, jobs, tokens and cache hit rate, top spenders and margin — and where those figures appear across the staff console.',\n },\n aiProducts: {\n path: '/ai/products-with-ai',\n title: 'Product copy and catalogs with AI',\n excerpt: 'An AI product description generator for your catalog: write copy for one product or a whole import, propose a first catalog, and propose categories and discounts — all as proposals you review.',\n },\n aiSeo: {\n path: '/building-sites/seo/seo-by-ai',\n title: 'AI SEO for your website',\n excerpt: 'AI SEO for websites, on the pages you already have: have AI write a page or product\\'s search listing, audit every published page with a proposed fix for each finding, and draft your structured data and /llms.txt.',\n },\n aiThemes: {\n path: '/ai/theme-assist',\n title: 'Change your site\\'s theme with AI',\n excerpt: 'Describe a change to your site\\'s theme and get a proposal for the theme editor\\'s own controls, previewed before and after, that you save in the editor. Rolling out.',\n },\n assistSignals: {\n path: '/staff-console/assist-signals',\n title: 'Assist Signal',\n excerpt: 'The docs-gap and cost board behind Assist — how the ranking is ordered, why ungrounded questions are counted separately, and what the cache-read rate says about margin.',\n },\n billing: {\n path: '/workspace-and-billing/billing-and-plans/overview',\n title: 'Billing & Plans',\n excerpt: 'How Aglyn\\'s tiers, entitlements, quotas, usage meters, and seat add-ons work.',\n },\n bindings: {\n path: '/building-sites/bindings/overview',\n title: 'Bindings, Variables & Functions',\n excerpt: 'Live values in your content — typed variables, no-code functions, and rename-safe id tokens.',\n },\n bookings: {\n path: '/commerce-and-bookings/bookings/overview',\n title: 'Bookings & Scheduling',\n excerpt: 'Offer services with availability, let visitors book, take payment, and send reminders.',\n },\n buildAWorkflow: {\n path: '/marketing-and-automation/workflows-and-actions/build-a-workflow',\n title: 'Build a workflow',\n excerpt: 'Create a multi-step workflow that runs when a site event fires.',\n },\n catalog: {\n path: '/commerce-and-bookings/commerce/catalog',\n title: 'Product catalog',\n excerpt: 'Products with options and variants, categories, tags, and manual or smart collections.',\n },\n commerce: {\n path: '/commerce-and-bookings/commerce/overview',\n title: 'Commerce',\n excerpt: 'Sell physical, digital, and service products with a full catalog, orders pipeline, shipping, taxes, and your own Stripe account.',\n },\n commerceEndToEnd: {\n path: '/guides/commerce-end-to-end',\n title: 'Commerce end to end',\n excerpt: 'Create products with billing modes, design the storefront with Commerce blocks, take Stripe checkout for one-time and subscription sales, and run orders from the console.',\n },\n companies: {\n path: '/content-and-data/crm/companies',\n title: 'Companies',\n excerpt: 'Group your contacts under the businesses they belong to — one record per company, with its domain, owner, address and the people who work there.',\n },\n consoleTour: {\n path: '/getting-started/console-tour',\n title: 'The console tour',\n excerpt: 'Where things live in the Aglyn console app bar and navigation.',\n },\n contactActivities: {\n path: '/content-and-data/crm/activities',\n title: 'Activities & the timeline',\n excerpt: 'Log calls, emails, meetings and notes against a contact, a company, a deal or a lead, email one person from their record, and read it all in one timeline beside everything captured and every campaign sent.',\n },\n contactFields: {\n path: '/content-and-data/crm/custom-fields',\n title: 'Custom fields',\n excerpt: 'Define your own properties on contacts, companies, deals and leads — text, number, date, choice, checkbox or link — show them on every record and its list, and save form answers straight into a contact\\'s.',\n },\n contactRecord: {\n path: '/content-and-data/crm/contact-record',\n title: 'The contact record',\n excerpt: 'Add a contact by hand, keep a profile on them — phone, company, title, address, owner and lifecycle stage — open their own page in the CRM, and merge two records that turn out to be one person.',\n },\n contacts: {\n path: '/content-and-data/crm/overview',\n title: 'CRM',\n excerpt: 'One place for the people who interact with your sites — contacts captured from forms, members, orders and bookings, with leads, companies, a deals pipeline, tasks, a timeline, reports and custom fields.',\n },\n crmEmailTemplates: {\n path: '/content-and-data/crm/email-templates',\n title: 'Email templates',\n excerpt: 'Keep the letters your team sends from a record — templates that fill in the subject and the message, snippets that drop in a paragraph, and merge fields that fill in the person\\'s name, their company, the deal and you.',\n },\n crmLeads: {\n path: '/content-and-data/crm/leads',\n title: 'Leads',\n excerpt: 'Work the people your site has captured — a status, an owner and notes on every lead — and convert one into a contact, a company and a deal.',\n },\n crmReports: {\n path: '/content-and-data/crm/reports',\n title: 'Reports',\n excerpt: 'New contacts, where they came from and which sources convert, the lead funnel, the open pipeline and its forecast, won and lost, who logged what, and the task load — counted on the server, every table exportable as CSV.',\n },\n crmSettings: {\n path: '/content-and-data/crm/settings',\n title: 'CRM settings',\n excerpt: 'What the CRM does on its own for every site — whether a company is created from a contact\\'s work email domain, who a new contact is assigned to, the address that files replies on a record, and each site\\'s recipes.',\n },\n crmTasks: {\n path: '/content-and-data/crm/tasks',\n title: 'Tasks & follow-ups',\n excerpt: 'Calls, emails, meetings and to-dos with a due date, an assignee and a link to the record they are for — overdue and today read off the clock, a snooze, a reminder at its own time, and a morning digest.',\n },\n crmViews: {\n path: '/content-and-data/crm/views',\n title: 'Saved views',\n excerpt: 'Keep a CRM list\\'s filters, columns and sort under a name, open it from the views menu or a link, share it with the team, and use a contacts view as an email audience.',\n },\n datasets: {\n path: '/content-and-data/datasets/overview',\n title: 'Datasets & Dynamic Content',\n excerpt: 'Model structured content with typed fields and relations, then bind it into repeatable components.',\n },\n deals: {\n path: '/content-and-data/crm/deals',\n title: 'Deals pipeline',\n excerpt: 'Every open deal by stage — with an amount, an owner and an expected close — as a board you drag across or a table you page through, and the won and lost history behind it.',\n },\n designedEmails: {\n path: '/marketing-and-automation/email-campaigns/designed-emails',\n title: 'Designed emails',\n excerpt: 'Build campaign emails in the besigner with email-safe blocks and merge tokens — no separate editor.',\n },\n emailCampaigns: {\n path: '/marketing-and-automation/email-campaigns/overview',\n title: 'Email Campaigns',\n excerpt: 'Campaigns to audiences built from your contacts. Campaign email starts at Pro and needs a sending domain of the site\\'s own; receipts and account email send on every plan.',\n },\n events: {\n path: '/content-and-data/events/overview',\n title: 'Events Calendar',\n excerpt: 'Keep a schedule of events in the console and publish the ones you choose to any screen, with search-engine event markup.',\n },\n forms: {\n path: '/content-and-data/forms/overview',\n title: 'Forms & Lead Capture',\n excerpt: 'Add forms to your site, collect submissions in an inbox, and write them into datasets.',\n },\n installYourFirstPlugin: {\n path: '/guides/install-your-first-plugin',\n title: 'Install your first marketplace item',\n excerpt: 'A click-by-click walkthrough of the Marketplace — find something, choose which sites get it, install it, and turn it off again.',\n },\n inviteTeammates: {\n path: '/workspace-and-billing/teams-and-roles/invite-teammates',\n title: 'Invite teammates',\n excerpt: 'Add people to your site and understand how team members act within your organization.',\n },\n manifestAndEnvs: {\n path: '/developers/plugins/reference/manifest-and-envs',\n title: 'Manifests, trust lifecycle & environment',\n excerpt: 'The plugin manifest schema, the marketplace listing/version documents, the trust state machine, and every PLUGIN_* environment variable.',\n },\n marketingOverlays: {\n path: '/marketing-and-automation/marketing-overlays/overview',\n title: 'Marketing Overlays',\n excerpt: 'Site-wide announcement bars and promotional popups with triggers, scheduling, and email capture.',\n },\n membersOnly: {\n path: '/workspace-and-billing/teams-and-roles/members-only',\n title: 'Members-only areas',\n excerpt: 'Let visitors sign up as members and gate screens so only members can view them.',\n },\n orgAutomations: {\n path: '/marketing-and-automation/workflows-and-actions/org-automations',\n title: 'Org automations',\n excerpt: 'Write an automation once for the whole organization and run it on the sites you choose, with a pause on each site.',\n },\n plugins: {\n path: '/developers/plugins/overview',\n title: 'Plugins & Marketplace',\n excerpt: 'Extend Aglyn with sandboxed plugins — install from the marketplace, configure them, and publish your own.',\n },\n pos: {\n path: '/commerce-and-bookings/commerce/pos-and-reservations',\n title: 'POS & reservations',\n excerpt: 'Sell in person from the console register and take date-range reservations with deposits.',\n },\n publishAPlugin: {\n path: '/developers/plugins/publish-a-plugin',\n title: 'Publish a plugin',\n excerpt: 'Ship your own plugin to the marketplace with version pinning.',\n },\n publisherHandbook: {\n path: '/developers/plugins/publishing/publisher-handbook',\n title: 'Publisher handbook',\n excerpt: 'Publishing to the Aglyn marketplace — from profile setup through listing authoring, review, updates, and getting paid.',\n },\n redirects: {\n path: '/building-sites/redirects/overview',\n title: 'Redirects',\n excerpt: 'Manage URL redirects with validation, loop detection, and hit metrics.',\n },\n sandboxSecurity: {\n path: '/developers/plugins/reference/sandbox-security',\n title: 'Sandbox security model',\n excerpt: 'How sandboxed marketplace plugins are isolated — separate origin, per-manifest CSP, pinned artifacts — and what that means when you write one.',\n },\n sequences: {\n path: '/content-and-data/crm/sequences',\n title: 'Sequences',\n excerpt: 'One-to-one email sequences a rep sends to a person from their own connected mailbox, kept with the CRM. Rolling out.',\n },\n webhooks: {\n path: '/marketing-and-automation/workflows-and-actions/webhooks',\n title: 'Webhooks',\n excerpt: 'Connect Aglyn to other systems with outbound and inbound webhooks.',\n },\n} as const satisfies Record<string, PluginDocsTopic>\n\nexport type PluginDocsKey = keyof typeof PLUGIN_DOCS\n\nexport const PLUGIN_DOCS_ANCHORS = {\n abuseReports: ['#where-reports-come-from', '#held-outbound-email', '#what-is-screened', '#tiers', '#deciding-a-held-row', '#names-and-domains', '#stripe-fraud-signals', '#seller-fraud-pattern', '#card-testing-velocity', '#marketplace', '#triage-by-severity', '#csam', '#which-lever', '#statuses', '#disclosure', '#dmca', '#counter-notices', '#counter-notice-clock', '#counter-notice-steps', '#repeat-infringers', '#repeat-infringer-threshold', '#known-gaps', '#related'],\n actionsBuilder: ['#create-an-action', '#recipes', '#describe-it', '#triggers', '#crm-events', '#only-run-when-a-field-matches', '#chain-multiple-conditions-andor', '#steps', '#crm-steps', '#step-conditions', '#sequences', '#run-history', '#what-is-and-isnt-recorded', '#interactions-from-the-besigner', '#when-to-use-which', '#related'],\n aglynAssist: ['#what-it-can-do', '#aglyn-ai', '#answers-for-beginners-and-developers', '#offers-to-open-a-page', '#edits-in-the-besigner', '#where-an-answer-came-from', '#answers-straight-from-the-documentation', '#message-limits', '#feedback', '#privacy'],\n aiAutomations: ['#draft', '#explain', '#why-a-run-failed', '#what-is-sent', '#who-can-use-it', '#related'],\n aiCrm: ['#summarize-a-record', '#summaries-are-reused-until-the-record-changes', '#draft-an-email', '#match-columns', '#what-is-sent', '#who-can-use-it', '#related'],\n aiExperiments: ['#it-proposes-you-write', '#write-variants', '#putting-them-in', '#what-it-will-not-write', '#read-a-result', '#the-verdict', '#the-words', '#undecided', '#what-is-sent', '#who-can-use-it', '#related'],\n aiMonitoring: ['#the-ai-card', '#where-else', '#one-account', '#the-spend-leaderboard', '#alerts', '#related'],\n aiProducts: ['#write-a-products-copy', '#write-copy-for-many-products', '#when-you-import-products', '#propose-a-first-catalog', '#propose-categories-and-discounts', '#what-the-copy-never-says', '#what-is-sent-to-the-ai-provider', '#who-can-use-it', '#related'],\n aiSeo: ['#write-a-pages-listing', '#write-a-products-listing', '#audit-the-whole-site', '#target-keywords', '#apply-all-as-drafts', '#structured-data-and-llmstxt', '#related'],\n aiThemes: ['#change-what-you-describe-or-design-a-new-theme', '#match-your-brand', '#review-the-proposal', '#save-it-or-dont', '#who-can-use-it', '#related'],\n assistSignals: ['#the-workflow-this-board-exists-for', '#fleet', '#the-cache-read-rate-and-what-a-bad-number-looks-like', '#where-the-money-goes', '#tokens-by-kind', '#docs-gaps', '#questions-the-docs-could-not-answer', '#what-people-actually-asked', '#what-assist-costs-by-workspace', '#reading-the-sample-honestly', '#related'],\n billing: ['#tiers--entitlements', '#enterprise', '#single-sign-on-and-enforcement', '#usage-meters', '#who-is-generating-what', '#ai-allotments', '#storage-overage', '#if-you-would-rather-uploads-stopped', '#assist-overage', '#stop-ai-assist-at-the-included-band', '#ai-overage-ceiling', '#free-ai-credits', '#ai-credit-alerts', '#usage-budget', '#seats', '#crm-records', '#the-crm-suite', '#one-to-one-email', '#organization-data', '#api-access', '#payments', '#outstanding', '#plan-total', '#billing-email', '#payment-methods', '#billing-address', '#tax-ids', '#sales-tax', '#platform-fees', '#related'],\n bindings: ['#binding-tokens', '#rename-safe-id-tokens', '#insert-a-variable', '#token-pills', '#in-the-canvas-text-editor', '#site-details', '#typed-variables', '#no-code-functions', '#parameters-a-visitor-can-answer', '#a-calculator-you-lay-out', '#where-used--safety', '#workflows', '#related'],\n bookings: ['#set-up-bookings', '#taking-bookings', '#reminders', '#payments-and-fees', '#service-tax', '#manage', '#booking-from-the-crm', '#canceling-and-refunding', '#related'],\n buildAWorkflow: ['#1-open-the-workflows-page', '#2-choose-a-trigger', '#3-add-steps', '#waiting', '#4-save-and-test', '#duplicate-a-workflow', '#tips', '#related'],\n catalog: ['#products-options-and-variants', '#billing-modes-and-subscriptions', '#ai', '#categories-and-tags', '#collections', '#slugs', '#merchant-center-feed', '#related'],\n commerce: ['#products-hub', '#inventory', '#reserved-stock', '#stock-movements', '#gift-cards', '#recovery-and-alerts', '#orders', '#orders-screen', '#order-statuses', '#order-money-tiles', '#a-lost-dispute', '#shipping--taxes', '#lodging-tax-on-reservations', '#storefront-sales-tax', '#destination-coverage', '#dropshipping', '#related'],\n commerceEndToEnd: ['#1-connect-payments', '#2-create-products', '#3-design-the-storefront', '#catalog-search-filters-and-sort', '#category-pages', '#the-product-page-template', '#4-what-checkout-does', '#paying-without-leaving-your-site', '#5-run-orders-from-the-console', '#6-subscriptions--the-stripe-portal', '#related'],\n companies: ['#the-companies-list', '#create-a-company', '#a-companys-page', '#contacts-at-a-company', '#linked-on-capture', '#import-from-csv', '#export-csv', '#deleting-a-company', '#who-can-see-a-company', '#files', '#related'],\n consoleTour: ['#the-app-bar', '#in-context-help', '#filter-and-search', '#primary-navigation', '#editing-vs-managing', '#the-sites-list', '#the-status-pill', '#how-the-pill-is-decided', '#your-site-allowance', '#a-sites-dashboard', '#next', '#workspace-settings--notifications', '#the-notifications-feed', '#notification-settings', '#one-kind-at-a-time', '#workspace-and-site-overrides', '#daily-digests', '#alerts-on-this-device'],\n contactActivities: ['#four-kinds-of-history', '#reading-the-timeline', '#campaign-email', '#logging-an-activity', '#meeting-from-a-booking', '#click-to-call', '#sending-an-email', '#delivery-states', '#captured-email', '#where-an-activity-is-visible', '#the-recent-activity-feed', '#related'],\n contactFields: ['#define-a-field', '#fields-per-record', '#where-values-show', '#save-a-form-field', '#lead-source-values', '#over-the-api', '#retire-restore-delete', '#recompute-next-activity', '#related'],\n contactRecord: ['#adding-a-contact-by-hand', '#the-record-page', '#deleting-and-erasing', '#what-each-site-keeps-to-itself', '#when-sites-join-or-leave-a-group', '#merging-two-records', '#likely-duplicates', '#owner', '#last-engaged', '#lifecycle-stages', '#where-the-persons-lead-is', '#finding-a-contact', '#files', '#related'],\n contacts: ['#whats-in-the-crm-area', '#unified-ingestion', '#what-each-plan-includes', '#the-contacts-page', '#import-from-csv', '#segments', '#everywhere-the-crm-shows-up', '#capture-replies', '#at-the-organization-level', '#who-can-open-the-crm', '#one-sender-one-crm', '#related'],\n crmEmailTemplates: ['#templates-and-snippets', '#merge-fields', '#saving', '#managing-templates', '#duplicate-a-template', '#shared-or-personal', '#over-the-rest-api', '#related'],\n crmLeads: ['#what-makes-a-lead', '#what-a-lead-holds', '#adding-a-lead-by-hand', '#the-leads-list', '#filter-the-leads', '#working-a-lead-from-the-row', '#several-leads-at-once', '#export-csv', '#import-from-csv', '#who-owns-a-lead', '#a-leads-page', '#email-state', '#converting-a-lead', '#unqualifying-a-lead', '#erasing-the-person', '#who-can-do-this', '#related'],\n crmReports: ['#choosing-a-period', '#contacts', '#sources-and-lifecycle', '#conversion-by-source', '#lead-funnel', '#pipeline', '#forecast-by-close-month', '#won-and-lost', '#won-and-lost-by-owner', '#activity-by-teammate', '#tasks', '#exporting-a-table', '#crm-at-a-glance', '#how-the-numbers-are-counted', '#related'],\n crmSettings: ['#companies', '#create-companies-from-work-email-domains', '#default-owner', '#assignment-rules', '#round-robin', '#email-templates', '#email-capture', '#your-sending-addresses', '#recipes', '#related'],\n crmTasks: ['#the-tasks-page', '#the-calendar-view', '#snoozing-a-task', '#selecting-exporting-and-acting-on-many', '#import-from-csv', '#creating-a-task', '#assigning-a-task-to-someone-else', '#completing-and-reopening', '#organization-tasks', '#tasks-on-a-contact-company-or-deal', '#reminders', '#turning-reminders-off', '#next-activity', '#the-daily-digest', '#turning-it-off', '#the-dashboard-card', '#who-can-do-what', '#related'],\n crmViews: ['#the-views-control', '#a-view-is-a-link', '#filters', '#filters-on-the-contacts-list', '#filters-on-the-other-lists', '#columns-and-sort', '#segments-and-views', '#who-sees-what', '#related'],\n datasets: ['#model-builder', '#typed-documents', '#filter-records', '#relations', '#query-layer', '#repeatable-components', '#who-a-dataset-is-shared-with', '#import--export', '#related'],\n deals: ['#pipelines', '#stages', '#the-board-and-the-table', '#import-from-csv', '#creating-a-deal', '#line-items', '#moving-winning-and-losing', '#a-won-deal-makes-its-contact-a-customer', '#a-deals-page', '#files', '#related'],\n designedEmails: ['#create-a-template', '#find-a-template', '#duplicate-a-template', '#styling-email-blocks', '#merge-tokens', '#send-it', '#the-plain-text-version', '#start-from-a-brief-instead'],\n emailCampaigns: ['#send-a-campaign', '#campaigns-belong-to-the-organization', '#organization-emails-page', '#campaigns-group-emails', '#filter-the-lists', '#what-belongs-to-a-campaign', '#who-the-email-comes-from', '#sending-domains', '#account-email-always-sends', '#marketing-needs-a-domain', '#two-ways-to-get-a-domain', '#a-domain-we-set-up-is-a-request', '#domain-states', '#senders', '#send-a-test', '#preview-the-email', '#monthly-send-cap', '#personalize-with-merge-tags', '#recipient-count', '#who-a-campaign-is-allowed-to-reach', '#schedule-a-send', '#held-for-review', '#duplicate-an-email', '#email-lists', '#manual-lists', '#list-members', '#add-to-a-list', '#import-a-list', '#remove-from-a-list', '#lists-built-from-a-rule', '#experiments', '#experiments-across-sites', '#opens--clicks', '#the-campaign-report', '#per-contact-engagement', '#which-links-were-clicked', '#revenue-from-a-campaign', '#conversions', '#compliance', '#list-unsubscribe', '#topics', '#preference-page', '#consent-groups', '#consent-group-create', '#consent-group-join', '#consent-group-leave', '#consent-group-rename', '#consent-group-progress', '#frequency-opt-down', '#double-opt-in', '#consent-group-confirmation', '#marketing-mail', '#frequency-cap', '#suppressions', '#add-a-suppression', '#platform-suppressions', '#related'],\n events: ['#manage-events', '#show-events-on-a-screen', '#search-engines', '#related'],\n forms: ['#reading-submissions-from-code', '#build-a-form', '#monthly-allowance-per-plan', '#spam-and-abuse-protection', '#the-per-site-monthly-ceiling', '#field-types', '#labels-and-placeholders', '#example-a-quick-survey', '#after-submit', '#example-grow-an-email-list-from-a-signup-form', '#consent-group-disclosure', '#where-submissions-go', '#the-inbox', '#filter-the-inbox', '#who-a-submission-is-from', '#where-this-one-went', '#replying-to-a-submission', '#every-sites-inbox-at-once', '#one-forms-own-page', '#find-a-form', '#duplicate-a-form', '#switch-forms-off-for-one-site', '#related'],\n installYourFirstPlugin: ['#before-you-start', '#step-1-open', '#step-2-browse', '#step-3-reviews', '#step-4-targeting', '#step-5-install', '#step-6-use', '#step-7-off', '#what-to-do-next', '#related'],\n inviteTeammates: ['#invite-someone', '#pending-invites', '#who-gets-told', '#accepting-an-invite', '#an-invitation-never-changes-who-owns-the-workspace', '#how-team-members-act', '#you-are-a-site-collaborators-support-channel', '#help-a-teammate-who-is-locked-out', '#why-you-cant-always-set-a-password', '#activity-log', '#ai-actions', '#ai-usage', '#ai-allotment', '#tips', '#related'],\n manifestAndEnvs: ['#plugin-manifest-published-with-every-version', '#contributes--where-the-plugin-loads', '#config--settings-without-writing-a-settings-screen', '#listing--version-documents', '#review--trust-lifecycle', '#environment-variables', '#pluginsconfigjson-first-party-contributors'],\n marketingOverlays: ['#announcement-bar', '#promotional-popups', '#frequency', '#popup-v2', '#multiple-overlays-scheduling--page-targeting', '#variables-in-copy', '#engagement-stats', '#across-your-sites', '#related'],\n membersOnly: ['#let-visitors-sign-up', '#sign-in-sign-up-and-recovery-pages', '#forgotten-passwords', '#gate-a-screen', '#manage-your-members', '#suspend-or-reactivate-a-member', '#tips', '#related'],\n orgAutomations: ['#what-an-org-automation-is', '#create-one', '#triggers', '#steps', '#pause-it-on-one-site', '#waiting-switching-off-and-deleting', '#every-sites-own-automations', '#related'],\n plugins: ['#install--upgrade', '#browse-card', '#whats-included', '#what-the-badges-on-a-listing-mean', '#how-plugins-run', '#when-one-plugin-depends-on-another', '#a-dependency-that-is-off-for-one-site', '#configure', '#configure-site', '#publish-your-own', '#related'],\n pos: ['#registers', '#the-register', '#platform-fees-at-the-register', '#selling-past-the-count', '#when-something-disconnects', '#reservations', '#related'],\n publishAPlugin: ['#the-publish-pipeline', '#private-plugins', '#paid-listings', '#your-publisher-profile', '#tips', '#related'],\n publisherHandbook: ['#before-your-first-publish', '#the-publisher-agreement', '#where-to-publish-from', '#what-installing-each-type-does', '#rules-an-email-starter-has-to-meet', '#publishing-a-version', '#before-you-publish', '#review-what-happens-after-you-publish', '#the-two-badges-and-what-each-one-promises', '#asking-to-be-verified', '#testing-a-version-before-it-is-approved', '#watching-your-own-submission', '#disabled-versions', '#private-plugins', '#authoring-your-listing', '#what-your-listing-can-say-about-aglyn', '#versioning--updates', '#shipping-a-new-version', '#how-installs-work-the-buyer-side', '#getting-paid', '#low-prices-and-processing'],\n redirects: ['#manage-redirects', '#sending-visitors-to-another-site', '#metrics', '#match-modes-v2', '#related'],\n sandboxSecurity: ['#a-separate-origin', '#per-manifest-network-policy', '#when-you-cant-declare-the-origin', '#pinned-immutable-artifacts', '#what-this-means-when-you-build', '#related'],\n sequences: ['#what-it-is-for', '#sent-from-your-own-mailbox', '#where-it-lives', '#connect-a-mailbox', '#send-as', '#daily-cap', '#mailbox-status', '#auto-pause', '#mailbox-actions', '#link-domains', '#compliance-settings', '#allowed-countries', '#do-not-contact-domains', '#sequences', '#build-a-sequence', '#send-a-test', '#sequence-status', '#enroll', '#mail-gateways', '#cold-contacts', '#enrollments', '#person-history', '#curate', '#sending', '#what-stops-a-sequence', '#unsubscribe', '#related'],\n webhooks: ['#outbound-webhooks', '#inbound-webhooks', '#tips', '#related'],\n} as const satisfies Partial<Record<PluginDocsKey, readonly `#${string}`[]>>\n\ntype PluginAnchorMap = typeof PLUGIN_DOCS_ANCHORS\n\n/** Valid heading anchors for a plugin docs page (`never` when none). */\nexport type PluginDocsAnchor<K extends PluginDocsKey> =\n K extends keyof PluginAnchorMap ? PluginAnchorMap[K][number] : never\n"],"names":["PLUGIN_DOCS","abuseReports","path","title","excerpt","actionsBuilder","aglynAssist","aiAutomations","aiCrm","aiExperiments","aiMonitoring","aiProducts","aiSeo","aiThemes","assistSignals","billing","bindings","bookings","buildAWorkflow","catalog","commerce","commerceEndToEnd","companies","consoleTour","contactActivities","contactFields","contactRecord","contacts","crmEmailTemplates","crmLeads","crmReports","crmSettings","crmTasks","crmViews","datasets","deals","designedEmails","emailCampaigns","events","forms","installYourFirstPlugin","inviteTeammates","manifestAndEnvs","marketingOverlays","membersOnly","orgAutomations","plugins","pos","publishAPlugin","publisherHandbook","redirects","sandboxSecurity","sequences","webhooks","PLUGIN_DOCS_ANCHORS"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD,iDAAiD;AACjD,8CAA8C;AAC9C,oEAAoE;AAEpE,6EAA6E;AAC7E,2EAA2E;AAC3E,+EAA+E;AAW/E,OAAO,MAAMA,cAAc;IACzBC,cAAc;QACZC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAC,gBAAgB;QACdH,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAE,aAAa;QACXJ,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAG,eAAe;QACbL,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAI,OAAO;QACLN,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAK,eAAe;QACbP,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAM,cAAc;QACZR,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAO,YAAY;QACVT,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAQ,OAAO;QACLV,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAS,UAAU;QACRX,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAU,eAAe;QACbZ,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAW,SAAS;QACPb,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAY,UAAU;QACRd,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAa,UAAU;QACRf,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAc,gBAAgB;QACdhB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAe,SAAS;QACPjB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAgB,UAAU;QACRlB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAiB,kBAAkB;QAChBnB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAkB,WAAW;QACTpB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAmB,aAAa;QACXrB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAoB,mBAAmB;QACjBtB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAqB,eAAe;QACbvB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAsB,eAAe;QACbxB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAuB,UAAU;QACRzB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAwB,mBAAmB;QACjB1B,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAyB,UAAU;QACR3B,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA0B,YAAY;QACV5B,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA2B,aAAa;QACX7B,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA4B,UAAU;QACR9B,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA6B,UAAU;QACR/B,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA8B,UAAU;QACRhC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA+B,OAAO;QACLjC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAgC,gBAAgB;QACdlC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAiC,gBAAgB;QACdnC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAkC,QAAQ;QACNpC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAmC,OAAO;QACLrC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAoC,wBAAwB;QACtBtC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAqC,iBAAiB;QACfvC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAsC,iBAAiB;QACfxC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAuC,mBAAmB;QACjBzC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAwC,aAAa;QACX1C,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAyC,gBAAgB;QACd3C,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA0C,SAAS;QACP5C,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA2C,KAAK;QACH7C,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA4C,gBAAgB;QACd9C,MAAM;QACNC,O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"}
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/docs-help.generated.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// GENERATED FILE — do not edit. Regenerate with:\n// node tools/scripts/generate-docs-help.mjs\n// Source of truth: apps/docs/docs frontmatter + headings (AGL-602).\n\n// The first-party plugin consoles live in `libs/plugins/*` and cannot import\n// the console's constants, so they carry their own generated subset of the\n// docs help registry — the topics their cards actually link to, and no others.\n\nexport interface PluginDocsTopic {\n /** Docs-site path, e.g. `/commerce-and-bookings/commerce/overview`. */\n path: string\n /** Docs page title. */\n title: string\n /** Verbatim docs frontmatter description — the tooltip excerpt. */\n excerpt: string\n}\n\nexport const PLUGIN_DOCS = {\n abuseReports: {\n path: '/staff-console/abuse-reports',\n title: 'Abuse reports',\n excerpt: 'The public abuse-report queue — where outside reports land, how to triage by severity, which lever answers which report, and the CSAM and DMCA paths that are not takedown buttons.',\n },\n actionsBuilder: {\n path: '/marketing-and-automation/workflows-and-actions/actions-builder',\n title: 'Actions builder',\n excerpt: 'Map a single event to a single action without building a full workflow.',\n },\n aglynAssist: {\n path: '/getting-started/aglyn-assist',\n title: 'Aglyn Assist',\n excerpt: 'Ask the built-in AI helper how to do anything in Aglyn — it answers from these docs and links you straight to the right page.',\n },\n aiAutomations: {\n path: '/ai/automations-with-ai',\n title: 'Explain automations with AI',\n excerpt: 'Aglyn AI can explain an automation you already have and tell you why one of its runs failed. Drafting an automation from a description is not available yet.',\n },\n aiCrm: {\n path: '/ai/crm-by-ai',\n title: 'The AI CRM built into Aglyn',\n excerpt: 'An AI CRM for small business: a short summary and a suggested next step on a contact, company, deal or lead, a one-to-one email drafted into the composer, and an import\\'s columns matched to fields.',\n },\n aiExperiments: {\n path: '/ai/ab-tests-with-ai',\n title: 'A/B tests by AI: write variants, read the result',\n excerpt: 'Have Aglyn AI write two to four variants for a screen, section or email experiment, and put a finished test into plain language — with the verdict decided from the counts before the model is asked anything.',\n },\n aiMonitoring: {\n path: '/staff-console/ai-monitoring',\n title: 'AI monitoring',\n excerpt: 'How staff watch one organization\\'s AI usage — the add-on, credit pool, overage, refusals, jobs, tokens and cache hit rate, top spenders and margin — and where those figures appear across the staff console.',\n },\n aiProducts: {\n path: '/ai/products-with-ai',\n title: 'Product copy and catalogs with AI',\n excerpt: 'An AI product description generator for your catalog: write copy for one product or a whole import, propose a first catalog, and propose categories and discounts — all as proposals you review.',\n },\n aiSeo: {\n path: '/building-sites/seo/seo-by-ai',\n title: 'AI SEO for your website',\n excerpt: 'AI SEO for websites, on the pages you already have: have AI write a page or product\\'s search listing, audit every published page with a proposed fix for each finding, and draft your structured data and /llms.txt.',\n },\n aiThemes: {\n path: '/ai/theme-assist',\n title: 'Change your site\\'s theme with AI',\n excerpt: 'Describe a change to your site\\'s theme and get a proposal for the theme editor\\'s own controls, previewed before and after, that you save in the editor. Rolling out.',\n },\n assistSignals: {\n path: '/staff-console/assist-signals',\n title: 'Assist Signal',\n excerpt: 'The docs-gap and cost board behind Assist — how the ranking is ordered, why ungrounded questions are counted separately, and what the cache-read rate says about margin.',\n },\n billing: {\n path: '/workspace-and-billing/billing-and-plans/overview',\n title: 'Billing & Plans',\n excerpt: 'How Aglyn\\'s tiers, entitlements, quotas, usage meters, and seat add-ons work.',\n },\n bindings: {\n path: '/building-sites/bindings/overview',\n title: 'Bindings, Variables & Functions',\n excerpt: 'Live values in your content — typed variables, no-code functions, and rename-safe id tokens.',\n },\n bookings: {\n path: '/commerce-and-bookings/bookings/overview',\n title: 'Bookings & Scheduling',\n excerpt: 'Offer services with availability, let visitors book, take payment, and send reminders.',\n },\n buildAWorkflow: {\n path: '/marketing-and-automation/workflows-and-actions/build-a-workflow',\n title: 'Build a workflow',\n excerpt: 'Create a multi-step workflow that runs when a site event fires.',\n },\n catalog: {\n path: '/commerce-and-bookings/commerce/catalog',\n title: 'Product catalog',\n excerpt: 'Products with options and variants, categories, tags, and manual or smart collections.',\n },\n commerce: {\n path: '/commerce-and-bookings/commerce/overview',\n title: 'Commerce',\n excerpt: 'Sell physical, digital, and service products with a full catalog, orders pipeline, shipping, taxes, and your own Stripe account.',\n },\n commerceEndToEnd: {\n path: '/guides/commerce-end-to-end',\n title: 'Commerce end to end',\n excerpt: 'Create products with billing modes, design the storefront with Commerce blocks, take Stripe checkout for one-time and subscription sales, and run orders from the console.',\n },\n companies: {\n path: '/content-and-data/crm/companies',\n title: 'Companies',\n excerpt: 'Group your contacts under the businesses they belong to — one record per company, with its domain, owner, address and the people who work there.',\n },\n consoleTour: {\n path: '/getting-started/console-tour',\n title: 'The console tour',\n excerpt: 'Where things live in the Aglyn console app bar and navigation.',\n },\n contactActivities: {\n path: '/content-and-data/crm/activities',\n title: 'Activities & the timeline',\n excerpt: 'Log calls, emails, meetings and notes against a contact, a company, a deal or a lead, email one person from their record, and read it all in one timeline beside everything captured and every campaign sent.',\n },\n contactFields: {\n path: '/content-and-data/crm/custom-fields',\n title: 'Custom fields',\n excerpt: 'Define your own properties on contacts, companies, deals and leads — text, number, date, choice, checkbox or link — show them on every record and its list, and save form answers straight into a contact\\'s.',\n },\n contactRecord: {\n path: '/content-and-data/crm/contact-record',\n title: 'The contact record',\n excerpt: 'Add a contact by hand, keep a profile on them — phone, company, title, address, owner and lifecycle stage — open their own page in the CRM, and merge two records that turn out to be one person.',\n },\n contacts: {\n path: '/content-and-data/crm/overview',\n title: 'CRM',\n excerpt: 'One place for the people who interact with your sites — contacts captured from forms, members, orders and bookings, with leads, companies, a deals pipeline, tasks, a timeline, reports and custom fields.',\n },\n crmEmailTemplates: {\n path: '/content-and-data/crm/email-templates',\n title: 'Email templates',\n excerpt: 'Keep the letters your team sends from a record — templates that fill in the subject and the message, snippets that drop in a paragraph, and merge fields that fill in the person\\'s name, their company, the deal and you.',\n },\n crmLeads: {\n path: '/content-and-data/crm/leads',\n title: 'Leads',\n excerpt: 'Work the people your site has captured — a status, an owner and notes on every lead — and convert one into a contact, a company and a deal.',\n },\n crmReports: {\n path: '/content-and-data/crm/reports',\n title: 'Reports',\n excerpt: 'New contacts, where they came from and which sources convert, the lead funnel, the open pipeline and its forecast, won and lost, who logged what, and the task load — counted on the server, every table exportable as CSV.',\n },\n crmSettings: {\n path: '/content-and-data/crm/settings',\n title: 'CRM settings',\n excerpt: 'What the CRM does on its own for every site — whether a company is created from a contact\\'s work email domain, who a new contact is assigned to, the address that files replies on a record, and each site\\'s recipes.',\n },\n crmTasks: {\n path: '/content-and-data/crm/tasks',\n title: 'Tasks & follow-ups',\n excerpt: 'Calls, emails, meetings and to-dos with a due date, an assignee and a link to the record they are for — overdue and today read off the clock, a snooze, a reminder at its own time, and a morning digest.',\n },\n crmViews: {\n path: '/content-and-data/crm/views',\n title: 'Saved views',\n excerpt: 'Keep a CRM list\\'s filters, columns and sort under a name, open it from the views menu or a link, share it with the team, and use a contacts view as an email audience.',\n },\n datasets: {\n path: '/content-and-data/datasets/overview',\n title: 'Datasets & Dynamic Content',\n excerpt: 'Model structured content with typed fields and relations, then bind it into repeatable components.',\n },\n deals: {\n path: '/content-and-data/crm/deals',\n title: 'Deals pipeline',\n excerpt: 'Every open deal by stage — with an amount, an owner and an expected close — as a board you drag across or a table you page through, and the won and lost history behind it.',\n },\n designedEmails: {\n path: '/marketing-and-automation/email-campaigns/designed-emails',\n title: 'Designed emails',\n excerpt: 'Build campaign emails in the besigner with email-safe blocks and merge tokens — no separate editor.',\n },\n emailCampaigns: {\n path: '/marketing-and-automation/email-campaigns/overview',\n title: 'Email Campaigns',\n excerpt: 'Campaigns to audiences built from your contacts. Campaign email starts at Pro and needs a sending domain of the site\\'s own; receipts and account email send on every plan.',\n },\n events: {\n path: '/content-and-data/events/overview',\n title: 'Events Calendar',\n excerpt: 'Keep a schedule of events in the console and publish the ones you choose to any screen, with search-engine event markup.',\n },\n forms: {\n path: '/content-and-data/forms/overview',\n title: 'Forms & Lead Capture',\n excerpt: 'Add forms to your site, collect submissions in an inbox, and write them into datasets.',\n },\n installYourFirstPlugin: {\n path: '/guides/install-your-first-plugin',\n title: 'Install your first marketplace item',\n excerpt: 'A click-by-click walkthrough of the Marketplace — find something, choose which sites get it, install it, and turn it off again.',\n },\n inviteTeammates: {\n path: '/workspace-and-billing/teams-and-roles/invite-teammates',\n title: 'Invite teammates',\n excerpt: 'Add people to your site and understand how team members act within your organization.',\n },\n manifestAndEnvs: {\n path: '/developers/plugins/reference/manifest-and-envs',\n title: 'Manifests, trust lifecycle & environment',\n excerpt: 'The plugin manifest schema, the marketplace listing/version documents, the trust state machine, and every PLUGIN_* environment variable.',\n },\n marketingOverlays: {\n path: '/marketing-and-automation/marketing-overlays/overview',\n title: 'Marketing Overlays',\n excerpt: 'Site-wide announcement bars and promotional popups with triggers, scheduling, and email capture.',\n },\n membersOnly: {\n path: '/workspace-and-billing/teams-and-roles/members-only',\n title: 'Members-only areas',\n excerpt: 'Let visitors sign up as members and gate screens so only members can view them.',\n },\n orgAutomations: {\n path: '/marketing-and-automation/workflows-and-actions/org-automations',\n title: 'Org automations',\n excerpt: 'Write an automation once for the whole organization and run it on the sites you choose, with a pause on each site.',\n },\n plugins: {\n path: '/developers/plugins/overview',\n title: 'Plugins & Marketplace',\n excerpt: 'Extend Aglyn with sandboxed plugins — install from the marketplace, configure them, and publish your own.',\n },\n pos: {\n path: '/commerce-and-bookings/commerce/pos-and-reservations',\n title: 'POS & reservations',\n excerpt: 'Sell in person from the console register and take date-range reservations with deposits.',\n },\n publishAPlugin: {\n path: '/developers/plugins/publish-a-plugin',\n title: 'Publish a plugin',\n excerpt: 'Ship your own plugin to the marketplace with version pinning.',\n },\n publisherHandbook: {\n path: '/developers/plugins/publishing/publisher-handbook',\n title: 'Publisher handbook',\n excerpt: 'Publishing to the Aglyn marketplace — from profile setup through listing authoring, review, updates, and getting paid.',\n },\n redirects: {\n path: '/building-sites/redirects/overview',\n title: 'Redirects',\n excerpt: 'Manage URL redirects with validation, loop detection, and hit metrics.',\n },\n sandboxSecurity: {\n path: '/developers/plugins/reference/sandbox-security',\n title: 'Sandbox security model',\n excerpt: 'How sandboxed marketplace plugins are isolated — separate origin, per-manifest CSP, pinned artifacts — and what that means when you write one.',\n },\n sequences: {\n path: '/content-and-data/crm/sequences',\n title: 'Sequences',\n excerpt: 'One-to-one email sequences a rep sends to a person from their own connected mailbox, kept with the CRM. Rolling out.',\n },\n webhooks: {\n path: '/marketing-and-automation/workflows-and-actions/webhooks',\n title: 'Webhooks',\n excerpt: 'Connect Aglyn to other systems with outbound and inbound webhooks.',\n },\n} as const satisfies Record<string, PluginDocsTopic>\n\nexport type PluginDocsKey = keyof typeof PLUGIN_DOCS\n\nexport const PLUGIN_DOCS_ANCHORS = {\n abuseReports: ['#fraud-and-risk-alerts-by-email', '#where-reports-come-from', '#held-outbound-email', '#what-is-screened', '#tiers', '#deciding-a-held-row', '#names-and-domains', '#stripe-fraud-signals', '#seller-fraud-pattern', '#card-testing-velocity', '#marketplace', '#risk-notices', '#triage-by-severity', '#csam', '#which-lever', '#statuses', '#disclosure', '#dmca', '#counter-notices', '#counter-notice-clock', '#counter-notice-steps', '#repeat-infringers', '#repeat-infringer-threshold', '#known-gaps', '#related'],\n actionsBuilder: ['#create-an-action', '#recipes', '#describe-it', '#triggers', '#crm-events', '#only-run-when-a-field-matches', '#chain-multiple-conditions-andor', '#steps', '#crm-steps', '#step-conditions', '#sequences', '#run-history', '#what-is-and-isnt-recorded', '#interactions-from-the-besigner', '#when-to-use-which', '#related'],\n aglynAssist: ['#what-it-can-do', '#aglyn-ai', '#answers-for-beginners-and-developers', '#offers-to-open-a-page', '#edits-in-the-besigner', '#where-an-answer-came-from', '#answers-straight-from-the-documentation', '#message-limits', '#feedback', '#privacy'],\n aiAutomations: ['#draft', '#explain', '#why-a-run-failed', '#what-is-sent', '#who-can-use-it', '#related'],\n aiCrm: ['#summarize-a-record', '#summaries-are-reused-until-the-record-changes', '#draft-an-email', '#match-columns', '#what-is-sent', '#who-can-use-it', '#related'],\n aiExperiments: ['#it-proposes-you-write', '#write-variants', '#putting-them-in', '#what-it-will-not-write', '#read-a-result', '#the-verdict', '#the-words', '#undecided', '#what-is-sent', '#who-can-use-it', '#related'],\n aiMonitoring: ['#the-ai-card', '#where-else', '#one-account', '#the-spend-leaderboard', '#alerts', '#related'],\n aiProducts: ['#write-a-products-copy', '#write-copy-for-many-products', '#when-you-import-products', '#propose-a-first-catalog', '#propose-categories-and-discounts', '#what-the-copy-never-says', '#what-is-sent-to-the-ai-provider', '#who-can-use-it', '#related'],\n aiSeo: ['#write-a-pages-listing', '#write-a-products-listing', '#audit-the-whole-site', '#target-keywords', '#apply-all-as-drafts', '#structured-data-and-llmstxt', '#related'],\n aiThemes: ['#change-what-you-describe-or-design-a-new-theme', '#match-your-brand', '#review-the-proposal', '#save-it-or-dont', '#who-can-use-it', '#related'],\n assistSignals: ['#the-workflow-this-board-exists-for', '#fleet', '#the-cache-read-rate-and-what-a-bad-number-looks-like', '#where-the-money-goes', '#tokens-by-kind', '#docs-gaps', '#questions-the-docs-could-not-answer', '#what-people-actually-asked', '#what-assist-costs-by-workspace', '#reading-the-sample-honestly', '#related'],\n billing: ['#tiers--entitlements', '#enterprise', '#single-sign-on-and-enforcement', '#usage-meters', '#who-is-generating-what', '#ai-allotments', '#storage-overage', '#if-you-would-rather-uploads-stopped', '#assist-overage', '#stop-ai-assist-at-the-included-band', '#ai-overage-ceiling', '#free-ai-credits', '#ai-credit-alerts', '#usage-budget', '#seats', '#crm-records', '#the-crm-suite', '#one-to-one-email', '#organization-data', '#api-access', '#payments', '#outstanding', '#plan-total', '#billing-email', '#payment-methods', '#billing-address', '#tax-ids', '#sales-tax', '#platform-fees', '#related'],\n bindings: ['#binding-tokens', '#rename-safe-id-tokens', '#insert-a-variable', '#token-pills', '#in-the-canvas-text-editor', '#site-details', '#typed-variables', '#no-code-functions', '#parameters-a-visitor-can-answer', '#a-calculator-you-lay-out', '#where-used--safety', '#workflows', '#related'],\n bookings: ['#set-up-bookings', '#taking-bookings', '#reminders', '#payments-and-fees', '#service-tax', '#manage', '#booking-from-the-crm', '#canceling-and-refunding', '#related'],\n buildAWorkflow: ['#1-open-the-workflows-page', '#2-choose-a-trigger', '#3-add-steps', '#waiting', '#4-save-and-test', '#duplicate-a-workflow', '#tips', '#related'],\n catalog: ['#products-options-and-variants', '#billing-modes-and-subscriptions', '#ai', '#categories-and-tags', '#collections', '#slugs', '#merchant-center-feed', '#related'],\n commerce: ['#products-hub', '#inventory', '#reserved-stock', '#stock-movements', '#gift-cards', '#recovery-and-alerts', '#orders', '#orders-screen', '#order-statuses', '#order-money-tiles', '#a-lost-dispute', '#shipping--taxes', '#lodging-tax-on-reservations', '#storefront-sales-tax', '#destination-coverage', '#dropshipping', '#related'],\n commerceEndToEnd: ['#1-connect-payments', '#2-create-products', '#3-design-the-storefront', '#catalog-search-filters-and-sort', '#category-pages', '#the-product-page-template', '#4-what-checkout-does', '#paying-without-leaving-your-site', '#5-run-orders-from-the-console', '#6-subscriptions--the-stripe-portal', '#related'],\n companies: ['#the-companies-list', '#create-a-company', '#a-companys-page', '#contacts-at-a-company', '#linked-on-capture', '#import-from-csv', '#export-csv', '#deleting-a-company', '#who-can-see-a-company', '#files', '#related'],\n consoleTour: ['#the-app-bar', '#in-context-help', '#filter-and-search', '#primary-navigation', '#editing-vs-managing', '#the-sites-list', '#the-status-pill', '#how-the-pill-is-decided', '#your-site-allowance', '#a-sites-dashboard', '#next', '#workspace-settings--notifications', '#the-notifications-feed', '#notification-settings', '#one-kind-at-a-time', '#workspace-and-site-overrides', '#daily-digests', '#alerts-on-this-device'],\n contactActivities: ['#four-kinds-of-history', '#reading-the-timeline', '#campaign-email', '#logging-an-activity', '#meeting-from-a-booking', '#click-to-call', '#sending-an-email', '#delivery-states', '#captured-email', '#where-an-activity-is-visible', '#the-recent-activity-feed', '#related'],\n contactFields: ['#define-a-field', '#fields-per-record', '#where-values-show', '#save-a-form-field', '#lead-source-values', '#over-the-api', '#retire-restore-delete', '#recompute-next-activity', '#related'],\n contactRecord: ['#adding-a-contact-by-hand', '#the-record-page', '#deleting-and-erasing', '#what-each-site-keeps-to-itself', '#when-sites-join-or-leave-a-group', '#merging-two-records', '#likely-duplicates', '#owner', '#last-engaged', '#lifecycle-stages', '#where-the-persons-lead-is', '#finding-a-contact', '#files', '#related'],\n contacts: ['#whats-in-the-crm-area', '#unified-ingestion', '#what-each-plan-includes', '#the-contacts-page', '#import-from-csv', '#segments', '#everywhere-the-crm-shows-up', '#capture-replies', '#at-the-organization-level', '#who-can-open-the-crm', '#one-sender-one-crm', '#related'],\n crmEmailTemplates: ['#templates-and-snippets', '#merge-fields', '#saving', '#managing-templates', '#duplicate-a-template', '#shared-or-personal', '#over-the-rest-api', '#related'],\n crmLeads: ['#what-makes-a-lead', '#what-a-lead-holds', '#adding-a-lead-by-hand', '#the-leads-list', '#filter-the-leads', '#working-a-lead-from-the-row', '#several-leads-at-once', '#export-csv', '#import-from-csv', '#who-owns-a-lead', '#a-leads-page', '#email-state', '#converting-a-lead', '#unqualifying-a-lead', '#erasing-the-person', '#who-can-do-this', '#related'],\n crmReports: ['#choosing-a-period', '#contacts', '#sources-and-lifecycle', '#conversion-by-source', '#lead-funnel', '#pipeline', '#forecast-by-close-month', '#won-and-lost', '#won-and-lost-by-owner', '#activity-by-teammate', '#tasks', '#exporting-a-table', '#crm-at-a-glance', '#how-the-numbers-are-counted', '#related'],\n crmSettings: ['#companies', '#create-companies-from-work-email-domains', '#default-owner', '#assignment-rules', '#round-robin', '#email-templates', '#email-capture', '#your-sending-addresses', '#recipes', '#related'],\n crmTasks: ['#the-tasks-page', '#the-calendar-view', '#snoozing-a-task', '#selecting-exporting-and-acting-on-many', '#import-from-csv', '#creating-a-task', '#assigning-a-task-to-someone-else', '#completing-and-reopening', '#organization-tasks', '#tasks-on-a-contact-company-or-deal', '#reminders', '#turning-reminders-off', '#next-activity', '#the-daily-digest', '#turning-it-off', '#the-dashboard-card', '#who-can-do-what', '#related'],\n crmViews: ['#the-views-control', '#a-view-is-a-link', '#filters', '#filters-on-the-contacts-list', '#filters-on-the-other-lists', '#columns-and-sort', '#segments-and-views', '#who-sees-what', '#related'],\n datasets: ['#model-builder', '#typed-documents', '#filter-records', '#relations', '#query-layer', '#repeatable-components', '#who-a-dataset-is-shared-with', '#import--export', '#related'],\n deals: ['#pipelines', '#stages', '#the-board-and-the-table', '#import-from-csv', '#creating-a-deal', '#line-items', '#moving-winning-and-losing', '#a-won-deal-makes-its-contact-a-customer', '#a-deals-page', '#files', '#related'],\n designedEmails: ['#create-a-template', '#find-a-template', '#duplicate-a-template', '#styling-email-blocks', '#merge-tokens', '#send-it', '#the-plain-text-version', '#start-from-a-brief-instead'],\n emailCampaigns: ['#send-a-campaign', '#campaigns-belong-to-the-organization', '#organization-emails-page', '#campaigns-group-emails', '#filter-the-lists', '#what-belongs-to-a-campaign', '#who-the-email-comes-from', '#sending-domains', '#account-email-always-sends', '#marketing-needs-a-domain', '#two-ways-to-get-a-domain', '#a-domain-we-set-up-is-a-request', '#domain-states', '#senders', '#send-a-test', '#preview-the-email', '#monthly-send-cap', '#personalize-with-merge-tags', '#recipient-count', '#who-a-campaign-is-allowed-to-reach', '#schedule-a-send', '#held-for-review', '#duplicate-an-email', '#email-lists', '#manual-lists', '#list-members', '#add-to-a-list', '#import-a-list', '#remove-from-a-list', '#lists-built-from-a-rule', '#experiments', '#experiments-across-sites', '#opens--clicks', '#the-campaign-report', '#per-contact-engagement', '#which-links-were-clicked', '#revenue-from-a-campaign', '#conversions', '#compliance', '#list-unsubscribe', '#topics', '#preference-page', '#consent-groups', '#consent-group-create', '#consent-group-join', '#consent-group-leave', '#consent-group-rename', '#consent-group-progress', '#frequency-opt-down', '#double-opt-in', '#consent-group-confirmation', '#marketing-mail', '#frequency-cap', '#suppressions', '#add-a-suppression', '#platform-suppressions', '#related'],\n events: ['#manage-events', '#show-events-on-a-screen', '#search-engines', '#related'],\n forms: ['#reading-submissions-from-code', '#build-a-form', '#monthly-allowance-per-plan', '#spam-and-abuse-protection', '#the-per-site-monthly-ceiling', '#field-types', '#labels-and-placeholders', '#example-a-quick-survey', '#after-submit', '#example-grow-an-email-list-from-a-signup-form', '#consent-group-disclosure', '#where-submissions-go', '#the-inbox', '#filter-the-inbox', '#who-a-submission-is-from', '#where-this-one-went', '#replying-to-a-submission', '#every-sites-inbox-at-once', '#one-forms-own-page', '#find-a-form', '#duplicate-a-form', '#switch-forms-off-for-one-site', '#related'],\n installYourFirstPlugin: ['#before-you-start', '#step-1-open', '#step-2-browse', '#step-3-reviews', '#step-4-targeting', '#step-5-install', '#step-6-use', '#step-7-off', '#what-to-do-next', '#related'],\n inviteTeammates: ['#invite-someone', '#pending-invites', '#who-gets-told', '#accepting-an-invite', '#an-invitation-never-changes-who-owns-the-workspace', '#how-team-members-act', '#you-are-a-site-collaborators-support-channel', '#help-a-teammate-who-is-locked-out', '#why-you-cant-always-set-a-password', '#activity-log', '#ai-actions', '#ai-usage', '#ai-allotment', '#tips', '#related'],\n manifestAndEnvs: ['#plugin-manifest-published-with-every-version', '#contributes--where-the-plugin-loads', '#config--settings-without-writing-a-settings-screen', '#listing--version-documents', '#review--trust-lifecycle', '#environment-variables', '#pluginsconfigjson-first-party-contributors'],\n marketingOverlays: ['#announcement-bar', '#promotional-popups', '#frequency', '#popup-v2', '#multiple-overlays-scheduling--page-targeting', '#variables-in-copy', '#engagement-stats', '#across-your-sites', '#related'],\n membersOnly: ['#let-visitors-sign-up', '#sign-in-sign-up-and-recovery-pages', '#forgotten-passwords', '#gate-a-screen', '#manage-your-members', '#suspend-or-reactivate-a-member', '#tips', '#related'],\n orgAutomations: ['#what-an-org-automation-is', '#create-one', '#triggers', '#steps', '#pause-it-on-one-site', '#waiting-switching-off-and-deleting', '#every-sites-own-automations', '#related'],\n plugins: ['#install--upgrade', '#browse-card', '#whats-included', '#what-the-badges-on-a-listing-mean', '#how-plugins-run', '#when-one-plugin-depends-on-another', '#a-dependency-that-is-off-for-one-site', '#configure', '#configure-site', '#publish-your-own', '#related'],\n pos: ['#registers', '#the-register', '#platform-fees-at-the-register', '#selling-past-the-count', '#when-something-disconnects', '#reservations', '#related'],\n publishAPlugin: ['#the-publish-pipeline', '#private-plugins', '#paid-listings', '#your-publisher-profile', '#tips', '#related'],\n publisherHandbook: ['#before-your-first-publish', '#the-publisher-agreement', '#where-to-publish-from', '#what-installing-each-type-does', '#rules-an-email-starter-has-to-meet', '#publishing-a-version', '#before-you-publish', '#review-what-happens-after-you-publish', '#the-two-badges-and-what-each-one-promises', '#asking-to-be-verified', '#testing-a-version-before-it-is-approved', '#watching-your-own-submission', '#disabled-versions', '#private-plugins', '#authoring-your-listing', '#what-your-listing-can-say-about-aglyn', '#versioning--updates', '#shipping-a-new-version', '#how-installs-work-the-buyer-side', '#getting-paid', '#low-prices-and-processing'],\n redirects: ['#manage-redirects', '#sending-visitors-to-another-site', '#metrics', '#match-modes-v2', '#related'],\n sandboxSecurity: ['#a-separate-origin', '#per-manifest-network-policy', '#when-you-cant-declare-the-origin', '#pinned-immutable-artifacts', '#what-this-means-when-you-build', '#related'],\n sequences: ['#what-it-is-for', '#sent-from-your-own-mailbox', '#where-it-lives', '#connect-a-mailbox', '#send-as', '#daily-cap', '#mailbox-status', '#auto-pause', '#mailbox-actions', '#link-domains', '#compliance-settings', '#allowed-countries', '#do-not-contact-domains', '#sequences', '#build-a-sequence', '#send-a-test', '#sequence-status', '#enroll', '#mail-gateways', '#cold-contacts', '#enrollments', '#person-history', '#curate', '#sending', '#what-stops-a-sequence', '#unsubscribe', '#related'],\n webhooks: ['#outbound-webhooks', '#inbound-webhooks', '#tips', '#related'],\n} as const satisfies Partial<Record<PluginDocsKey, readonly `#${string}`[]>>\n\ntype PluginAnchorMap = typeof PLUGIN_DOCS_ANCHORS\n\n/** Valid heading anchors for a plugin docs page (`never` when none). */\nexport type PluginDocsAnchor<K extends PluginDocsKey> =\n K extends keyof PluginAnchorMap ? PluginAnchorMap[K][number] : never\n"],"names":["PLUGIN_DOCS","abuseReports","path","title","excerpt","actionsBuilder","aglynAssist","aiAutomations","aiCrm","aiExperiments","aiMonitoring","aiProducts","aiSeo","aiThemes","assistSignals","billing","bindings","bookings","buildAWorkflow","catalog","commerce","commerceEndToEnd","companies","consoleTour","contactActivities","contactFields","contactRecord","contacts","crmEmailTemplates","crmLeads","crmReports","crmSettings","crmTasks","crmViews","datasets","deals","designedEmails","emailCampaigns","events","forms","installYourFirstPlugin","inviteTeammates","manifestAndEnvs","marketingOverlays","membersOnly","orgAutomations","plugins","pos","publishAPlugin","publisherHandbook","redirects","sandboxSecurity","sequences","webhooks","PLUGIN_DOCS_ANCHORS"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD,iDAAiD;AACjD,8CAA8C;AAC9C,oEAAoE;AAEpE,6EAA6E;AAC7E,2EAA2E;AAC3E,+EAA+E;AAW/E,OAAO,MAAMA,cAAc;IACzBC,cAAc;QACZC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAC,gBAAgB;QACdH,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAE,aAAa;QACXJ,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAG,eAAe;QACbL,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAI,OAAO;QACLN,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAK,eAAe;QACbP,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAM,cAAc;QACZR,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAO,YAAY;QACVT,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAQ,OAAO;QACLV,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAS,UAAU;QACRX,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAU,eAAe;QACbZ,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAW,SAAS;QACPb,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAY,UAAU;QACRd,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAa,UAAU;QACRf,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAc,gBAAgB;QACdhB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAe,SAAS;QACPjB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAgB,UAAU;QACRlB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAiB,kBAAkB;QAChBnB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAkB,WAAW;QACTpB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAmB,aAAa;QACXrB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAoB,mBAAmB;QACjBtB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAqB,eAAe;QACbvB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAsB,eAAe;QACbxB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAuB,UAAU;QACRzB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAwB,mBAAmB;QACjB1B,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAyB,UAAU;QACR3B,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA0B,YAAY;QACV5B,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA2B,aAAa;QACX7B,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA4B,UAAU;QACR9B,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA6B,UAAU;QACR/B,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA8B,UAAU;QACRhC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA+B,OAAO;QACLjC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAgC,gBAAgB;QACdlC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAiC,gBAAgB;QACdnC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAkC,QAAQ;QACNpC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAmC,OAAO;QACLrC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAoC,wBAAwB;QACtBtC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAqC,iBAAiB;QACfvC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAsC,iBAAiB;QACfxC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAuC,mBAAmB;QACjBzC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAwC,aAAa;QACX1C,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAyC,gBAAgB;QACd3C,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA0C,SAAS;QACP5C,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA2C,KAAK;QACH7C,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA4C,gBAAgB;QACd9C,MAAM;QACNC,O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"}
@@ -3404,7 +3404,7 @@ export const ASSIST_DOCS_INDEX = [
3404
3404
  title: 'Activities & the timeline',
3405
3405
  heading: 'Sending an email',
3406
3406
  anchor: '#sending-an-email',
3407
- text: 'A contact\'s, a lead\'s and a deal\'s page each carry Send email: one message, from you, to the person the record is about. It is a letter, not a campaign — there is no audience, no designed layout and no unsubscribe footer — and it is logged on the timeline the moment it leaves, with its delivery state following. One-to-one email is part of the CRM, included from Starter; on Free the record pages that send it are locked with the rest of the CRM.\n| Field | What it is |\n| To | The record\'s own address. It cannot be changed here: a deal writes to its contact, a lead to the address it was captured with. |\n| From | The site\'s sending address — the same identity campaigns and transactional mail leave on — with your name in front of it. |\n| Reply-to | Your own address, so a reply lands in your inbox rather than the site\'s mailbox. |\n| Template | A saved letter to start from — fills in the subject and the message. Shown once the workspace has one; see Email templates & snippets. |\n| Subject and Message | What you write. The message is plain text; a blank line starts a new paragraph. Insert drops a snippet or a merge field — {{contact.firstName}}, {{deal.amount}} — at the cursor, filled in from the record when the email is sent, and Save as template… keeps the draft for next time. |\nThe send is refused, and the dialog says why, when:\n- the site has no sending identity in effect — an admin sets one up under Emails › Sending, and the dialog links there;\n- the address is on a suppression list because it bounced or reported a message as spam, or the person has asked this site not to email them;\n- the organization has reached its one-to-one email allowance for the day — see the cap, which resets at midnight UTC;\n- you have sent more than twenty emails in the last minute.\nEvery sent ema'
3407
+ text: 'A contact\'s, a lead\'s and a deal\'s page each carry Send email: one message, from you, to the person the record is about. It is a letter, not a campaign — there is no audience, no designed layout and no unsubscribe footer — and it is logged on the timeline the moment it leaves, with its delivery state following. One-to-one email is part of the CRM, included from Starter; on Free the record pages that send it are locked with the rest of the CRM.\n| Field | What it is |\n| To | The record\'s own address. It cannot be changed here: a deal writes to its contact, a lead to the address it was captured with. |\n| From | The site\'s sending address — the same identity campaigns and transactional mail leave on — with your name in front of it. |\n| Reply-to | Your own address, so a reply lands in your inbox rather than the site\'s mailbox. |\n| Template | A saved letter to start from — fills in the subject and the message. Shown once the workspace has one; see Email templates & snippets. |\n| Subject and Message | What you write. The message is plain text; a blank line starts a new paragraph. Insert drops a snippet or a merge field — {{contact.firstName}}, {{deal.amount}} — at the cursor, filled in from the record when the email is sent, and Save as template… keeps the draft for next time. |\nThe send is refused, and the dialog says why, when:\n- the site has no sending identity in effect — an admin sets one up under Emails › Sending, and the dialog links there;\n- the address is on a suppression list because it bounced or reported a message as spam, or the person has asked this site not to email them;\n- the organization has reached its one-to-one email allowance for the day — see the cap, which resets at midnight UTC;\n- you have sent more than twenty emails in the last minute;\n- the address\''
3408
3408
  },
3409
3409
  {
3410
3410
  path: '/content-and-data/crm/activities',
@@ -3677,7 +3677,7 @@ export const ASSIST_DOCS_INDEX = [
3677
3677
  title: 'The contact record',
3678
3678
  heading: 'The record page',
3679
3679
  anchor: '#the-record-page',
3680
- text: 'Clicking a row in the list opens the person\'s own page at …/crm/contacts/{id} — an address you can paste and that every other CRM record links to. The page opens with the person\'s name, their lifecycle stage, their owner, when they last engaged with one of your campaigns, and — for a buyer — how many orders they have placed and what they have spent; the order count is a link to the site\'s orders list narrowed to the person\'s address. When a sender has given a verdict on the address — a bounce, a gateway block, an unsubscribe, a spam complaint, a do-not-contact mark — it leads the chip row, with the date and the server\'s words on hover, and Send email is disabled with the reason; the Contacts list carries the same verdict in its Email column and filter. It is the lead\'s email state, on the contact the lead became or was always. Beside Send email it carries Call, which dials the person\'s number, and Log a call — see click to call. Under it, one card per aspect of the record:\n- Properties — the editable profile: name, phone (with a dial button beside it, see click to call), job title, company, lifecycle stage, owner, tags, address and an About box for your team\'s notes, then under More fields one control per custom field your organization has defined. One Save, in the card\'s header, writes everything at once, and Discard changes beside it puts back what you had not saved. The email is shown but cannot be edited here, because it is the shared identity. The Company field is the same picker the New contact drawer has: search the companies your site may see, create one by typing its name, or clear the field to unlink the person. A record that carries a company name with no link — an import, or a save from before the picker — shows the name as a note beneath the field, with one'
3680
+ text: 'Clicking a row in the list opens the person\'s own page at …/crm/contacts/{id} — an address you can paste and that every other CRM record links to. The page opens with the person\'s name, their lifecycle stage, their owner, when they last engaged with one of your campaigns, and — for a buyer — how many orders they have placed and what they have spent; the order count is a link to the site\'s orders list narrowed to the person\'s address. When a sender has given a verdict on the address — a bounce, a gateway block, an unsubscribe, a spam complaint, a do-not-contact mark — or the address\'s domain has no mail server (Would bounce), it leads the chip row, with the date and the server\'s words on hover, and Send email is disabled with the reason; the Contacts list carries the same verdict in its Email column and filter. Beside it, a chip names the mail gateway in front of the address, when its domain names one. It is the lead\'s email state, on the contact the lead became or was always. Beside Send email it carries Call, which dials the person\'s number, and Log a call — see click to call. Under it, one card per aspect of the record:\n- Properties — the editable profile: name, phone (with a dial button beside it, see click to call), job title, company, lifecycle stage, owner, tags, address and an About box for your team\'s notes, then under More fields one control per custom field your organization has defined. One Save, in the card\'s header, writes everything at once, and Discard changes beside it puts back what you had not saved. The email is shown but cannot be edited here, because it is the shared identity. The Company field is the same picker the New contact drawer has: search the companies your site may see, create one by typing its name, or clear the field to unlink the person'
3681
3681
  },
3682
3682
  {
3683
3683
  path: '/content-and-data/crm/contact-record',
@@ -3950,7 +3950,7 @@ export const ASSIST_DOCS_INDEX = [
3950
3950
  title: 'Import contacts from CSV',
3951
3951
  heading: 'Three steps',
3952
3952
  anchor: '#three-steps',
3953
- text: '1. Choose the file. A CSV with a header row. Up to 5,000 rows per file — split a larger one and import the pieces. Nothing is sent until you press Import. Download template beside it hands you the export\'s own header over no rows — every field below, plus one column per custom field — so a sheet filled in against it maps itself.\n2. Match the columns. Each column in the file is matched to a contact field. Aglyn proposes a match from the header names (Email, Phone, Company, Tags and their usual variants) and shows the first row\'s value beside each one so you can correct it. Choose Do not import for a column you do not want. The email column is required; every other column is optional. A preview of the first ten rows shows exactly what each mapping produces.\n3. Import. Rows are sent in batches of 200 with a progress bar. When it finishes, the result says how many people were added, how many existing contacts were updated, and how many rows were skipped — with a Download skipped rows button that gives you those rows back as a CSV, with a last column saying why, so you can fix them and import that file again.\nClosing the drawer during an import stops it after the batch in progress. Rows already imported stay imported; run the same file again to finish, and the rows from the first run are reported as updated.'
3953
+ text: '1. Choose the file. A CSV with a header row. Up to 5,000 rows per file — split a larger one and import the pieces. Nothing is sent until you press Import. Download template beside it hands you the export\'s own header over no rows — every field below, plus one column per custom field — so a sheet filled in against it maps itself.\n2. Match the columns. Each column in the file is matched to a contact field. Aglyn proposes a match from the header names (Email, Phone, Company, Tags and their usual variants) and shows the first row\'s value beside each one so you can correct it. Choose Do not import for a column you do not want. The email column is required; every other column is optional. A preview of the first ten rows shows exactly what each mapping produces.\n3. Import. Rows are sent in batches of 200 with a progress bar. When it finishes, the result says how many people were added, how many existing contacts were updated, and how many rows were skipped — with a Download skipped rows button that gives you those rows back as a CSV, with a last column saying why, so you can fix them and import that file again. When some of the imported addresses are on a domain with no mail server, the result says how many: those records read Would bounce (see the lead\'s email state), and campaigns and sequences skip them.\nClosing the drawer during an import stops it after the batch in progress. Rows already imported stay imported; run the same file again to finish, and the rows from the first run are reported as updated.'
3954
3954
  },
3955
3955
  {
3956
3956
  path: '/content-and-data/crm/import',
@@ -4125,7 +4125,7 @@ export const ASSIST_DOCS_INDEX = [
4125
4125
  title: 'CRM',
4126
4126
  heading: 'Related',
4127
4127
  anchor: '#related',
4128
- text: '- The contact record · Leads · Companies · Deals pipeline · Tasks & follow-ups · Reports · Custom fields\n- Forms & lead capture\n- Email campaigns\n- Automations for the CRM\n- CRM by AI: summaries, next steps, email drafts and column matching\n- REST API — contacts, companies, pipelines, deals, tasks and activities'
4128
+ text: '- The contact record · Leads · Companies · Deals pipeline · Tasks & follow-ups · Reports · Custom fields\n- Forms & lead capture\n- Email campaigns\n- Deliverability checks: which addresses would bounce, and why mail to one is not sent\n- Automations for the CRM\n- CRM by AI: summaries, next steps, email drafts and column matching\n- REST API — contacts, companies, pipelines, deals, tasks and activities'
4129
4129
  },
4130
4130
  {
4131
4131
  path: '/content-and-data/crm/reports',
@@ -4342,7 +4342,7 @@ export const ASSIST_DOCS_INDEX = [
4342
4342
  title: 'Sequences',
4343
4343
  heading: 'Related',
4344
4344
  anchor: '#related',
4345
- text: '- CRM\n- Email campaigns\n- Custom roles'
4345
+ text: '- CRM\n- Deliverability checks\n- Email campaigns\n- Custom roles'
4346
4346
  },
4347
4347
  {
4348
4348
  path: '/content-and-data/crm/settings',
@@ -7375,6 +7375,76 @@ export const ASSIST_DOCS_INDEX = [
7375
7375
  anchor: '#related',
7376
7376
  text: '- Google Analytics events\n- SEO toolkit\n- Billing & plans'
7377
7377
  },
7378
+ {
7379
+ path: '/marketing-and-automation/email-campaigns/deliverability-checks',
7380
+ title: 'Deliverability checks',
7381
+ heading: '',
7382
+ anchor: '',
7383
+ text: 'Deliverability checks\nEvery email Aglyn sends is checked before it leaves — campaigns, workflow emails, sequences, one-to-one CRM email, receipts, booking confirmations and account email alike. The checks run on their own, for every organization; there is nothing to switch on.\nThey ask two kinds of question:\n- Can this address receive mail at all? A domain with no mail server bounces every message sent to it, and every bounce counts against the sender\'s reputation.\n- Is the recipient\'s mail gateway refusing this sender? Some companies put a security gateway — Barracuda, Proofpoint, Mimecast — in front of their mail, and a gateway refuses a sender for everyone behind it. The next person at that company bounces the same way.\nThe answers come from public DNS and from what happened to earlier mail, and they are shared: a recipient domain\'s mail servers are looked up once and remembered for a week, so the first organization to write to a domain warms the answer for everyone.'
7384
+ },
7385
+ {
7386
+ path: '/marketing-and-automation/email-campaigns/deliverability-checks',
7387
+ title: 'Deliverability checks',
7388
+ heading: 'Every send: a domain with no mail server',
7389
+ anchor: '#no-mail-server',
7390
+ text: 'Before any message is handed to the mail provider, each recipient\'s domain is checked for a mail server (its MX record). A domain that publishes no MX record, or the null MX record (.) that says "this domain takes no mail", has nowhere to deliver to, so the message is not sent:\n- the address is refused for that send, with the reason;\n- the address is added to the platform suppression list with the reason No mail server, so later mail skips it too;\n- only the address is listed, never the domain — a whole domain is never suppressed on the strength of one lookup.\nThis applies to every email, including account and transactional mail: a message to a domain that cannot receive mail can only bounce.\nA lookup that goes unanswered is treated as unknown, and unknown sends. A "no mail server" answer stops mail only when two separate resolvers agree on it, and an old "no mail server" answer is never reused to stop a send when a fresh lookup cannot be made. A domain with no MX record but an address record still receives mail at that address (the implicit MX), so it is sent to, with a warning where a surface shows one.'
7391
+ },
7392
+ {
7393
+ path: '/marketing-and-automation/email-campaigns/deliverability-checks',
7394
+ title: 'Deliverability checks',
7395
+ heading: 'Bulk mail: the gateway hold',
7396
+ anchor: '#gateway-hold',
7397
+ text: 'What each gateway did with your mail is learned from outcomes. Every delivery is credited to the gateway in front of the recipient, and a hard bounce that reads as the gateway refusing the sender — not an unknown address — counts as a refusal. Both the mail provider\'s bounce reports and a connected mailbox\'s bounce messages feed it.\nWhen a gateway has refused mail from a sending domain twice in the last thirty days and delivered none of it, bulk mail from that sending domain to anyone behind that gateway waits. Bulk mail means:\n- email campaigns;\n- workflow emails;\n- sequences.\nThe hold is keyed by the sending domain, not by your organization or the recipient: the gateway is judging the reputation of the domain in the From: line. Mail from a different sending domain is not held, and a hold on a shared sending domain is visible to Aglyn staff because it affects every site on it. A delivery through the gateway clears the hold, and so does time: once fewer than two refusals remain in the thirty-day window, mail goes again.\nA held recipient is skipped for that send and the reason is recorded. In Sequences, a held enrollment pauses as Held — gateway refused this sender twice and Resume sends it anyway; see mail gateways in Sequences.'
7398
+ },
7399
+ {
7400
+ path: '/marketing-and-automation/email-campaigns/deliverability-checks',
7401
+ title: 'Deliverability checks',
7402
+ heading: 'Transactional mail is never held',
7403
+ anchor: '#transactional-mail',
7404
+ text: 'Mail a person asked for — a password reset, a receipt, a booking confirmation, an account notice — is never held for a gateway. It is only refused when the recipient\'s domain has no mail server at all. A gateway\'s opinion of your campaigns must not stop a customer from resetting their password.'
7405
+ },
7406
+ {
7407
+ path: '/marketing-and-automation/email-campaigns/deliverability-checks',
7408
+ title: 'Deliverability checks',
7409
+ heading: 'When an address arrives',
7410
+ anchor: '#when-an-address-arrives',
7411
+ text: 'Addresses are checked when they enter the workspace, not only when mail is sent to them. A lead or contact created in the CRM, a CSV import, a form submission and an address added to an email list each look the domain up in the background, without slowing the save:\n- an address whose domain takes no mail is marked Would bounce in its email state, and nothing is sent to find out;\n- the gateway in front of the domain is shown beside the address, the same chip Sequences shows.\nThe Leads and Contacts lists filter by Would bounce, so the addresses to fix or remove can be found in one step, and an import reports how many of the addresses it brought in have no mail server.'
7412
+ },
7413
+ {
7414
+ path: '/marketing-and-automation/email-campaigns/deliverability-checks',
7415
+ title: 'Deliverability checks',
7416
+ heading: 'Before a campaign sends',
7417
+ anchor: '#before-a-campaign-sends',
7418
+ text: 'The campaign\'s send review counts the recipients the checks take out and says why, in one line:\n> 3 recipients excluded: 2 no mail server · 1 behind a gateway that refused this sender\nThose recipients are skipped when the campaign sends; everyone else receives it. See see who it will reach.'
7419
+ },
7420
+ {
7421
+ path: '/marketing-and-automation/email-campaigns/deliverability-checks',
7422
+ title: 'Deliverability checks',
7423
+ heading: 'Before a one-to-one email',
7424
+ anchor: '#before-a-one-to-one-email',
7425
+ text: 'The CRM\'s compose window checks the recipient before you send, and warns when the address would bounce or sits behind a gateway that has been refusing your sending domain. It warns rather than blocks for a gateway: a one-to-one email is your call.'
7426
+ },
7427
+ {
7428
+ path: '/marketing-and-automation/email-campaigns/deliverability-checks',
7429
+ title: 'Deliverability checks',
7430
+ heading: 'Sender readiness',
7431
+ anchor: '#sender-readiness',
7432
+ text: 'The recipient\'s side is only half of it. Receivers decide whether mail really comes from your domain by three records you publish:\n| Record | Where it lives | What it does |\n| SPF | The envelope domain | Names the servers allowed to send for the domain. It should name your mail provider and end in all or -all; +all lets anyone send as you. |\n| DKIM | .domainkey. | The public key the message signature is checked against. |\n| DMARC | dmarc. | Says what a receiver does with mail that fails (p=none, quarantine or reject) and where aggregate reports go (rua). |\nHaving all three is not enough: DMARC passes only when SPF or DKIM passes for a domain that lines up with the From: domain — its alignment. So Sender readiness shows each record and then whether they align:\n- Pass on alignment means DMARC passes on DKIM, which survives forwarding.\n- Check means DMARC passes on SPF alone, which forwarding breaks, or a record is present but weaker than it should be (?all, no closing all, p=none with no rua address to report to, no DMARC policy at all).\n- Fail means receivers cannot confirm the mail is from your domain: no SPF, more than one SPF record, a missing or revoked DKIM key, or nothing that aligns.\n- Not answered means a lookup went unanswered. Nothing is known to be wrong; check again in a few minutes.\nIt appears in two places:\n- Emails → Sending, on a sending domain once its records are issued: SPF on the send. subdomain, the domain\'s own DKIM selector, and the domain\'s DMARC policy. The records table on the same card says what to publish; the verification decides whether the domain may send; readiness is how receivers will judge it. Check DNS reads the zone fresh.\n- Sequences → Mailboxes, on each connected mailbox: the domain of the address it sends as, with Google\'s SPF incl'
7433
+ },
7434
+ {
7435
+ path: '/marketing-and-automation/email-campaigns/deliverability-checks',
7436
+ title: 'Deliverability checks',
7437
+ heading: 'What Aglyn never does',
7438
+ anchor: '#what-aglyn-never-does',
7439
+ text: '- No SMTP probing. Aglyn never connects to a recipient\'s mail server to ask whether an address exists (an RCPT TO probe). Receivers treat probing as abuse and blocklist the sender who does it, so every check here uses public DNS and the outcomes of mail that was really sent.\n- No catch-all detection and no paid verification service: an address whose domain takes mail is sent to, and a bounce is what teaches the ledger.'
7440
+ },
7441
+ {
7442
+ path: '/marketing-and-automation/email-campaigns/deliverability-checks',
7443
+ title: 'Deliverability checks',
7444
+ heading: 'Related',
7445
+ anchor: '#related',
7446
+ text: '- Email campaigns\n- Sequences\n- CRM'
7447
+ },
7378
7448
  {
7379
7449
  path: '/marketing-and-automation/email-campaigns/designed-emails',
7380
7450
  title: 'Designed emails',
@@ -7541,7 +7611,7 @@ export const ASSIST_DOCS_INDEX = [
7541
7611
  title: 'Email Campaigns',
7542
7612
  heading: 'Related',
7543
7613
  anchor: '#related',
7544
- text: '- CRM\n- Forms & lead capture\n- Marketing overlays (email capture popups)'
7614
+ text: '- Deliverability checks\n- CRM\n- Forms & lead capture\n- Marketing overlays (email capture popups)'
7545
7615
  },
7546
7616
  {
7547
7617
  path: '/marketing-and-automation/marketing-overlays/overview',
@@ -7639,7 +7709,7 @@ export const ASSIST_DOCS_INDEX = [
7639
7709
  title: 'Actions builder',
7640
7710
  heading: 'Run history',
7641
7711
  anchor: '#run-history',
7642
- text: 'Every automation row has a Runs button. It opens Runs — your automation with a Recent runs table of that one automation\'s executions, in four columns:\n| Column | What\'s in it |\n| Time | The clock time of the run; hover it for the full date and time. |\n| Trigger | The event, in words — formSubmission shows as Form submitted. A custom event keeps the name you gave it. |\n| Result | Succeeded, Failed, or Skipped. |\n| What happened | For a run, what each step did, joined with ·. For a failure, the errors. For a skip, which condition stopped it. |\nThe table\'s toolbar narrows it. Filters offers Trigger and Result as picked values (is or is any of) and Time by day (is, after, on or after, before, on or before); Search finds a word at the start of any word in What happened. Every filter and the search are asked of the whole history, however far back it goes, and the table pages through the matches newest first, so a run from months ago is found as readily as one from this morning. Each filter in force shows as a chip above the table. Search and filters combine; a combination the history can\'t answer at once is named above the table and not applied, rather than applied to some runs and not others. See Filter and search a list.\nSkipped is a result, not an absence — see Only run when a field matches. A run that fired but whose steps errored is Failed, and the errors are in the last column rather than in a log you have to go and find.\nAt the top of the Actions tab — and again on the Workflows tab, counting workflow runs — a line reads 1,284 action runs this month · 50,000 included: how many metered runs this site has used this calendar month, against the number your plan includes. This line is the only place action runs are counted for you — the billing usage meters meter workflow r'
7712
+ text: 'Every automation row has a Runs button. It opens Runs — your automation with a Recent runs table of that one automation\'s executions, in five columns:\n| Column | What\'s in it |\n| Time | The clock time of the run; hover it for the full date and time. |\n| Trigger | The event, in words — formSubmission shows as Form submitted. A custom event keeps the name you gave it. |\n| Who | Who set the run off: a teammate\'s address, API key and the key\'s name, Site visitor (with the address they gave, when they gave one), or Inbound webhook and the hook\'s name. A purchase, booking or sign-up credits the customer who made it. A chained run — one an earlier automation\'s step caused — names whoever caused the first. Runs from before this column existed, and a run that resumes after a wait, say Not recorded. |\n| Result | Succeeded, Failed, or Skipped. |\n| What happened | For a run, what each step did, joined with ·. For a failure, the errors. For a skip, which condition stopped it. |\nThe table\'s toolbar narrows it. Filters offers Trigger and Result as picked values (is or is any of) and Time by day (is, after, on or after, before, on or before); Search finds a word at the start of any word in What happened. Every filter and the search are asked of the whole history, however far back it goes, and the table pages through the matches newest first, so a run from months ago is found as readily as one from this morning. Each filter in force shows as a chip above the table. Search and filters combine; a combination the history can\'t answer at once is named above the table and not applied, rather than applied to some runs and not others. See Filter and search a list.\nSkipped is a result, not an absence — see Only run when a field matches. A run that fired but whose steps errored is Failed, and th'
7643
7713
  },
7644
7714
  {
7645
7715
  path: '/marketing-and-automation/workflows-and-actions/actions-builder',
@@ -7807,7 +7877,7 @@ export const ASSIST_DOCS_INDEX = [
7807
7877
  title: 'Automation',
7808
7878
  heading: 'Run history and the run allowance',
7809
7879
  anchor: '#run-history',
7810
- text: 'The Workflows and Actions tabs each open with a line reading 1,284 action runs this month · 50,000 included — the metered run allowance this site is spending. When a site reaches the month\'s limit, triggered automations stop running rather than queueing or billing on.\nEach row has a Runs button opening a four-column table — Time, Trigger, Result, What happened — with Succeeded, Failed and Skipped chips. The table is runs only: publishes, media saves and member changes stay in the site\'s general activity feed, where there is nothing to say Succeeded about.\nSkipped is the row that earns the table: an automation stopped by an unmet trigger condition is now recorded, naming the condition field, so "why didn\'t my automation fire?" is answered where the runs are. A skip is not a metered run. See Run history for what is and isn\'t recorded — page-view skips deliberately aren\'t — and Build a workflow for where workflow executions currently show up.\nMore detailed how-tos coming Recipes for common automations (notify on form submit, sync orders, etc.) are on the way.'
7880
+ text: 'The Workflows and Actions tabs each open with a line reading 1,284 action runs this month · 50,000 included — the metered run allowance this site is spending. When a site reaches the month\'s limit, triggered automations stop running rather than queueing or billing on.\nEach row has a Runs button opening a five-column table — Time, Trigger, Who, Result, What happened — with Succeeded, Failed and Skipped chips. The table is runs only: publishes, media saves and member changes stay in the site\'s general activity feed, where there is nothing to say Succeeded about.\nSkipped is the row that earns the table: an automation stopped by an unmet trigger condition is now recorded, naming the condition field, so "why didn\'t my automation fire?" is answered where the runs are. A skip is not a metered run. See Run history for what is and isn\'t recorded — page-view skips deliberately aren\'t — and Build a workflow for where workflow executions currently show up.\nMore detailed how-tos coming Recipes for common automations (notify on form submit, sync orders, etc.) are on the way.'
7811
7881
  },
7812
7882
  {
7813
7883
  path: '/marketing-and-automation/workflows-and-actions/overview',