@graph8/sdk 0.370.0 → 0.374.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (646) hide show
  1. package/README.md +22 -3
  2. package/blocks/_foundation/README.md +61 -0
  3. package/blocks/_foundation/block.json +57 -0
  4. package/blocks/_foundation/components/auth-gate.tsx +363 -0
  5. package/blocks/_foundation/components/confirm-spend-dialog.tsx +15 -8
  6. package/blocks/_foundation/components/empty-state.tsx +5 -2
  7. package/blocks/_foundation/components/list-select.tsx +112 -0
  8. package/blocks/_foundation/components/paging-notices.tsx +55 -0
  9. package/blocks/_foundation/components/screen/detail-panel.tsx +2 -1
  10. package/blocks/_foundation/components/screen/filter-controls.tsx +92 -0
  11. package/blocks/_foundation/components/screen/index.ts +1 -0
  12. package/blocks/_foundation/components/screen/split-view.tsx +1 -1
  13. package/blocks/_foundation/hooks/use-all-pages.ts +54 -0
  14. package/blocks/_foundation/hooks/use-load-more-list.ts +61 -0
  15. package/blocks/_foundation/hooks/use-url-filters.ts +93 -0
  16. package/blocks/_foundation/lib/paging.ts +57 -0
  17. package/blocks/_foundation/lib/use-debounced-value.ts +13 -0
  18. package/blocks/_foundation/server/auth.ts +815 -0
  19. package/blocks/agents/README.md +81 -4
  20. package/blocks/agents/block.json +311 -2
  21. package/blocks/agents/components/agent-activity.tsx +112 -0
  22. package/blocks/agents/components/agent-approvals.tsx +135 -0
  23. package/blocks/agents/components/agent-builder.tsx +17 -3
  24. package/blocks/agents/components/agent-conversations.tsx +143 -0
  25. package/blocks/agents/components/agent-knowledge-panel.tsx +2 -2
  26. package/blocks/agents/components/agent-operators.tsx +164 -0
  27. package/blocks/agents/components/agent-run-detail.tsx +20 -3
  28. package/blocks/agents/components/agent-runs.tsx +3 -3
  29. package/blocks/agents/components/agent-skills-panel.tsx +7 -7
  30. package/blocks/agents/components/agent-test-history.tsx +121 -0
  31. package/blocks/agents/components/agents-screen.tsx +53 -11
  32. package/blocks/agents/components/assistant-sessions.tsx +150 -0
  33. package/blocks/agents/components/duplicate-agent-button.tsx +42 -0
  34. package/blocks/agents/components/from-template-button.tsx +65 -0
  35. package/blocks/agents/components/knowledge-browser.tsx +107 -0
  36. package/blocks/agents/components/knowledge-detail.tsx +156 -0
  37. package/blocks/agents/components/knowledge-form.tsx +69 -0
  38. package/blocks/agents/components/name-dialog.tsx +32 -0
  39. package/blocks/agents/components/receptionist-button.tsx +46 -0
  40. package/blocks/agents/components/save-as-template-button.tsx +32 -0
  41. package/blocks/agents/components/skill-editor.tsx +147 -0
  42. package/blocks/agents/components/skill-library.tsx +111 -0
  43. package/blocks/agents/lib/agent-types.ts +7 -1
  44. package/blocks/agents/lib/facts.tsx +13 -0
  45. package/blocks/agents/lib/knowledge-utils.ts +49 -0
  46. package/blocks/agents/lib/load-more.tsx +141 -0
  47. package/blocks/agents/lib/skill-utils.ts +91 -0
  48. package/blocks/agents/recipes/create-agent-and-attach-skill.ts +1 -1
  49. package/blocks/analytics/components/analytics-screen.tsx +16 -3
  50. package/blocks/analytics/components/kpi-tiles.tsx +1 -1
  51. package/blocks/analytics/components/report-builder.tsx +15 -2
  52. package/blocks/appointments/README.md +6 -0
  53. package/blocks/appointments/block.json +27 -0
  54. package/blocks/appointments/components/booking-page.tsx +2 -2
  55. package/blocks/appointments/components/meeting-contact-links.tsx +49 -0
  56. package/blocks/appointments/components/meeting-detail.tsx +2 -2
  57. package/blocks/appointments/components/meeting-list.tsx +29 -13
  58. package/blocks/appointments/components/record-meetings-tab.tsx +92 -0
  59. package/blocks/campaigns/README.md +1 -1
  60. package/blocks/campaigns/block.json +34 -1
  61. package/blocks/campaigns/components/campaign-assets.tsx +1 -1
  62. package/blocks/campaigns/components/campaign-launch-step.tsx +16 -11
  63. package/blocks/campaigns/components/campaign-links.tsx +180 -0
  64. package/blocks/campaigns/components/campaign-metrics.tsx +1 -1
  65. package/blocks/campaigns/components/campaign-wizard.tsx +10 -5
  66. package/blocks/campaigns/components/icp-detail.tsx +11 -2
  67. package/blocks/campaigns/components/icp-editor.tsx +8 -3
  68. package/blocks/campaigns/components/persona-detail.tsx +11 -2
  69. package/blocks/campaigns/components/persona-editor.tsx +6 -1
  70. package/blocks/campaigns/lib/campaigns-utils.ts +4 -4
  71. package/blocks/coach/components/call-scorecard.tsx +7 -7
  72. package/blocks/company-intelligence/README.md +132 -0
  73. package/blocks/company-intelligence/block.json +318 -0
  74. package/blocks/company-intelligence/components/company-intelligence-screen.tsx +114 -0
  75. package/blocks/company-intelligence/components/company-intelligence-tab.tsx +88 -0
  76. package/blocks/company-intelligence/components/company-profile.tsx +68 -0
  77. package/blocks/company-intelligence/components/intelligence-sources.tsx +63 -0
  78. package/blocks/company-intelligence/components/report-detail.tsx +189 -0
  79. package/blocks/company-intelligence/components/research-reports.tsx +136 -0
  80. package/blocks/company-intelligence/components/research-run.tsx +165 -0
  81. package/blocks/company-intelligence/components/source-detail.tsx +117 -0
  82. package/blocks/company-intelligence/components/source-view.tsx +274 -0
  83. package/blocks/company-intelligence/components/wiki-document-detail.tsx +116 -0
  84. package/blocks/company-intelligence/components/wiki-documents.tsx +66 -0
  85. package/blocks/company-intelligence/hooks/use-intelligence.ts +173 -0
  86. package/blocks/company-intelligence/lib/intelligence-types.ts +230 -0
  87. package/blocks/company-intelligence/lib/safe-markdown.tsx +150 -0
  88. package/blocks/company-intelligence/recipes/read-competitors-and-news.ts +33 -0
  89. package/blocks/company-intelligence/recipes/research-your-website.ts +43 -0
  90. package/blocks/company-intelligence/recipes/write-the-research-reports.ts +54 -0
  91. package/blocks/copilot/components/copilot-memory.tsx +6 -2
  92. package/blocks/copilot/components/copilot-panel.tsx +1 -1
  93. package/blocks/copilot/components/copilot-team-chat.tsx +6 -1
  94. package/blocks/crm/README.md +25 -0
  95. package/blocks/crm/block.json +257 -9
  96. package/blocks/crm/components/bulk-update.tsx +119 -0
  97. package/blocks/crm/components/companies-screen.tsx +23 -9
  98. package/blocks/crm/components/company-detail.tsx +2 -2
  99. package/blocks/crm/components/contact-detail.tsx +4 -4
  100. package/blocks/crm/components/contact-documents-tab.tsx +144 -0
  101. package/blocks/crm/components/contact-insights-tab.tsx +118 -0
  102. package/blocks/crm/components/contact-lookup-link.tsx +85 -0
  103. package/blocks/crm/components/contact-row-actions.tsx +43 -0
  104. package/blocks/crm/components/contact-tag-chips.tsx +113 -0
  105. package/blocks/crm/components/contacts-screen.tsx +77 -18
  106. package/blocks/crm/components/dedupe-review.tsx +6 -1
  107. package/blocks/crm/components/import-wizard.tsx +3 -7
  108. package/blocks/crm/components/list-select.tsx +20 -0
  109. package/blocks/crm/components/owner-assign.tsx +132 -0
  110. package/blocks/crm/components/record-engagement-tab.tsx +103 -0
  111. package/blocks/crm/components/record-form.tsx +1 -1
  112. package/blocks/crm/components/record-open-links.tsx +79 -0
  113. package/blocks/crm/components/tag-assign.tsx +112 -0
  114. package/blocks/crm/components/tag-manager.tsx +178 -0
  115. package/blocks/crm/components/tasks-screen.tsx +35 -15
  116. package/blocks/crm/components/unlock-info-button.tsx +82 -0
  117. package/blocks/crm/components/verify-email-button.tsx +93 -0
  118. package/blocks/crm/hooks/use-all-lists.ts +18 -0
  119. package/blocks/crm/hooks/use-contact-tags.ts +29 -0
  120. package/blocks/crm/hooks/use-page-query.ts +3 -1
  121. package/blocks/crm/lib/crm-types.ts +14 -0
  122. package/blocks/crm/lib/read-lists.ts +16 -0
  123. package/blocks/crm/lib/record-actions.ts +102 -0
  124. package/blocks/deals/README.md +25 -6
  125. package/blocks/deals/block.json +168 -2
  126. package/blocks/deals/components/close-deal-dialog.tsx +169 -0
  127. package/blocks/deals/components/create-deal-button.tsx +109 -0
  128. package/blocks/deals/components/deal-contacts-editor.tsx +134 -0
  129. package/blocks/deals/components/deal-detail.tsx +7 -5
  130. package/blocks/deals/components/deal-filters.tsx +65 -0
  131. package/blocks/deals/components/deal-line-items.tsx +120 -0
  132. package/blocks/deals/components/deal-notes-tab.tsx +97 -0
  133. package/blocks/deals/components/deal-related.tsx +30 -9
  134. package/blocks/deals/components/deal-tasks-tab.tsx +88 -0
  135. package/blocks/deals/components/deals-screen.tsx +57 -22
  136. package/blocks/deals/components/forecast-view.tsx +4 -4
  137. package/blocks/deals/components/pipeline-board.tsx +8 -6
  138. package/blocks/deals/components/pipeline-editor.tsx +132 -0
  139. package/blocks/deals/components/record-deals-tab.tsx +62 -0
  140. package/blocks/deals/components/stage-editor.tsx +1 -1
  141. package/blocks/deals/hooks/use-pipeline-deals.ts +17 -4
  142. package/blocks/deals/lib/deal-extras.ts +156 -0
  143. package/blocks/dialer/README.md +14 -5
  144. package/blocks/dialer/block.json +17 -2
  145. package/blocks/dialer/components/call-contact-button.tsx +26 -9
  146. package/blocks/dialer/components/call-log-screen.tsx +10 -2
  147. package/blocks/dialer/components/call-log.tsx +16 -4
  148. package/blocks/dialer/components/call-queue.tsx +3 -3
  149. package/blocks/dialer/components/dialer-screen.tsx +69 -27
  150. package/blocks/dialer/components/disposition-panel.tsx +1 -1
  151. package/blocks/dialer/components/record-calls-tab.tsx +101 -0
  152. package/blocks/dialer/components/session-start-form.tsx +58 -11
  153. package/blocks/dialer/components/softphone.tsx +1 -1
  154. package/blocks/documents/README.md +86 -0
  155. package/blocks/documents/block.json +378 -0
  156. package/blocks/documents/components/account-bar.tsx +48 -0
  157. package/blocks/documents/components/correspondence-form.tsx +95 -0
  158. package/blocks/documents/components/correspondence-import.tsx +106 -0
  159. package/blocks/documents/components/correspondence-panel.tsx +191 -0
  160. package/blocks/documents/components/documents-screen.tsx +76 -0
  161. package/blocks/documents/components/library-document-detail.tsx +175 -0
  162. package/blocks/documents/components/library-document-form.tsx +105 -0
  163. package/blocks/documents/components/library-folder-form.tsx +74 -0
  164. package/blocks/documents/components/library-panel.tsx +235 -0
  165. package/blocks/documents/components/library-upload.tsx +160 -0
  166. package/blocks/documents/components/question-row.tsx +130 -0
  167. package/blocks/documents/components/questionnaire-detail.tsx +197 -0
  168. package/blocks/documents/components/questionnaires-panel.tsx +173 -0
  169. package/blocks/documents/components/record-documents-tab.tsx +109 -0
  170. package/blocks/documents/lib/documents-types.ts +252 -0
  171. package/blocks/documents/lib/download.ts +64 -0
  172. package/blocks/documents/recipes/follow-a-questionnaire.ts +26 -0
  173. package/blocks/documents/recipes/log-account-correspondence.ts +27 -0
  174. package/blocks/documents/recipes/organize-the-library.ts +25 -0
  175. package/blocks/email-signatures/README.md +123 -0
  176. package/blocks/email-signatures/block.json +341 -0
  177. package/blocks/email-signatures/components/email-signatures-screen.tsx +166 -0
  178. package/blocks/email-signatures/components/signature-assign-template.tsx +119 -0
  179. package/blocks/email-signatures/components/signature-block-editor.tsx +87 -0
  180. package/blocks/email-signatures/components/signature-campaign-form.tsx +165 -0
  181. package/blocks/email-signatures/components/signature-campaigns.tsx +75 -0
  182. package/blocks/email-signatures/components/signature-identity-form.tsx +131 -0
  183. package/blocks/email-signatures/components/signature-mailboxes.tsx +195 -0
  184. package/blocks/email-signatures/components/signature-policy.tsx +130 -0
  185. package/blocks/email-signatures/components/signature-preview.tsx +119 -0
  186. package/blocks/email-signatures/components/signature-report.tsx +77 -0
  187. package/blocks/email-signatures/components/signature-template-details.tsx +112 -0
  188. package/blocks/email-signatures/components/signature-template-form.tsx +109 -0
  189. package/blocks/email-signatures/hooks/use-signatures-data.ts +93 -0
  190. package/blocks/email-signatures/lib/signatures-types.ts +392 -0
  191. package/blocks/email-signatures/recipes/assign-a-signature-to-a-new-hire.ts +32 -0
  192. package/blocks/email-signatures/recipes/create-a-template-and-make-it-default.ts +35 -0
  193. package/blocks/email-signatures/recipes/signature-engagement-report.ts +16 -0
  194. package/blocks/enrichment/README.md +38 -10
  195. package/blocks/enrichment/block.json +164 -12
  196. package/blocks/enrichment/components/auto-enrichment-configs.tsx +134 -0
  197. package/blocks/enrichment/components/auto-enrichment-settings.tsx +82 -0
  198. package/blocks/enrichment/components/bulk-enrich-panel.tsx +79 -30
  199. package/blocks/enrichment/components/email-verify-badge.tsx +3 -3
  200. package/blocks/enrichment/components/enrich-button.tsx +1 -1
  201. package/blocks/enrichment/components/enrichment-activity.tsx +93 -0
  202. package/blocks/enrichment/components/enrichment-job-detail.tsx +18 -0
  203. package/blocks/enrichment/components/enrichment-screen.tsx +103 -19
  204. package/blocks/enrichment/components/list-pipelines.tsx +97 -0
  205. package/blocks/enrichment/components/pipeline-detail.tsx +214 -0
  206. package/blocks/enrichment/components/pipeline-editor.tsx +144 -0
  207. package/blocks/enrichment/components/pipeline-templates.tsx +90 -0
  208. package/blocks/enrichment/components/waterfall-config.tsx +27 -7
  209. package/blocks/enrichment/lib/enrichment-types.ts +51 -0
  210. package/blocks/enrichment/lib/pipeline-types.ts +234 -0
  211. package/blocks/enrichment/recipes/bulk-enrich-and-poll.ts +15 -4
  212. package/blocks/forms/README.md +10 -0
  213. package/blocks/forms/block.json +107 -0
  214. package/blocks/forms/components/form-activity.tsx +71 -0
  215. package/blocks/forms/components/form-backfill.tsx +108 -0
  216. package/blocks/forms/components/form-builder.tsx +1 -1
  217. package/blocks/forms/components/form-embed.tsx +1 -1
  218. package/blocks/forms/components/form-groups.tsx +151 -0
  219. package/blocks/forms/components/form-mapping-editor.tsx +6 -9
  220. package/blocks/forms/components/form-route-history.tsx +72 -0
  221. package/blocks/forms/components/form-saved-views.tsx +81 -0
  222. package/blocks/forms/components/forms-screen.tsx +57 -20
  223. package/blocks/forms/components/more-rows.tsx +30 -0
  224. package/blocks/forms/components/submissions-export.tsx +67 -0
  225. package/blocks/forms/components/submissions-table.tsx +15 -6
  226. package/blocks/forms/lib/forms-extra.ts +67 -0
  227. package/blocks/inbox/README.md +45 -4
  228. package/blocks/inbox/block.json +145 -8
  229. package/blocks/inbox/components/blocked-senders.tsx +103 -0
  230. package/blocks/inbox/components/compose-message.tsx +193 -0
  231. package/blocks/inbox/components/inbox-screen.tsx +54 -6
  232. package/blocks/inbox/components/inbox-search.tsx +106 -0
  233. package/blocks/inbox/components/record-conversations-tab.tsx +154 -0
  234. package/blocks/inbox/components/tag-manager.tsx +136 -0
  235. package/blocks/inbox/components/thread-actions.tsx +133 -0
  236. package/blocks/inbox/components/thread-badge.tsx +2 -2
  237. package/blocks/inbox/components/thread-view.tsx +3 -1
  238. package/blocks/inbox/hooks/use-inbox-mailboxes.ts +31 -0
  239. package/blocks/inbox/lib/inbox-manage.ts +227 -0
  240. package/blocks/integrations/README.md +54 -1
  241. package/blocks/integrations/block.json +189 -15
  242. package/blocks/integrations/components/ad-accounts.tsx +82 -0
  243. package/blocks/integrations/components/connection-health.tsx +59 -0
  244. package/blocks/integrations/components/crm-detail.tsx +20 -1
  245. package/blocks/integrations/components/crm-records.tsx +103 -0
  246. package/blocks/integrations/components/field-mappings.tsx +61 -0
  247. package/blocks/integrations/components/integration-list.tsx +4 -4
  248. package/blocks/integrations/components/integrations-screen.tsx +20 -3
  249. package/blocks/integrations/components/migration-panel.tsx +133 -0
  250. package/blocks/integrations/components/sugarcrm-connect.tsx +95 -0
  251. package/blocks/integrations/components/sync-status.tsx +6 -6
  252. package/blocks/integrations/components/sync-triggers.tsx +134 -0
  253. package/blocks/integrations/components/webhook-detail.tsx +26 -7
  254. package/blocks/integrations/components/webhook-manager.tsx +30 -17
  255. package/blocks/integrations/components/workspace-tools.tsx +127 -0
  256. package/blocks/integrations/lib/crm-utils.ts +39 -0
  257. package/blocks/integrations/lib/facts.tsx +13 -0
  258. package/blocks/integrations/lib/integrations-utils.ts +5 -0
  259. package/blocks/integrations/lib/load-more.tsx +141 -0
  260. package/blocks/intent/components/account-intent-score.tsx +5 -5
  261. package/blocks/intent/components/intent-feed.tsx +8 -8
  262. package/blocks/intent/components/intent-keyword-manager.tsx +8 -8
  263. package/blocks/intent/lib/intent-utils.ts +4 -4
  264. package/blocks/lifecycle/components/account-related.tsx +8 -1
  265. package/blocks/lifecycle/components/churn-list.tsx +5 -2
  266. package/blocks/lifecycle/components/customer-table.tsx +1 -1
  267. package/blocks/lists/README.md +7 -1
  268. package/blocks/lists/block.json +31 -3
  269. package/blocks/lists/components/add-company-people-to-list-button.tsx +73 -0
  270. package/blocks/lists/components/add-to-list-button.tsx +17 -7
  271. package/blocks/lists/components/list-picker.tsx +13 -3
  272. package/blocks/lists/components/list-table.tsx +1 -1
  273. package/blocks/lists/components/lists-screen.tsx +4 -2
  274. package/blocks/lists/components/record-lists-tab.tsx +99 -0
  275. package/blocks/lists/components/suppression-manager.tsx +27 -6
  276. package/blocks/lists/components/suppressions-screen.tsx +21 -12
  277. package/blocks/mailboxes/block.json +4 -0
  278. package/blocks/mailboxes/components/connect-mailbox.tsx +9 -8
  279. package/blocks/mailboxes/components/domain-detail.tsx +10 -4
  280. package/blocks/mailboxes/components/domain-purchase.tsx +14 -11
  281. package/blocks/mailboxes/components/mailbox-detail.tsx +3 -2
  282. package/blocks/mailboxes/components/mailbox-list.tsx +8 -3
  283. package/blocks/mailboxes/components/mailboxes-screen.tsx +8 -3
  284. package/blocks/mailboxes/components/warmup-health.tsx +20 -20
  285. package/blocks/mailboxes/lib/mailboxes-utils.ts +4 -4
  286. package/blocks/mailboxes/lib/use-provider-domains.ts +23 -0
  287. package/blocks/newsletter/README.md +12 -0
  288. package/blocks/newsletter/block.json +117 -0
  289. package/blocks/newsletter/components/issue-scheduler.tsx +2 -2
  290. package/blocks/newsletter/components/issue-versions.tsx +93 -0
  291. package/blocks/newsletter/components/more-rows.tsx +30 -0
  292. package/blocks/newsletter/components/newsletter-editor.tsx +1 -1
  293. package/blocks/newsletter/components/newsletter-list-syncs.tsx +121 -0
  294. package/blocks/newsletter/components/newsletter-screen.tsx +38 -13
  295. package/blocks/newsletter/components/newsletter-settings.tsx +211 -0
  296. package/blocks/newsletter/components/newsletter-stats.tsx +2 -2
  297. package/blocks/newsletter/components/newsletter-templates.tsx +133 -0
  298. package/blocks/newsletter/components/subscriber-table.tsx +5 -7
  299. package/blocks/newsletter/components/subscriber-timeline.tsx +65 -0
  300. package/blocks/newsletter/lib/newsletter-extra.ts +60 -0
  301. package/blocks/nurture/components/nurture-builder.tsx +10 -2
  302. package/blocks/nurture/components/nurture-enroll.tsx +6 -7
  303. package/blocks/nurture/components/nurture-form.tsx +5 -8
  304. package/blocks/nurture/components/nurture-screen.tsx +16 -5
  305. package/blocks/nurture/components/nurture-stats.tsx +5 -4
  306. package/blocks/objects/README.md +1 -1
  307. package/blocks/objects/block.json +13 -5
  308. package/blocks/objects/components/attribute-field-input.tsx +14 -1
  309. package/blocks/objects/components/object-record-detail.tsx +8 -1
  310. package/blocks/objects/components/object-schema-editor.tsx +1 -1
  311. package/blocks/objects/components/record-roles-panel.tsx +15 -5
  312. package/blocks/pages/README.md +14 -0
  313. package/blocks/pages/block.json +123 -2
  314. package/blocks/pages/components/more-rows.tsx +30 -0
  315. package/blocks/pages/components/page-ai-tools.tsx +196 -0
  316. package/blocks/pages/components/page-analytics.tsx +176 -0
  317. package/blocks/pages/components/page-clone.tsx +99 -0
  318. package/blocks/pages/components/page-domains.tsx +167 -0
  319. package/blocks/pages/components/page-editor.tsx +1 -1
  320. package/blocks/pages/components/page-generation-status.tsx +48 -0
  321. package/blocks/pages/components/page-tracking.tsx +137 -0
  322. package/blocks/pages/components/pages-screen.tsx +57 -16
  323. package/blocks/pages/components/seo-report.tsx +6 -1
  324. package/blocks/pages/hooks/use-landing-page-paths.ts +60 -0
  325. package/blocks/pages/lib/studio.ts +7 -0
  326. package/blocks/phone-numbers/README.md +74 -0
  327. package/blocks/phone-numbers/block.json +262 -0
  328. package/blocks/phone-numbers/components/a2p-registration.tsx +230 -0
  329. package/blocks/phone-numbers/components/assign-number.tsx +104 -0
  330. package/blocks/phone-numbers/components/number-search.tsx +239 -0
  331. package/blocks/phone-numbers/components/number-settings.tsx +134 -0
  332. package/blocks/phone-numbers/components/number-usage.tsx +97 -0
  333. package/blocks/phone-numbers/components/phone-number-detail.tsx +84 -0
  334. package/blocks/phone-numbers/components/phone-number-list.tsx +110 -0
  335. package/blocks/phone-numbers/components/phone-numbers-screen.tsx +201 -0
  336. package/blocks/phone-numbers/components/register-owned-number.tsx +98 -0
  337. package/blocks/phone-numbers/components/release-number.tsx +126 -0
  338. package/blocks/phone-numbers/hooks/use-phone-numbers.ts +58 -0
  339. package/blocks/phone-numbers/lib/phone-numbers-utils.ts +202 -0
  340. package/blocks/phone-numbers/recipes/01-search-and-buy-number.ts +49 -0
  341. package/blocks/phone-numbers/recipes/02-assign-number-to-rep.ts +25 -0
  342. package/blocks/phone-numbers/recipes/03-audit-numbers.ts +36 -0
  343. package/blocks/phone-numbers/recipes/04-register-text-messaging.ts +39 -0
  344. package/blocks/pickers/README.md +23 -0
  345. package/blocks/pickers/block.json +103 -0
  346. package/blocks/pickers/components/company-picker.tsx +90 -0
  347. package/blocks/pickers/components/contact-picker.tsx +131 -0
  348. package/blocks/pickers/components/member-picker.tsx +99 -0
  349. package/blocks/pickers/components/search-picker.tsx +98 -0
  350. package/blocks/pickers/hooks/use-current-member.ts +104 -0
  351. package/blocks/pickers/hooks/use-picker-pages.ts +73 -0
  352. package/blocks/pickers/lib/use-debounced.ts +13 -0
  353. package/blocks/programmatic-pages/README.md +82 -0
  354. package/blocks/programmatic-pages/block.json +292 -0
  355. package/blocks/programmatic-pages/components/programmatic-campaign-form.tsx +136 -0
  356. package/blocks/programmatic-pages/components/programmatic-generate.tsx +112 -0
  357. package/blocks/programmatic-pages/components/programmatic-generated-pages.tsx +210 -0
  358. package/blocks/programmatic-pages/components/programmatic-overview.tsx +84 -0
  359. package/blocks/programmatic-pages/components/programmatic-pages-screen.tsx +221 -0
  360. package/blocks/programmatic-pages/components/programmatic-preview.tsx +66 -0
  361. package/blocks/programmatic-pages/components/programmatic-publish.tsx +102 -0
  362. package/blocks/programmatic-pages/components/programmatic-settings.tsx +96 -0
  363. package/blocks/programmatic-pages/components/programmatic-source-fields.tsx +75 -0
  364. package/blocks/programmatic-pages/components/programmatic-source.tsx +106 -0
  365. package/blocks/programmatic-pages/components/programmatic-variables.tsx +84 -0
  366. package/blocks/programmatic-pages/hooks/use-programmatic-workspace.ts +19 -0
  367. package/blocks/programmatic-pages/lib/programmatic-types.ts +330 -0
  368. package/blocks/programmatic-pages/recipes/create-a-programmatic-campaign.ts +23 -0
  369. package/blocks/programmatic-pages/recipes/render-and-follow.ts +21 -0
  370. package/blocks/programmatic-pages/recipes/review-then-publish.ts +17 -0
  371. package/blocks/quotes/README.md +38 -1
  372. package/blocks/quotes/block.json +170 -6
  373. package/blocks/quotes/components/create-quote-from-deal.tsx +48 -0
  374. package/blocks/quotes/components/manual-products.tsx +144 -0
  375. package/blocks/quotes/components/quote-batch-actions.tsx +86 -0
  376. package/blocks/quotes/components/quote-builder.tsx +1 -1
  377. package/blocks/quotes/components/quote-documents.tsx +94 -0
  378. package/blocks/quotes/components/quote-evidence.tsx +95 -0
  379. package/blocks/quotes/components/quote-lifecycle-actions.tsx +146 -0
  380. package/blocks/quotes/components/quote-list.tsx +28 -8
  381. package/blocks/quotes/components/quote-related.tsx +3 -2
  382. package/blocks/quotes/components/quote-settings.tsx +184 -0
  383. package/blocks/quotes/components/quote-templates.tsx +189 -0
  384. package/blocks/quotes/components/quote-viewer.tsx +23 -5
  385. package/blocks/quotes/components/quotes-screen.tsx +15 -5
  386. package/blocks/quotes/components/record-quotes-tab.tsx +90 -0
  387. package/blocks/quotes/lib/quote-admin.ts +264 -0
  388. package/blocks/radar/components/competitor-battle-card.tsx +1 -1
  389. package/blocks/radar/components/radar-overview.tsx +4 -4
  390. package/blocks/radar/components/radar-screen.tsx +2 -2
  391. package/blocks/sequencer/README.md +61 -0
  392. package/blocks/sequencer/block.json +200 -6
  393. package/blocks/sequencer/components/add-to-sequence-button.tsx +82 -0
  394. package/blocks/sequencer/components/enroll-dialog.tsx +2 -2
  395. package/blocks/sequencer/components/enrollment-withdraw.tsx +71 -0
  396. package/blocks/sequencer/components/record-sequences-tab.tsx +126 -0
  397. package/blocks/sequencer/components/sequence-builder.tsx +32 -23
  398. package/blocks/sequencer/components/sequence-campaign-link.tsx +78 -0
  399. package/blocks/sequencer/components/sequence-detail.tsx +10 -2
  400. package/blocks/sequencer/components/sequence-edit-form.tsx +111 -0
  401. package/blocks/sequencer/components/sequence-enrollments.tsx +5 -2
  402. package/blocks/sequencer/components/sequence-list.tsx +1 -1
  403. package/blocks/sequencer/components/sequence-manage-actions.tsx +119 -0
  404. package/blocks/sequencer/components/sequence-senders-panel.tsx +84 -0
  405. package/blocks/sequencer/components/sequence-stats.tsx +8 -8
  406. package/blocks/sequencer/components/sequence-templates.tsx +160 -0
  407. package/blocks/sequencer/components/sequences-screen.tsx +39 -6
  408. package/blocks/sequencer/components/step-ai-assist.tsx +196 -0
  409. package/blocks/sequencer/components/step-editor.tsx +21 -12
  410. package/blocks/sequencer/components/step-templates.tsx +224 -0
  411. package/blocks/sequencer/lib/sequencer-manage.ts +235 -0
  412. package/blocks/sequencer/lib/sequencer-utils.ts +9 -5
  413. package/blocks/signals/README.md +39 -0
  414. package/blocks/signals/block.json +287 -0
  415. package/blocks/signals/components/auto-seed-panel.tsx +111 -0
  416. package/blocks/signals/components/listener-form.tsx +184 -0
  417. package/blocks/signals/components/matched-posts.tsx +195 -0
  418. package/blocks/signals/components/post-engagements.tsx +114 -0
  419. package/blocks/signals/components/record-social-tab.tsx +49 -0
  420. package/blocks/signals/components/social-listeners-screen.tsx +250 -0
  421. package/blocks/signals/components/social-settings.tsx +128 -0
  422. package/blocks/signals/components/source-lists.tsx +83 -0
  423. package/blocks/signals/components/top-engagers.tsx +68 -0
  424. package/blocks/signals/components/x-integration.tsx +129 -0
  425. package/blocks/signals/lib/signals-types.ts +228 -0
  426. package/blocks/signals/recipes/preview-and-create-a-listener.ts +16 -0
  427. package/blocks/signals/recipes/spend-against-caps.ts +14 -0
  428. package/blocks/signals/recipes/triage-matched-posts.ts +18 -0
  429. package/blocks/social-ads/README.md +8 -0
  430. package/blocks/social-ads/block.json +137 -4
  431. package/blocks/social-ads/components/audience-sync-detail.tsx +9 -3
  432. package/blocks/social-ads/components/audience-sync.tsx +6 -2
  433. package/blocks/social-ads/components/campaign-ads.tsx +166 -0
  434. package/blocks/social-ads/components/campaign-detail.tsx +6 -1
  435. package/blocks/social-ads/components/campaign-editor.tsx +134 -0
  436. package/blocks/social-ads/components/creative-concepts.tsx +74 -0
  437. package/blocks/social-ads/components/creative-detail.tsx +6 -1
  438. package/blocks/social-ads/components/creative-gallery.tsx +20 -2
  439. package/blocks/social-ads/components/creative-revisions.tsx +57 -0
  440. package/blocks/social-ads/components/creative-tools.tsx +176 -0
  441. package/blocks/social-ads/components/post-composer.tsx +4 -2
  442. package/blocks/social-ads/components/post-detail.tsx +3 -0
  443. package/blocks/social-ads/components/social-ideas.tsx +64 -0
  444. package/blocks/social-ads/components/social-performance.tsx +80 -0
  445. package/blocks/social-ads/components/social-screen.tsx +33 -6
  446. package/blocks/social-ads/lib/ads-extra.ts +67 -0
  447. package/blocks/surveys/README.md +91 -0
  448. package/blocks/surveys/block.json +299 -0
  449. package/blocks/surveys/components/account-nps.tsx +69 -0
  450. package/blocks/surveys/components/contact-survey-history.tsx +52 -0
  451. package/blocks/surveys/components/org-summary.tsx +82 -0
  452. package/blocks/surveys/components/sending-domains.tsx +113 -0
  453. package/blocks/surveys/components/survey-form.tsx +126 -0
  454. package/blocks/surveys/components/survey-responses.tsx +145 -0
  455. package/blocks/surveys/components/survey-results.tsx +72 -0
  456. package/blocks/surveys/components/survey-send-history.tsx +58 -0
  457. package/blocks/surveys/components/survey-send.tsx +162 -0
  458. package/blocks/surveys/components/survey-triggers.tsx +166 -0
  459. package/blocks/surveys/components/surveys-screen.tsx +234 -0
  460. package/blocks/surveys/lib/surveys-types.ts +228 -0
  461. package/blocks/surveys/recipes/create-an-nps-survey.ts +22 -0
  462. package/blocks/surveys/recipes/send-a-survey-and-follow-it.ts +27 -0
  463. package/blocks/surveys/recipes/survey-score-report.ts +27 -0
  464. package/blocks/visitors/components/snippet-install.tsx +18 -9
  465. package/blocks/visitors/components/traffic-chart.tsx +7 -6
  466. package/blocks/visitors/components/visitor-company-card.tsx +24 -14
  467. package/blocks/visitors/components/visitor-feed.tsx +5 -5
  468. package/blocks/web-analytics/README.md +102 -0
  469. package/blocks/web-analytics/block.json +292 -0
  470. package/blocks/web-analytics/components/acquisition-tab.tsx +154 -0
  471. package/blocks/web-analytics/components/analytics-filters.tsx +161 -0
  472. package/blocks/web-analytics/components/audience-tab.tsx +135 -0
  473. package/blocks/web-analytics/components/conversions-tab.tsx +128 -0
  474. package/blocks/web-analytics/components/engagement-tab.tsx +123 -0
  475. package/blocks/web-analytics/components/overview-tab.tsx +102 -0
  476. package/blocks/web-analytics/components/pages-tab.tsx +148 -0
  477. package/blocks/web-analytics/components/ranked-table.tsx +103 -0
  478. package/blocks/web-analytics/components/realtime-tab.tsx +103 -0
  479. package/blocks/web-analytics/components/report-card.tsx +71 -0
  480. package/blocks/web-analytics/components/web-analytics-screen.tsx +90 -0
  481. package/blocks/web-analytics/hooks/use-report.ts +48 -0
  482. package/blocks/web-analytics/lib/analytics-scope.ts +155 -0
  483. package/blocks/web-analytics/lib/page-path.ts +14 -0
  484. package/blocks/web-analytics/lib/web-analytics-types.ts +187 -0
  485. package/blocks/web-analytics/recipes/best-landing-pages.ts +26 -0
  486. package/blocks/web-analytics/recipes/form-conversion-check.ts +19 -0
  487. package/blocks/web-analytics/recipes/weekly-traffic-report.ts +37 -0
  488. package/blocks/webchat/components/chat-widget-config.tsx +1 -1
  489. package/blocks/webchat/components/conversation-list.tsx +1 -1
  490. package/blocks/workbench/README.md +54 -2
  491. package/blocks/workbench/block.json +234 -5
  492. package/blocks/workbench/components/column-recipe-editor.tsx +1 -1
  493. package/blocks/workbench/components/run-progress.tsx +1 -1
  494. package/blocks/workbench/components/workbench-auto-enrich.tsx +88 -0
  495. package/blocks/workbench/components/workbench-automation.tsx +36 -0
  496. package/blocks/workbench/components/workbench-connections.tsx +136 -0
  497. package/blocks/workbench/components/workbench-crm-import.tsx +91 -0
  498. package/blocks/workbench/components/workbench-datafile-import.tsx +154 -0
  499. package/blocks/workbench/components/workbench-grid.tsx +71 -8
  500. package/blocks/workbench/components/workbench-pipelines.tsx +173 -0
  501. package/blocks/workbench/components/workbench-row-actions.tsx +8 -1
  502. package/blocks/workbench/components/workbench-schedule.tsx +191 -0
  503. package/blocks/workbench/components/workbench-screen.tsx +65 -22
  504. package/blocks/workbench/components/workbench-send-to-sequence.tsx +116 -0
  505. package/blocks/workbench/components/workbench-table-list.tsx +14 -2
  506. package/blocks/workbench/components/workbench-table-settings.tsx +53 -0
  507. package/blocks/workbench/components/workbench-triggers.tsx +178 -0
  508. package/blocks/workbench/hooks/use-connected-crms.ts +29 -0
  509. package/blocks/workbench/lib/api-status.ts +8 -0
  510. package/blocks/workbench/lib/automation-types.ts +257 -0
  511. package/blocks/workflows/components/workflow-editor.tsx +1 -1
  512. package/dist/cli.js +744 -394
  513. package/dist/index.browser.js +1 -1
  514. package/dist/index.browser.js.map +1 -1
  515. package/dist/index.browser.mjs +1 -1
  516. package/dist/index.browser.mjs.map +1 -1
  517. package/dist/index.d.mts +2 -2
  518. package/dist/index.d.ts +2 -2
  519. package/dist/index.js +1 -1
  520. package/dist/index.js.map +1 -1
  521. package/dist/index.mjs +1 -1
  522. package/dist/index.mjs.map +1 -1
  523. package/dist/react.browser.js +1 -1
  524. package/dist/react.browser.js.map +1 -1
  525. package/dist/react.browser.mjs +1 -1
  526. package/dist/react.browser.mjs.map +1 -1
  527. package/dist/react.d.mts +2 -2
  528. package/dist/react.d.ts +2 -2
  529. package/dist/react.js +1 -1
  530. package/dist/react.js.map +1 -1
  531. package/dist/react.mjs +1 -1
  532. package/dist/react.mjs.map +1 -1
  533. package/dist/types/blocks/auth.d.ts +45 -0
  534. package/dist/types/blocks/brand.d.ts +11 -2
  535. package/dist/types/blocks/create.d.ts +11 -4
  536. package/dist/types/blocks/doctor.d.ts +1 -0
  537. package/dist/types/blocks/flows.d.ts +7 -1
  538. package/dist/types/blocks/orgBrand.d.ts +34 -0
  539. package/dist/types/cli.d.ts +3 -0
  540. package/dist/types/version.d.ts +2 -2
  541. package/docs/api/desk.d.ts +2 -2
  542. package/docs/api/desk.md +3 -3
  543. package/docs/api/forms.md +1 -1
  544. package/package.json +1 -1
  545. package/stacks/6sense/pages/companies.tsx.tmpl +21 -0
  546. package/stacks/6sense/pages/contacts.tsx.tmpl +21 -0
  547. package/stacks/6sense/pages/tasks.tsx.tmpl +19 -0
  548. package/stacks/6sense.json +37 -4
  549. package/stacks/apollo/pages/calls.tsx.tmpl +23 -0
  550. package/stacks/apollo/pages/campaigns.tsx.tmpl +27 -0
  551. package/stacks/apollo/pages/companies.tsx.tmpl +19 -36
  552. package/stacks/apollo/pages/contacts.tsx.tmpl +35 -95
  553. package/stacks/apollo/pages/deals.tsx.tmpl +17 -0
  554. package/stacks/apollo/pages/inbox-contact.tsx.tmpl +13 -0
  555. package/stacks/apollo/pages/lists.tsx.tmpl +24 -0
  556. package/stacks/apollo/pages/meetings.tsx.tmpl +13 -0
  557. package/stacks/apollo/pages/sequences.tsx.tmpl +18 -0
  558. package/stacks/apollo/pages/tasks.tsx.tmpl +20 -0
  559. package/stacks/apollo.json +68 -9
  560. package/stacks/calendly/pages/companies.tsx.tmpl +19 -0
  561. package/stacks/calendly/pages/contacts.tsx.tmpl +21 -0
  562. package/stacks/calendly/pages/meetings.tsx.tmpl +13 -0
  563. package/stacks/calendly/pages/tasks.tsx.tmpl +19 -0
  564. package/stacks/calendly.json +8 -4
  565. package/stacks/clay/pages/companies.tsx.tmpl +17 -1
  566. package/stacks/clay/pages/contacts.tsx.tmpl +14 -0
  567. package/stacks/clay/pages/lists.tsx.tmpl +24 -0
  568. package/stacks/clay/pages/tasks.tsx.tmpl +19 -0
  569. package/stacks/clay.json +16 -3
  570. package/stacks/gong/pages/calls.tsx.tmpl +23 -0
  571. package/stacks/gong/pages/companies.tsx.tmpl +22 -0
  572. package/stacks/gong/pages/contacts.tsx.tmpl +34 -0
  573. package/stacks/gong/pages/lists.tsx.tmpl +12 -0
  574. package/stacks/gong/pages/meetings.tsx.tmpl +13 -0
  575. package/stacks/gong/pages/tasks.tsx.tmpl +19 -0
  576. package/stacks/gong.json +23 -6
  577. package/stacks/hootsuite/pages/companies.tsx.tmpl +22 -0
  578. package/stacks/hootsuite/pages/contacts.tsx.tmpl +30 -0
  579. package/stacks/hootsuite/pages/lists.tsx.tmpl +12 -0
  580. package/stacks/hootsuite/pages/tasks.tsx.tmpl +19 -0
  581. package/stacks/hootsuite.json +19 -4
  582. package/stacks/hubspot/pages/campaigns.tsx.tmpl +30 -0
  583. package/stacks/hubspot/pages/companies.tsx.tmpl +16 -35
  584. package/stacks/hubspot/pages/contacts.tsx.tmpl +35 -95
  585. package/stacks/hubspot/pages/deals.tsx.tmpl +14 -1
  586. package/stacks/hubspot/pages/forms.tsx.tmpl +36 -0
  587. package/stacks/hubspot/pages/inbox.tsx.tmpl +12 -1
  588. package/stacks/hubspot/pages/lifecycle.tsx.tmpl +23 -0
  589. package/stacks/hubspot/pages/lists.tsx.tmpl +12 -0
  590. package/stacks/hubspot/pages/meetings.tsx.tmpl +13 -0
  591. package/stacks/hubspot/pages/sequences.tsx.tmpl +18 -0
  592. package/stacks/hubspot/pages/tasks.tsx.tmpl +20 -0
  593. package/stacks/hubspot/pages/web-analytics.tsx.tmpl +14 -0
  594. package/stacks/hubspot.json +66 -7
  595. package/stacks/intercom/pages/companies.tsx.tmpl +19 -0
  596. package/stacks/intercom/pages/contacts.tsx.tmpl +21 -0
  597. package/stacks/intercom/pages/inbox-contact.tsx.tmpl +13 -0
  598. package/stacks/intercom/pages/tasks.tsx.tmpl +19 -0
  599. package/stacks/intercom.json +18 -4
  600. package/stacks/mailchimp/pages/companies.tsx.tmpl +22 -0
  601. package/stacks/mailchimp/pages/contacts.tsx.tmpl +30 -0
  602. package/stacks/mailchimp/pages/forms.tsx.tmpl +36 -0
  603. package/stacks/mailchimp/pages/lists.tsx.tmpl +12 -0
  604. package/stacks/mailchimp/pages/tasks.tsx.tmpl +19 -0
  605. package/stacks/mailchimp.json +22 -6
  606. package/stacks/outreach/pages/calls.tsx.tmpl +23 -0
  607. package/stacks/outreach/pages/campaigns.tsx.tmpl +27 -0
  608. package/stacks/outreach/pages/companies.tsx.tmpl +22 -0
  609. package/stacks/outreach/pages/contacts.tsx.tmpl +29 -66
  610. package/stacks/outreach/pages/inbox-contact.tsx.tmpl +13 -0
  611. package/stacks/outreach/pages/lists.tsx.tmpl +12 -0
  612. package/stacks/outreach/pages/meetings.tsx.tmpl +13 -0
  613. package/stacks/outreach/pages/sequences.tsx.tmpl +18 -0
  614. package/stacks/outreach/pages/tasks.tsx.tmpl +19 -0
  615. package/stacks/outreach.json +38 -9
  616. package/stacks/pipedrive/pages/companies.tsx.tmpl +11 -35
  617. package/stacks/pipedrive/pages/contacts.tsx.tmpl +17 -35
  618. package/stacks/pipedrive/pages/deals.tsx.tmpl +10 -1
  619. package/stacks/pipedrive/pages/inbox.tsx.tmpl +12 -1
  620. package/stacks/pipedrive/pages/meetings.tsx.tmpl +13 -0
  621. package/stacks/pipedrive/pages/tasks.tsx.tmpl +20 -0
  622. package/stacks/pipedrive.json +14 -2
  623. package/stacks/salesforce/pages/companies.tsx.tmpl +16 -35
  624. package/stacks/salesforce/pages/contacts.tsx.tmpl +26 -35
  625. package/stacks/salesforce/pages/deals.tsx.tmpl +14 -1
  626. package/stacks/salesforce/pages/lifecycle.tsx.tmpl +23 -0
  627. package/stacks/salesforce/pages/lists.tsx.tmpl +12 -0
  628. package/stacks/salesforce/pages/tasks.tsx.tmpl +20 -0
  629. package/stacks/salesforce.json +17 -3
  630. package/stacks/zoominfo/pages/companies.tsx.tmpl +17 -1
  631. package/stacks/zoominfo/pages/contacts.tsx.tmpl +16 -0
  632. package/stacks/zoominfo/pages/lists.tsx.tmpl +24 -0
  633. package/stacks/zoominfo/pages/tasks.tsx.tmpl +19 -0
  634. package/stacks/zoominfo.json +36 -3
  635. package/styles/styles.css +1 -1
  636. package/templates/auth/auth-route.ts.tmpl +16 -0
  637. package/templates/auth/g8.auth.anonymous.ts.tmpl +15 -0
  638. package/templates/auth/g8.auth.basic.ts.tmpl +17 -0
  639. package/templates/auth/g8.auth.jwt.ts.tmpl +23 -0
  640. package/templates/auth/g8.auth.password.ts.tmpl +18 -0
  641. package/templates/auth/g8.auth.session.ts.tmpl +23 -0
  642. package/templates/auth/middleware.ts.tmpl +14 -0
  643. package/templates/next-app/README.md.tmpl +5 -0
  644. package/templates/next-app/app/globals.css.tmpl +349 -64
  645. package/templates/next-app/components/app-shell.tsx.tmpl +6 -0
  646. package/templates/next-app/components/shell/quick-jump.tsx.tmpl +4 -0
@@ -0,0 +1,252 @@
1
+ "use client";
2
+
3
+ /**
4
+ * Shapes, vocabularies and helpers the documents block shares.
5
+ *
6
+ * The documents responses are OPEN in the contract (`DocumentPayload` / `DocumentListPayload`), so every field here is
7
+ * optional and read defensively: a missing value is shown as a dash, never replaced by a guess. A list read always carries
8
+ * its rows under `items` (the contract's promise), whichever key the app used underneath.
9
+ *
10
+ * SDK operations: none.
11
+ */
12
+
13
+ import type { Tone } from "@/g8/components/screen/status-pill";
14
+
15
+ export type Rec = Record<string, unknown>;
16
+
17
+ /** The Library root; a folder's `parent_path` for a top-level folder. */
18
+ export const ROOT = "Library";
19
+ /** Largest file the /api/g8 proxy forwards by default (5 MiB). The app itself accepts 50 MB: raise `maxBodyBytes` in the proxy route first. */
20
+ export const DEFAULT_MAX_BYTES = 5 * 1024 * 1024;
21
+ /** Bytes a multipart body adds around its files: the boundary lines and headers (about 200 per file) plus the other fields. */
22
+ export const BASE_OVERHEAD = 4096;
23
+ export const FILE_OVERHEAD = 512;
24
+ /** The largest single file that fits one request under the proxy's body cap (`maxBytes`). */
25
+ export const fileLimit = (maxBytes: number): number => maxBytes - BASE_OVERHEAD - FILE_OVERHEAD;
26
+ /** The extensions the Library accepts (contract text of uploadLibraryDocument). */
27
+ export const LIBRARY_EXTENSIONS = ["pdf", "docx", "doc", "txt", "md", "mdx", "csv", "json", "png", "jpg", "jpeg", "xls", "xlsx", "ppt", "pptx"] as const;
28
+ /** Security questionnaires are read from a spreadsheet or a PDF. */
29
+ export const QUESTIONNAIRE_EXTENSIONS = ["xlsx", "pdf"] as const;
30
+ /** The types whose text the Library can extract again. */
31
+ export const REEXTRACT_TYPES = ["pdf", "docx"] as const;
32
+
33
+ export const CORRESPONDENCE_CHANNELS = [
34
+ { value: "email", label: "Email" },
35
+ { value: "linkedin", label: "LinkedIn" },
36
+ { value: "call_notes", label: "Call notes" },
37
+ ] as const;
38
+ export const DIRECTIONS = [
39
+ { value: "inbound", label: "Inbound" },
40
+ { value: "outbound", label: "Outbound" },
41
+ ] as const;
42
+
43
+ export interface LibraryDoc {
44
+ id: string;
45
+ folder_path: string;
46
+ file_name?: string | null;
47
+ display_name?: string | null;
48
+ file_type?: string | null;
49
+ content?: string | null;
50
+ has_file_data?: boolean;
51
+ file_size_bytes?: number | null;
52
+ mime_type?: string | null;
53
+ priority?: string | null;
54
+ source?: string | null;
55
+ indexed_at?: string | null;
56
+ deleted_at?: string | null;
57
+ created_at?: string | null;
58
+ updated_at?: string | null;
59
+ created_by?: string | null;
60
+ owner_name?: string | null;
61
+ current_version?: number | null;
62
+ meta_data?: Rec | null;
63
+ }
64
+
65
+ export interface LibraryFolder {
66
+ id: string;
67
+ name: string;
68
+ parent_path: string;
69
+ full_path: string;
70
+ created_at?: string | null;
71
+ }
72
+
73
+ export interface Questionnaire {
74
+ id: string;
75
+ account_id?: string | null;
76
+ original_file_name?: string | null;
77
+ original_file_type?: string | null;
78
+ display_name?: string | null;
79
+ status?: string | null;
80
+ total_questions?: number | null;
81
+ answered_questions?: number | null;
82
+ escalated_questions?: number | null;
83
+ error_message?: string | null;
84
+ created_at?: string | null;
85
+ completed_at?: string | null;
86
+ questions?: Question[] | null;
87
+ }
88
+
89
+ export interface Question {
90
+ id: string;
91
+ question_text?: string | null;
92
+ question_topic?: string | null;
93
+ cell_reference?: string | null;
94
+ sheet_name?: string | null;
95
+ answer_text?: string | null;
96
+ final_answer?: string | null;
97
+ confidence?: string | null;
98
+ status?: string | null;
99
+ review_status?: string | null;
100
+ escalation_notes?: string | null;
101
+ guardrail_warnings?: string[] | null;
102
+ citations?: Rec[] | null;
103
+ }
104
+
105
+ export interface QuestionnaireProgress {
106
+ id?: string;
107
+ status?: string | null;
108
+ total_questions?: number | null;
109
+ answered_questions?: number | null;
110
+ escalated_questions?: number | null;
111
+ error_message?: string | null;
112
+ progress?: number | null;
113
+ }
114
+
115
+ export interface Correspondence {
116
+ id: string;
117
+ account_id?: string | null;
118
+ stakeholder_id?: string | null;
119
+ channel?: string | null;
120
+ direction?: string | null;
121
+ subject?: string | null;
122
+ content?: string | null;
123
+ source?: string | null;
124
+ external_id?: string | null;
125
+ correspondence_date?: string | null;
126
+ indexed_in_rag?: boolean;
127
+ created_at?: string | null;
128
+ }
129
+
130
+ export interface ImportResult {
131
+ imported?: number;
132
+ skipped?: number;
133
+ errors?: string[];
134
+ }
135
+
136
+ /** The rows of an open list payload: its `items` array (the contract always sends it). */
137
+ export function rowsOf<T>(data: unknown): T[] {
138
+ if (Array.isArray(data)) return data as T[];
139
+ const items = (data as { items?: unknown } | null | undefined)?.items;
140
+ return Array.isArray(items) ? (items as T[]) : [];
141
+ }
142
+
143
+ /** The `total` an open list payload may carry, or null when it sends none. */
144
+ export function totalOf(data: unknown): number | null {
145
+ const t = (data as { total?: unknown } | null | undefined)?.total;
146
+ return typeof t === "number" ? t : null;
147
+ }
148
+
149
+ export const extOf = (name: string): string => (name.includes(".") ? name.slice(name.lastIndexOf(".") + 1).toLowerCase() : "");
150
+
151
+ /** "12 KB", "1.4 MB"; an empty string when the size is unknown (callers show a dash). */
152
+ export function fmtSize(bytes: number | null | undefined): string {
153
+ if (typeof bytes !== "number" || !Number.isFinite(bytes) || bytes < 0) return "";
154
+ if (bytes < 1024) return `${bytes} B`;
155
+ if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024).toLocaleString("en-US")} KB`;
156
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
157
+ }
158
+
159
+ export const when = (v: string | null | undefined): string => {
160
+ if (!v) return "";
161
+ const d = new Date(v);
162
+ return Number.isNaN(d.getTime()) ? "" : d.toLocaleString();
163
+ };
164
+
165
+ /** Keep the files that may go up and say why the others cannot. The same files are counted in the confirm and sent. */
166
+ export function checkFiles(files: readonly File[], allowed: readonly string[], maxBytes: number): { ok: File[]; problems: string[] } {
167
+ const ok: File[] = [];
168
+ const problems: string[] = [];
169
+ for (const f of files) {
170
+ if (!allowed.includes(extOf(f.name))) problems.push(`${f.name}: only ${allowed.join(", ")} files can be added.`);
171
+ else if (f.size === 0) problems.push(`${f.name}: the file is empty.`);
172
+ else if (f.size > maxBytes) problems.push(`${f.name}: ${fmtSize(f.size)} is over the ${fmtSize(maxBytes)} limit.`);
173
+ else ok.push(f);
174
+ }
175
+ return { ok, problems };
176
+ }
177
+
178
+ /** The API's folder-name rule (letters, digits, spaces, hyphens, underscores; at most 50) and its three-level depth limit. */
179
+ export function folderNameProblem(name: string, parentPath: string): string | null {
180
+ const n = name.trim();
181
+ if (!n) return "Give the folder a name.";
182
+ if (n.length > 50) return "A folder name is at most 50 characters.";
183
+ if (!/^[\w\s-]+$/.test(n)) return "Use letters, numbers, spaces, hyphens and underscores only.";
184
+ if (`${parentPath}/${n}`.split("/").length > 3) return "Folders go three levels deep at most.";
185
+ return null;
186
+ }
187
+
188
+ /** A stored file name proposed from a title: lower case, hyphens, `.md`. The person can edit it. */
189
+ export function fileNameFor(title: string): string {
190
+ const base = title.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
191
+ return base ? `${base}.md` : "";
192
+ }
193
+
194
+ export const docName = (d: Pick<LibraryDoc, "display_name" | "file_name">): string => d.display_name || d.file_name || "Untitled document";
195
+ export const folderLabel = (path: string): string => (path === ROOT ? "Library" : path.replace(/^Library\//, ""));
196
+
197
+ export const QUESTIONNAIRE_STATUS: Record<string, { label: string; tone: Tone }> = {
198
+ pending: { label: "Waiting", tone: "neutral" },
199
+ processing: { label: "Answering", tone: "info" },
200
+ completed: { label: "Completed", tone: "positive" },
201
+ needs_review: { label: "Needs review", tone: "warning" },
202
+ failed: { label: "Failed", tone: "negative" },
203
+ };
204
+ export const statusInfo = (s: string | null | undefined): { label: string; tone: Tone } =>
205
+ QUESTIONNAIRE_STATUS[s ?? ""] ?? { label: s ? s.replace(/_/g, " ") : "Unknown", tone: "neutral" };
206
+ /** The filled workbook is only available in these states (the API refuses earlier). */
207
+ export const canDownload = (s: string | null | undefined): boolean => s === "completed" || s === "needs_review";
208
+ export const isWorking = (s: string | null | undefined): boolean => s === "pending" || s === "processing";
209
+
210
+ /** What a person should read as the state of one question. */
211
+ export function reviewState(q: Question): { label: string; tone: Tone } {
212
+ if (q.review_status === "skip") return { label: "Not applicable", tone: "neutral" };
213
+ if (q.review_status === "wrong") return { label: "Marked wrong", tone: "negative" };
214
+ if (q.status === "escalated") return { label: "Escalated", tone: "warning" };
215
+ if (q.status === "approved" || q.confidence === "confirmed") return { label: "Approved", tone: "positive" };
216
+ if (q.final_answer) return { label: "Edited", tone: "info" };
217
+ if (q.answer_text) return { label: "Answered", tone: "neutral" };
218
+ return { label: "No answer", tone: "warning" };
219
+ }
220
+
221
+ /** The answer the filled workbook carries: the person's edit when there is one, else the AI's. */
222
+ export const answerOf = (q: Question): string => q.final_answer || q.answer_text || "";
223
+
224
+ /** Whole-number percent, or null when there is nothing to divide. */
225
+ export function percentOf(done: number | null | undefined, total: number | null | undefined): number | null {
226
+ if (typeof done !== "number" || typeof total !== "number" || total <= 0) return null;
227
+ return Math.min(100, Math.round((done / total) * 100));
228
+ }
229
+
230
+ /** One piece of correspondence as the import call takes it, or the reason it cannot be sent. */
231
+ export function parseImportItems(text: string): { items: Rec[]; problem: string | null } {
232
+ let parsed: unknown;
233
+ try {
234
+ parsed = JSON.parse(text);
235
+ } catch {
236
+ return { items: [], problem: "That is not valid JSON." };
237
+ }
238
+ const list = Array.isArray(parsed) ? parsed : (parsed as { items?: unknown } | null)?.items;
239
+ if (!Array.isArray(list) || list.length === 0) return { items: [], problem: "Paste a JSON array with at least one message." };
240
+ const channels = CORRESPONDENCE_CHANNELS.map((c) => c.value as string);
241
+ const directions = DIRECTIONS.map((d) => d.value as string);
242
+ for (const [i, raw] of list.entries()) {
243
+ const it = raw as Rec;
244
+ const at = `Message ${i + 1}`;
245
+ if (!it || typeof it !== "object") return { items: [], problem: `${at} is not an object.` };
246
+ if (typeof it.content !== "string" || !it.content.trim()) return { items: [], problem: `${at} needs content.` };
247
+ if (typeof it.channel !== "string" || !channels.includes(it.channel)) return { items: [], problem: `${at}: channel must be ${channels.join(", ")}.` };
248
+ if (typeof it.direction !== "string" || !directions.includes(it.direction)) return { items: [], problem: `${at}: direction must be inbound or outbound.` };
249
+ if (typeof it.correspondence_date !== "string" || Number.isNaN(Date.parse(it.correspondence_date))) return { items: [], problem: `${at}: correspondence_date must be a date and time, for example 2026-09-30T14:00:00Z.` };
250
+ }
251
+ return { items: list as Rec[], problem: null };
252
+ }
@@ -0,0 +1,64 @@
1
+ "use client";
2
+
3
+ /**
4
+ * Save a file the API returns as raw bytes (a Library document in its original format, a filled questionnaire workbook).
5
+ * These two routes are NOT the JSON envelope, so `g8Get` (which parses JSON) cannot read them; this goes through the same
6
+ * /api/g8 proxy and keeps the key on the server.
7
+ *
8
+ * SDK operations: none directly. The callers declare downloadLibraryDocument / downloadSecurityQuestionnaire.
9
+ */
10
+
11
+ import { G8Error } from "@graph8/sdk/errors";
12
+ import { buildG8Url, transport } from "@/g8/lib/g8-client";
13
+
14
+ /** @internal Swappable in tests (jsdom has no object URLs). */
15
+ export const saver = {
16
+ save(blob: Blob, name: string): void {
17
+ const url = URL.createObjectURL(blob);
18
+ const a = document.createElement("a");
19
+ a.href = url;
20
+ a.download = name;
21
+ document.body.appendChild(a);
22
+ a.click();
23
+ a.remove();
24
+ setTimeout(() => URL.revokeObjectURL(url), 1000);
25
+ },
26
+ };
27
+
28
+ /** The file name a Content-Disposition header names (RFC 5987 `filename*` first), or null. */
29
+ export function nameFromDisposition(header: string | null): string | null {
30
+ if (!header) return null;
31
+ const star = /filename\*\s*=\s*(?:UTF-8|utf-8)''([^;]+)/.exec(header);
32
+ if (star) {
33
+ try {
34
+ return decodeURIComponent(star[1].trim());
35
+ } catch {
36
+ /* fall through to the plain form */
37
+ }
38
+ }
39
+ const plain = /filename\s*=\s*"?([^";]+)"?/.exec(header);
40
+ return plain ? plain[1].trim() : null;
41
+ }
42
+
43
+ /** Fetch `path` (relative to /api/v1) and hand the bytes to the browser as a download. Throws `G8Error` on any failure. */
44
+ export async function downloadFile(path: string, fallbackName: string): Promise<string> {
45
+ let resp: Response;
46
+ try {
47
+ resp = await transport.fetch(buildG8Url(path), { method: "GET", credentials: "same-origin" });
48
+ } catch (err) {
49
+ throw new G8Error({ message: `Network error: ${(err as Error)?.message ?? "request failed"}`, status: 0, type: "network_error", retryable: true });
50
+ }
51
+ if (!resp.ok) {
52
+ let message = `Request failed with status ${resp.status}`;
53
+ try {
54
+ const env = JSON.parse(await resp.text()) as { message?: string; detail?: unknown };
55
+ message = env.message || (typeof env.detail === "string" ? env.detail : message);
56
+ } catch {
57
+ /* keep the status line */
58
+ }
59
+ throw new G8Error({ message, status: resp.status, type: "api_error", code: String(resp.status), retryable: resp.status === 429 || resp.status >= 500 });
60
+ }
61
+ const name = nameFromDisposition(resp.headers.get("content-disposition")) || fallbackName;
62
+ saver.save(await resp.blob(), name);
63
+ return name;
64
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * @title Follow a security questionnaire until it is answered and list the questions that need a person
3
+ * @when After a questionnaire was uploaded in the app (answering uses credits, so a person confirms the upload; upload is
4
+ * multipart and not part of this typed client). Poll the cheap status read, not the full detail, until the status is no longer
5
+ * pending or processing. This only READS: it approves nothing, because an approval is a person's judgement. The filled workbook
6
+ * is available only once the status is completed or needs_review.
7
+ */
8
+ import { g8 } from "@graph8/sdk";
9
+
10
+ g8.init({ apiKey: process.env.G8_API_KEY! });
11
+
12
+ const accountId = process.env.G8_COMPANY_ID!;
13
+ const questionnaireId = process.env.G8_QUESTIONNAIRE_ID!;
14
+ const path = { account_id: accountId, questionnaire_id: questionnaireId };
15
+
16
+ let status = "pending";
17
+ for (let i = 0; i < 60 && (status === "pending" || status === "processing"); i++) {
18
+ const s = (await g8.api.documents.getSecurityQuestionnaireStatus({ path })).data as { status?: string; answered_questions?: number; total_questions?: number };
19
+ status = s.status ?? "unknown";
20
+ console.log(status, `${s.answered_questions ?? "-"} of ${s.total_questions ?? "-"} answered`);
21
+ if (status === "pending" || status === "processing") await new Promise((r) => setTimeout(r, 5000));
22
+ }
23
+
24
+ const detail = (await g8.api.documents.getSecurityQuestionnaire({ path })).data as { questions?: Array<{ id: string; question_text?: string; status?: string; review_status?: string; answer_text?: string | null }> };
25
+ const needsAPerson = (detail.questions ?? []).filter((q) => q.status !== "approved" && q.review_status !== "skip");
26
+ console.log(`${needsAPerson.length} questions still need review`, needsAPerson.slice(0, 5).map((q) => q.question_text));
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @title Import an account's messages so running it twice does not duplicate them
3
+ * @when Bringing emails, LinkedIn messages or call notes in from another system. Partial success is normal: the status is 200
4
+ * even when rows failed, so read `imported`, `skipped` and `errors`. A row whose `external_id` is already stored is SKIPPED,
5
+ * so send one on every row. `correspondence_date` is when the message HAPPENED, not when you import it: generated documents
6
+ * read it as the relationship timeline. `channel` is email, linkedin or call_notes; `direction` is inbound or outbound.
7
+ */
8
+ import { g8 } from "@graph8/sdk";
9
+
10
+ g8.init({ apiKey: process.env.G8_API_KEY! });
11
+
12
+ const accountId = process.env.G8_COMPANY_ID!; // a company id from g8.api.companies.listCompanies
13
+
14
+ const result = await g8.api.documents.importAccountCorrespondence({
15
+ path: { account_id: accountId },
16
+ body: {
17
+ items: [
18
+ { channel: "email", direction: "inbound", subject: "Re: pricing", content: "Thanks for the call, sending the quote today.", correspondence_date: "2026-09-30T14:00:00Z", external_id: "msg-1001" },
19
+ { channel: "call_notes", direction: "outbound", content: "Walked through the security review.", correspondence_date: "2026-10-02T16:30:00Z", external_id: "call-2002" },
20
+ ],
21
+ },
22
+ });
23
+ const r = result.data as { imported?: number; skipped?: number; errors?: string[] };
24
+ console.log(`${r.imported ?? 0} imported, ${r.skipped ?? 0} skipped, ${(r.errors ?? []).length} failed`);
25
+
26
+ const recent = await g8.api.documents.listAccountCorrespondence({ path: { account_id: accountId }, query: { limit: 10 } });
27
+ console.log((recent.data.items ?? []).length, "messages on the account");
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @title Create a Library folder and a text document in it, then list what the folder holds
3
+ * @when Seeding the Library from a script. The folder must exist before a document can land in it, and a folder name is
4
+ * letters, numbers, spaces, hyphens and underscores (at most 50), three levels deep at most. A text document is stored exactly
5
+ * as sent (no extraction). Nothing here reaches anyone. To add a FILE (PDF, Word ...) the API takes multipart, which this
6
+ * typed client does not send: post it with `fetch` and a `FormData` to /api/v1/library/documents/upload.
7
+ */
8
+ import { g8 } from "@graph8/sdk";
9
+
10
+ g8.init({ apiKey: process.env.G8_API_KEY! });
11
+
12
+ const folder = await g8.api.documents.createLibraryFolder({ body: { name: "Case studies 2026", parent_path: "Library" } });
13
+ const folderPath = String((folder.data as { full_path?: string }).full_path ?? "Library/Case studies 2026");
14
+
15
+ const doc = await g8.api.documents.createLibraryDocument({
16
+ body: {
17
+ folder_path: folderPath,
18
+ file_name: "acme-rollout.md",
19
+ display_name: "Acme rollout",
20
+ content: "Acme moved 400 seats in six weeks.",
21
+ },
22
+ });
23
+
24
+ const inFolder = await g8.api.documents.listLibraryDocuments({ query: { folder_path: folderPath } });
25
+ console.log(folderPath, (doc.data as { id?: string }).id, (inFolder.data.items ?? []).length);
@@ -0,0 +1,123 @@
1
+ # email-signatures
2
+
3
+ Managed email signatures: design **one signature for the team** and keep every mailbox on it. A *template* is the shared
4
+ design (blocks, layout, font, colour, with placeholders such as `{{member.name}}`). A *deployment* binds one mailbox to one
5
+ employee and a template, and the platform installs or applies it. A *policy* decides whether signatures are managed at all,
6
+ which template is the default, and whether employees may change the unlocked parts. A *campaign* adds a banner to signatures
7
+ for a time window. Changing what a mailbox sends is consequential, so every such action is confirmed with the real number of
8
+ mailboxes it touches.
9
+
10
+ ## Screens
11
+
12
+ `EmailSignaturesScreen` (`components/email-signatures-screen.tsx`) is the finished page: templates, mailboxes, campaigns,
13
+ policy and report.
14
+
15
+ ```tsx
16
+ import { EmailSignaturesScreen } from "@/g8/email-signatures/components/email-signatures-screen";
17
+ export default function Page() { return <EmailSignaturesScreen />; }
18
+ ```
19
+
20
+ Tabs (`?tab=`): **Templates**, **Mailboxes**, **Campaigns**, **Policy**, **Report**. Open a template (`?id=`) for Preview,
21
+ Edit, Use on mailboxes and Details; `&view=full` shows it as a full page.
22
+
23
+ Flows: see every template (the list is complete: the API does not page it); create one from blocks and placeholders with a
24
+ live preview; preview a stored template as a sample or as a real employee on a real mailbox (full, compact, with the campaign
25
+ banner, or plain text); edit it (the form opens only after the full record has loaded, and a stale save is refused with
26
+ "Load the newer version"); apply it to chosen mailboxes, a department or every mailbox; make it the organisation default;
27
+ archive it. On **Mailboxes**: assign an employee to a mailbox (with a setup check first), push, restore the old signature and
28
+ change what a mailbox sends, one by one or in bulk. **Campaigns**: create and edit banner campaigns, preview the banner, and
29
+ test which mailbox and recipients it would reach. **Policy**: off, managed or enforced, default and department templates.
30
+ **Report**: impressions and clicks by mailbox and campaign.
31
+
32
+ Parts you can mount alone: `SignatureTemplateForm`, `TemplatePreview` (and `SafeSignatureFrame`), `SignatureAssignTemplate`,
33
+ `SignatureTemplateDetails`, `SignatureMailboxes`, `SignatureIdentityForm`, `SignaturePolicy`, `SignatureCampaigns`,
34
+ `SignatureCampaignForm`, `SignatureReport`, `SignatureBlockEditor`.
35
+
36
+ ## Install
37
+
38
+ ```
39
+ npx @graph8/sdk add email-signatures
40
+ ```
41
+
42
+ Installs `foundation` and `mailboxes` first if missing. Everything lands under `g8/email-signatures/`. The block does not use the `pickers` block on purpose: its `MemberPicker` reports a
43
+ user id, while a signature is assigned to a *directory employee* (`listSignatureMembers`, a different id), and the mailbox
44
+ list is merged with each mailbox's signature status, which no generic picker knows.
45
+
46
+ ## Prerequisites (the human must do these)
47
+
48
+ | Where | What | How to verify |
49
+ |---|---|---|
50
+ | graph8 | Mailboxes connected (Google or Microsoft in the app, or the mailboxes block). A signature is bound to a mailbox. | `GET /api/v1/mailboxes` lists it. |
51
+ | graph8 | The people who sign are team members **with an email address**. An employee without one cannot be assigned. | `GET /api/v1/signatures/members` lists them. |
52
+ | The app | Installing a signature inside a Google account needs the Google Workspace integration with the signature permission, granted by a Workspace admin. A deployment stuck at `needs_admin_grant` is missing it. Without native installation the signature is applied when mail is sent from the platform. | A deployment leaves `needs_admin_grant`. |
53
+ | graph8 key | `signatures:read`, `signatures:write`, `signatures:delete` (archive). `campaigns:read` lists mailboxes. | `g8.api.me.describeCurrentKey()` |
54
+ | Your app | `allowedPrefixes` must include `signatures` and `mailboxes`. | The route file lists them. |
55
+
56
+ ## The flow, in order (IDs passed between steps)
57
+
58
+ 1. **Template**: `createSignatureTemplate({ data: { name, layout, blocks, primary_color, font, brand_source } })` -> **`template.id`** and `template.version`. Exactly one required, locked `identity` block; at most one optional `banner`; unique block ids; required blocks are locked.
59
+ 2. **Preview**: `previewSignatureTemplate({ data })` for a draft, `previewSavedSignatureTemplate(template.id, { mailbox_id, member_id })` for a stored one. Pass **both** `mailbox_id` and `member_id` to render with that employee's real details, neither for sample data. Reads only.
60
+ 3. **Policy**: `getSignatureSettings` -> **`version`** (the *policy* version). `setDefaultSignatureTemplate(template.id, { version })` makes the template the default (confirmed). `updateSignatureSettings({ version, data })` turns management off, on (`default`) or `enforced`; on needs a default template, enforced needs every active mailbox assigned.
61
+ 4. **Employee and mailbox**: `listSignatureMembers` -> **`member.id`**; the mailbox from `listMailboxes` -> **`mailbox.id`**; `listSignatureDeployments` -> the mailbox's **`revision`** (0 when it has no assignment).
62
+ 5. **Check**: `previewSignatureSetup({ mailbox_id, data })` is a dry run against the mail provider (account and send-as resolve).
63
+ 6. **Assign**: `assignSignatureIdentity({ mailbox_ids, expected_revisions: { [mailbox.id]: revision }, confirm_takeover: true, data: { member_id, account_subject, send_as_email, template_id? } })` behind a confirmation. `account_subject` must be the employee's own directory email and `send_as_email` the mailbox address.
64
+ 7. **Bulk**: `assignSignatureTemplate(template.id, { mailbox_ids } | { department } | { all_mailboxes: true })` (exactly one; every target already assigned), `pushSignatures({ mailbox_ids })`, `pushSignature(mailbox.id, { version: revision })`.
65
+ 8. **Change or undo**: `changeSignatureSource(mailbox.id, { version, source: "org" | "template" | "custom", template_id? })`; `restoreSignature(mailbox.id, { version })` puts back what the mailbox had before (refused while the policy is `enforced`; parked as `restore_conflict` if the signature was edited elsewhere since).
66
+ 9. **Campaign**: `createSignatureCampaign({ data })` (banner block, `starts_at` / `ends_at` timezone-aware, status `draft`, `scheduled` or `paused`); only a `scheduled` campaign inside its dates is added. `previewSignatureCampaign` renders it and, with a mailbox and recipients, says whether it would apply.
67
+ 10. **Read**: `getSignatureReport`.
68
+
69
+ ## Default decisions (do not ask the human unless marked)
70
+
71
+ | Decision | Default | Ask? |
72
+ |---|---|---|
73
+ | New template | Classic stacked, Arial, text colour `#222222`, brand kit colour and font, one required locked identity block (the API's own defaults) | no |
74
+ | Which mailboxes get a template | None: a person chooses the mailboxes, a department or everyone and confirms the count | **yes** |
75
+ | Policy mode | Left as it is | **yes** |
76
+ | Assigning an employee to a mailbox | Account = the employee's directory email, send-as = the mailbox address, after the person agrees to the takeover | **yes** |
77
+ | Stopping a campaign | Set it to Paused (there is no delete) | no |
78
+ | Removing a template | Archive it, after giving its mailboxes another template | no |
79
+ | After editing a template | Push the mailboxes that use it | no |
80
+
81
+ ## Operation coverage (24 signatures operations, all reachable from `EmailSignaturesScreen`)
82
+
83
+ | Operation | Where |
84
+ |---|---|
85
+ | `listSignatureTemplates`, `getSignatureTemplate` | template list; the edit form (full record) |
86
+ | `createSignatureTemplate`, `updateSignatureTemplate` | `SignatureTemplateForm` |
87
+ | `archiveSignatureTemplate` | `SignatureTemplateDetails` (destructive, confirmed; disabled while the template is in use) |
88
+ | `setDefaultSignatureTemplate` | `SignatureTemplateDetails` (confirmed, sized from the mailboxes that follow the default) |
89
+ | `assignSignatureTemplate` | `SignatureAssignTemplate` (confirmed, sized from the mailboxes the choice covers) |
90
+ | `previewSignatureTemplate`, `previewSavedSignatureTemplate` | `TemplatePreview` |
91
+ | `listSignatureMembers` | employee menus |
92
+ | `listSignatureDeployments` | `SignatureMailboxes` and every count in a confirmation |
93
+ | `assignSignatureIdentity`, `previewSignatureSetup` | `SignatureIdentityForm` (takeover confirmed; setup check first) |
94
+ | `pushSignature`, `pushSignatures`, `restoreSignature`, `changeSignatureSource` | `SignatureMailboxes` (each confirmed) |
95
+ | `getSignatureSettings`, `updateSignatureSettings` | `SignaturePolicy` (save confirmed) |
96
+ | `listSignatureCampaigns`, `createSignatureCampaign`, `updateSignatureCampaign`, `previewSignatureCampaign` | `SignatureCampaigns`, `SignatureCampaignForm` |
97
+ | `getSignatureReport` | `SignatureReport` |
98
+
99
+ No operation of the namespace is skipped. The block also reads `listMailboxes` (the mailboxes to assign). Previews and the setup check are POSTs, so the contract tiers them as `write`; they change nothing.
100
+
101
+ ## API gaps (what is asked for that the API does not have)
102
+
103
+ | Asked for | Reality | What the block does |
104
+ |---|---|---|
105
+ | A list of template variables | None. The renderer resolves a fixed set (`member.*`, `mailbox.*`, `company.*`, `brand.*`). | A built-in placeholder list that mirrors the renderer (`lib/signatures-types.ts`). `{{x\|fallback}}` is supported. |
106
+ | Delete a template or a campaign | Templates can only be archived (refused while in use); campaigns cannot be removed. | Archive with the reason it is blocked; pause a campaign. |
107
+ | Read one campaign | There is `list` only. | Edit seeds from the list row, which is the complete stored campaign, and refreshes the list after a save. |
108
+ | Paging and filters | The template, member, deployment and campaign lists take no paging parameters and return everything. `listMailboxes` takes `limit` up to 500 and no page. | The UI says "All N"; a mailbox read that comes back full says "Showing the first 500 mailboxes". |
109
+ | Mailboxes with no signature yet | `listSignatureDeployments` only lists mailboxes that already have a deployment. | Merged with `listMailboxes` (needs `campaigns:read`); without that scope only the assigned mailboxes show, with a note. |
110
+ | Upload a logo or photo | Multipart uploads are not in the developer API. | Images are referenced by address. |
111
+ | Pick audience lists for a campaign | The block has no list picker (it does not depend on the lists block). | A campaign that already targets lists keeps them on every save and can have the targeting removed; add lists through `createSignatureCampaign` / `updateSignatureCampaign` (`recipient_list_ids`). |
112
+ | Employee self-service (`/me`) | An API key has no seat or directory identity. | Out of scope. Employee overrides are carried over unchanged when an assignment is edited. |
113
+ | Choose the Workspace integration | The integrations used by signatures are not listed by the API. | An existing assignment's integration is carried over; a new one lets the API pick. |
114
+ | Click-tracking redirect | Public, unauthenticated by design. | Not an API capability. |
115
+ | Edit part of a template | `updateSignatureTemplate` and `updateSignatureSettings` replace the document. | The whole template is sent, built from the full record that was read; a cleared field is sent empty (`""`), a cleared default template as `null`; a save with no change sends nothing. |
116
+
117
+ ## Things that surprise people
118
+
119
+ - Every write carries the version or revision that was read. A concurrent change answers **409**; the screen says so and offers the newer version. The **policy** has its own version, used by set-default and the policy save.
120
+ - Signature HTML comes from the API and a template can carry custom markup, so the preview shows it in a **sandboxed frame** with no scripts. Never put signature HTML into your own page with `dangerouslySetInnerHTML`.
121
+ - A required block with a placeholder that has no fallback fails to render for an employee whose directory entry lacks that value. Add `{{member.title|Team}}`.
122
+ - Campaign targeting by audience list applies only to mail sent from a sequence. Dates are picked in the viewer's time zone and sent as timezone-aware instants.
123
+ - Enforced mode refuses a restore and employee changes; turning management **off** stops managing every mailbox.