# Portwood DocGen — User Guide Build polished Word and PDF documents from any Salesforce record. Author your template in Word, Google Docs, or any tool that exports HTML — drop in merge tags, and DocGen renders the rest. Everything runs natively inside your Salesforce org, with the same record sharing and field-level security your users already have. PowerPoint and Excel templates are also supported as **alpha-stage** formats — see [§2](#2-what-docgen-does) for what to expect. [Install in Production](https://login.salesforce.com/packaging/installPackage.apexp?p0=04tVx000000nI5RIAU) · [Install in Sandbox](https://test.salesforce.com/packaging/installPackage.apexp?p0=04tVx000000nI5RIAU) · [Support](https://portwood.dev/support) · [Expert Services](https://portwood.dev/services) --- ## Table of contents 1. [Five-minute quick start](#1-five-minute-quick-start) — install → first PDF in five steps 2. [What DocGen does](#2-what-docgen-does) 3. [Install & post-install setup](#3-install--post-install-setup) 4. [Permission sets](#4-permission-sets) 5. [Templates](#5-templates) 6. [Query builder](#6-query-builder) — including [Apex Data Provider (V4)](#66-apex-data-provider-v4--class-backed-templates) 7. [Merge tag reference](#7-merge-tag-reference) 8. [Document generation](#8-document-generation) 9. [Bulk generation](#9-bulk-generation) 10. [E-signatures](#10-e-signatures-v3) 11. [Flow automation cookbook](#11-flow-automation-cookbook) 12. [Apex API reference](#12-apex-api-reference) 13. [Admin & settings](#13-admin--settings) 14. [Limits & known constraints](#14-limits--known-constraints) 15. [Troubleshooting](#15-troubleshooting) --- ## 1. Five-minute quick start The fastest path from a fresh install to your first generated PDF. Every step takes < 1 minute. ### Step 1 — Install Pick the install link above for your environment. Production for live orgs, Sandbox for sandboxes/scratch orgs. Click **Install for Admins Only**, accept third-party access, and wait for the green checkmark. ### Step 2 — Assign yourself the admin permission set 1. **Setup → Users → Permission Sets** 2. Click **DocGen Admin** → **Manage Assignments** → **Add Assignments** 3. Check the box next to your user → **Next** → **Assign** You can now create templates and generate documents. ### Step 3 — Enable the PDF rendering release update DocGen renders PDFs through Salesforce's Visualforce PDF service, which sits behind a release update. Without this, image-heavy PDFs fail silently. 1. **Setup → Release Updates** 2. Find **"Use the Visualforce PDF Rendering Service for Blob.toPdf() Invocations"** 3. Click **Get Started** → **Enable** One-time. Applies to the whole org. If the release update page looks like it might already be enabled but the button is grayed out and says **Enable Test Run**, it is **not enabled** in that org. When the update is active, Salesforce shows **Disable Test Run**. If you cannot enable it yourself, open a Salesforce Support case and ask Salesforce to enable the Visualforce PDF Rendering Service for `Blob.toPdf()` invocations in the affected org. A common symptom is raw CSS appearing at the top of generated PDFs. ### Step 4 — Make your first template 1. **App Launcher → DocGen** (search "DocGen" if it's not pinned). You land on the **Template Library**. 2. On the **Create New** tab, choose how you want to build. **Start from a Design** is the recommended path — pick a professional starter layout and your template renders on the very first click, no file authoring needed: ![The Create New wizard with the starter gallery — Record Report, Invoice, Business Letter, Agreement, and Certificate designs](docs/images/userguide/wizard-starters.png) - **Start from a Design** — a starter gallery (Record Report, Invoice / Line Items, Business Letter, signature-ready Agreement, landscape Certificate / Award). Your object's real merge fields are dropped in automatically. - **Generate with AI** — DocGen assembles a ready-to-paste prompt (your fields + the full tag syntax + PDF rendering constraints). Paste it into Claude, ChatGPT, or Copilot, then paste the HTML it returns back into the wizard. - **Start From Scratch** — a blank page in the visual designer. Click anywhere and type; hit `` ` `` for the insert menu. - **I Have an Existing File** — upload a Word, PowerPoint, Excel, fillable PDF, or HTML file you already maintain. 3. Give it a **Template Name** (e.g. "My First Account Brief"), pick a **Starter Design**, optionally choose a company logo from your Asset Library and a **Sample Record** for live previews, then click **Create & Open Designer**. 4. The **visual designer** opens on your document — a WYSIWYG page where merge fields appear as purple pills, tables resize by dragging cell edges, and the toolbar covers text formatting, tables, borders, images, and page setup. Edit anything (or nothing), then click **Save as New Version** to activate it: ![The visual designer editing a template — format toolbar, merge-tag pills, WYSIWYG page canvas](docs/images/userguide/designer-certificate.png) 5. Click **Download Sample** in the designer toolbar to see the rendered PDF with your sample record's real data — before you ever leave the page. ### Step 5 — Generate 1. Open any Account record. 2. Click the **DocGen Runner** component on the page (placed there automatically by the install). If it's not visible, edit the page in Lightning App Builder and add the **DocGen Runner** component. 3. Pick **My First Account Brief** → click **Generate**. 4. The PDF appears in the record's **Files** related list, and downloads to your browser. 🎉 **You've now done the full lifecycle.** Real templates layer on richer content — child loops for tables (e.g., line items), images, conditional sections, charts, barcodes, e-signatures — all using the same merge-tag patterns. Your growing library lives on the **Your Templates** tab (searchable, sortable, with created/modified dates): ![The Your Templates list — search, sortable columns, created and last-modified dates](docs/images/userguide/templates-list.png) The rest of this guide covers each capability with worked examples. > **Stuck?** The most common first-time issue is the runner not showing on the page layout. Edit the page → drag in the **DocGen Runner** component → save. If you see "Insufficient privileges," confirm Step 2 — the perm set assignment. --- ## 2. What DocGen does A native Salesforce document generation engine that turns merge-tag templates into rendered files. **Build with:** - **Word (`.docx`)** templates — the most common authoring tool, fully supported. - **HTML / Google Docs / Notion / ChatGPT** — any HTML source. Fully supported. - **PowerPoint (`.pptx`)** for slide decks. _Alpha — see note below._ - **Excel (`.xlsx`)** for spreadsheets. _Alpha — see note below._ **Render to:** - **PDF** — the universal share format (Word and HTML templates → PDF). - **DOCX** — keep the native format if you'd rather edit in Word. - **PPTX, XLSX** — output the native format. _Alpha — see note below._ > **PowerPoint and Excel are alpha-stage.** Core merge mechanics work — `{Field}`, parent lookups, basic loops, format suffixes. We're actively investing in these formats and the surface area will keep growing release over release. Until then, expect rough edges (PowerPoint→PDF isn't supported by the Salesforce platform, complex Excel formulas may not survive merging, advanced PPTX layouts are best-effort). For mission-critical decks and spreadsheets today, render to PDF or DOCX. Hit [portwood.dev/support](https://portwood.dev/support) with what you'd like prioritized. **Pull data from:** - Any Salesforce record — Account, Opportunity, Case, custom objects, anything - Multi-level parent lookups (`{Account.Owner.Manager.Email}`) - Child relationships at any depth (Opportunity → Line Items → Product → Pricebook) - Many-to-many junctions - External APIs / computed values via the [Apex Data Provider](#66-apex-data-provider-v4--class-backed-templates) **Run from:** - The record page (one-click Generate) - The Bulk Generation tab (mass runs across thousands of records) - Salesforce Flow (every major operation has an invocable action) - Apex (call `DocGenService.generateDocument` from your own classes) - Public Salesforce Sites (e-signatures with PIN verification, branded emails, audit trail) **Built for the platform:** - 100% native — no external services, no API callouts, no third-party data hand-offs - Honors the running user's record sharing and field-level security automatically - Handles small and huge datasets through the same Generate button — no async/sync toggle to think about - Uses platform-native fonts in PDF; DOCX preserves whatever fonts your template already has --- ## 3. Install & post-install setup ### Install the package ```bash sf package install --package 04tVx000000nI5RIAU --wait 10 --target-org ``` Or use the install links at the top of this guide. The install bundles the merge engine, all Lightning components, custom objects, permission sets, and the e-signature Visualforce pages. ### Post-install checklist 1. **Assign the `DocGen_Admin` permission set** to yourself — Setup → Users → Permission Sets → DocGen Admin → Manage Assignments → Add. See [§4](#4-permission-sets) for who needs what. 2. **Add `HTML` and `Excel` to two `Type` picklists** _(one-time, see callout below)_ — required before you can create HTML or Excel templates. 3. **Enable the Visualforce PDF Rendering Service release update** — Setup → Release Updates → "Use the Visualforce PDF Rendering Service for `Blob.toPdf()` Invocations" → Get Started → Enable. Mandatory for PDF output. If the page shows a grayed-out **Enable Test Run** button, the update is still off for that org; open a Salesforce Support case and ask Salesforce to enable it. 4. **Open the DocGen app** from the App Launcher. The Command Hub is your home base for managing templates, running bulk jobs, and configuring signatures. 5. **For e-signatures only** — run through the [signature admin setup](#1012-admin-setup-one-time) before sending the first signature request: Site URL, Org-Wide Email Address, guest permission set on the site's guest user. > ⚠️ **One-time picklist setup for HTML and Excel templates** > > Salesforce 2GP managed packages don't reliably propagate new picklist value-set additions on upgrade — the values defined in the package source can silently fail to install, leaving the runtime picklist behind. If you try to save an HTML (or Excel) template before doing this step, you'll see: > > ``` > INVALID_OR_NULL_FOR_RESTRICTED_PICKLIST, Type: bad value for restricted picklist field: HTML: [portwoodglobal__Type__c] > ``` > > Add the missing values **on both objects**: > > 1. Setup → Object Manager → **DocGen Template** → Fields & Relationships → **Type** → New under "Picklist Values" → add `HTML`, then `Excel`. Save. > 2. Setup → Object Manager → **DocGen Template Version** _(separate object, similar name)_ → Fields & Relationships → **Type** → New → add `HTML`, then `Excel`. Save. > > No data migration needed — existing rows aren't touched. Once both picklists list all four values (Word, PowerPoint, Excel, HTML), the Save flow in the Command Hub works normally for every template type. A future package release will move these picklists to a Global Value Set so this step goes away. --- ## 4. Permission sets Three permission sets ship with the package. Assign what each user needs. | Permission set | Who gets it | What they can do | | ------------------------ | ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `DocGen_Admin` | Template authors, system admins | Full CRUD on templates, query configs, signature objects, settings. Can create/edit/delete templates. Can "Sign In Person" bypass for signatures. | | `DocGen_User` | End users generating docs | Sees every active template in the picker — no sharing setup needed (v3.29+; audience narrowed by the §5.5 visibility controls). Generate single + bulk documents. Create signature requests. Can't create, edit, or delete templates or settings. | | `DocGen_Guest_Signature` | The Salesforce Site guest user (for external signers) | Read access to signature requests, signers, and placements. Create access on audit records. Access to the signing pages. Required for external signers without a Salesforce login. | ### Assigning a permission set 1. **Setup → Users → Permission Sets** 2. Click the permission-set name (e.g., **DocGen Admin**) 3. Click **Manage Assignments → Add Assignments** 4. Check the box next to each user → **Next** → **Assign** > **Assign deliberately — do not use the select-all checkbox.** `DocGen_User` includes read-only _View All_ on DocGen Template (v3.29+, see §5.4). Some Salesforce user licenses — **Chatter Free**, **Identity**, and **Customer/Partner Community** among them — do not permit View All Records on a custom object. Assigning `DocGen_User` to those users is not just unnecessary (they cannot generate documents anyway); it will **block your next package upgrade**, because Salesforce re-validates every existing assignment against its user license when the permission set changes. The install fails with: > > ``` > PermissionSet(DocGen_User) The user license doesn't allow the > permission: View All portwoodglobal__DocGen_Template__c > ``` > > This most often bites orgs that ran "select all" on the assignment screen during first-time setup, then upgraded from a pre-v3.29 version. To find offending assignments before you upgrade: > > ```sql > SELECT Assignee.Name, Assignee.Profile.UserLicense.Name > FROM PermissionSetAssignment > WHERE PermissionSet.Name = 'DocGen_User' > ``` > > Remove `DocGen_User` from any user whose license is not a full Salesforce license, then upgrade. The same applies to `DocGen_Admin`, which carries View All on more objects — keep it on genuine administrators only. ### Assigning DocGen_Guest_Signature to a Site guest user Salesforce hides guest users behind several clicks. Full path: 1. **Setup → Sites** → click your e-signature site's name. 2. Click **Public Access Settings** — opens the site profile. 3. From the profile, click **View Users**, then select the e-signature site guest user. 4. On the user record, scroll to **Permission Set Assignments** and add **DocGen Guest Signature**. ### Adding custom fields to DocGen objects? Update all three permission sets in the same change. Missed FLS grants silently break field access for the affected role (signer can't read it, generator can't populate it). --- ## 5. Templates ### 5.1 Creating a template Open the DocGen app → **My Templates** → the **Create New** tab. The wizard offers four authoring paths: - **Start from a Design** (recommended) — pick a professional starter (Record Report, Invoice / Line Items, Business Letter, signature-ready Agreement, landscape Certificate / Award). Your object's real merge fields are injected automatically and the template renders on the first click. Starters that need a specific page setup bring it along — the Certificate creates a landscape Letter page with 0.5" margins without you touching anything. - **Generate with AI** — a six-step flow, top to bottom on one page: build your query in the full visual builder (fields, parent lookups, related lists with filters), pick your images, describe the document in your own words, copy the assembled prompt (it carries DocGen's full tag syntax and the PDF engine's CSS rules), paste it into Claude / ChatGPT / Copilot, then paste the returned HTML back. The prompt updates live as you build. The result opens in the designer. - **Start From Scratch** — a blank page in the visual designer. - **I Have an Existing File** — upload **Word** (`.docx`), **HTML** (`.html` / `.htm` / `.zip`), fillable **PDF** (_testing_), **PowerPoint** (`.pptx`, _alpha_), or **Excel** (`.xlsx`, _alpha_) containing `{FieldName}` merge tags. Every path also asks for: 1. A **Template Name** (and optionally an API Name and Category under Advanced options). 2. A **base object** (Account, Opportunity, Case, any custom object). The picker ranks **standard objects first** — when an org has many namespaced custom objects whose names contain `Account`, `Opportunity`, etc. (common with payment processors and managed packages), the standard `Opportunity` always appears at the top of the list with a green **Standard** pill. Up to 50 matches render with a scroll. 3. The query — which fields, which child relationships (see [§6](#6-query-builder)). On the design/AI paths a field checklist builds it for you. 4. The default **output format** (PDF or the native format). 5. An optional **Sample Record**, so previews and Download Sample show real data. **Output formats by template type:** - Word (`.docx`) template → output PDF **or** DOCX (pick one when you save the template) - HTML (`.html` / `.htm` / `.zip`) template → output PDF only (see [§5.7](#57-html-templates-google-docs-notion-any-html-source)) - PDF (`.pdf`) fillable template → output PDF only (PDF-to-PDF / AcroForm filling is currently in testing) - PowerPoint (`.pptx`) template → output PPTX only (PowerPoint→PDF is not supported by the Salesforce platform) - Excel (`.xlsx`) template → output XLSX only > **One template → one output format.** Templates render in whatever format > they were saved with — there's no runtime "Output As" picker. To offer the > same source as both PDF and DOCX, save the template twice (one as PDF, one > as Native) and let users pick the one they want. > **Max template file size: 10 MB.** The uploader rejects anything larger with > a clear toast. Almost every 20+ MB template is uncompressed images — in > Word, right-click any image → **Compress Pictures → Email (96 ppi)** or > **Web (150 ppi)** — most templates drop to 1–2 MB with no visible quality > loss. See [§14.8](#148-template--output-size-guidance) for the full > breakdown of why this limit exists and what generation flows it affects. #### Fillable PDF templates (PDF-to-PDF, testing) DocGen can upload a fillable PDF form, read its AcroForm fields in the browser, and map those fields to Salesforce data. The generated output stays a PDF: DocGen fills the mapped fields server-side so the same template can run for a single record, Generate Sample, Flow/API generation, and bulk generation. This feature is currently in testing. It is designed for standard AcroForm PDFs. Many government forms work, including forms whose field dictionaries are stored in PDF object streams after the browser prepares a server-ready copy. XFA-only forms and unusual encrypted forms may still need additional support. For bulk jobs, fillable PDF templates support **Individual Files** mode. Merged PDF / Merge Only modes are not available for fillable PDF templates because those modes combine HTML render snippets, while fillable PDFs are generated by filling the original PDF form directly. Basic workflow: 1. Create or edit a template with **Type = PDF**. 2. Upload the fillable `.pdf`. 3. After upload, open the **Fillable Fields** tab. DocGen scans the PDF and lists the fields in page/position order. 4. Use **Label / Fillable Field** to add your own human label, such as `Checkbox 3c S-Corp`. The original PDF field name remains visible underneath for troubleshooting. 5. Pick a **Data Path** from the template query, or enter a static/formula-style value where appropriate. 6. For checkboxes/radio buttons (`Btn` fields), map the data path to a boolean-like value and set **Checked Value** to the PDF's on-state value when needed. Common Salesforce values such as `true`, `yes`, `1`, and `checked` are treated as checked. 7. Click **Save as New Version**. DocGen saves the field mapping snapshot and prepares a server-ready PDF body for generation. 8. Later edits to labels, mappings, or checked values can be made directly on the **Fillable Fields** tab with **Save Mapping**. Important notes while the feature is in testing: - The mapped field list is based on the PDF structure, not visual text recognition. Use the page, position, and friendly label to make large forms navigable. - If DocGen says the mapping does not match the active PDF body, save the PDF as a new template version so the mapping and server-ready body are regenerated together. - Browser-side scanning is used only to understand and normalize the PDF. Actual generation happens server-side, including Generate Sample and bulk generation. - Fillable fields generally remain fillable/editable in the generated PDF. Flattening output is not the default behavior. ### 5.1.1 API Name — a stable key for automation (v3.28+) Every template has an optional **API Name** — a unique developer key like `Opp_Close_Summary`. Flows reference it via the **Template API Name** input on the DocGen actions instead of a record Id, so automations survive sandbox→production deploys with nothing to remap. Letters, numbers, and underscores; must start with a letter; must be unique when set; existing templates work fine with it blank. ### 5.1.2 The Visual Designer (Beta) HTML templates open in a full WYSIWYG designer — the page you see is the page that renders. Merge tags appear as purple pills (loop and section markers in green) that move and delete as single objects, so tag syntax can't be half-mangled while editing. ![The visual designer on a landscape certificate — page pickers, format toolbar, merge-tag pills](docs/images/userguide/designer-landscape-certificate.png) **Around the canvas:** - **Toolbar** — Download Sample (rendered PDF with your sample record), Copy AI Prompt, PDF Preview, Format Code, Reload, **Save as New Version** (the one way to activate your edits), and Edit Template (jumps to the settings modal). - **Page setup** — size, orientation, and margins pickers write a clean `@page` rule into your document. If your HTML declares its own `@page`, the engine defers to it. - **Panels** — `+ Insert` (blocks, tables, charts, barcodes, special characters — or press `` ` `` anywhere on the page), `{} Tags` (your query's merge fields as clickable chips), Images (the shared Asset Library), Query (edit fields without leaving the designer), Versions, Header/Footer, and Watermark. **Text formatting:** bold/italic/underline/strike (the buttons read as _pressed_ when your cursor sits on formatted text — click again to unformat), super/subscript, lists, alignment, a **numeric point-size box** with −/+ steppers (any exact 6–96pt value; it reads the size at your cursor), text and highlight colors, font families, Unicode-safe special characters, undo/redo. **Merge tags style like the text around them (v3.36+).** A `{Name}` pill inside a 24pt serif heading renders in 24pt serif — what you see is what the merged value prints. You can also style a tag directly: click the pill and use the toolbar (bold, italic, underline, strike, color, font, size). The styling serializes as a styled span around the tag and survives save/reopen, so the engine merges the value _inside_ your formatting. **Tables — Excel-level editing:** - **Drag any cell edge** to resize columns (the neighbor gives way, so the table keeps its footprint); drag a bottom edge to change row heights. This works on _any_ table — pasted, Word-converted, or hand-written — not just designer-inserted ones. - **Drag across cells** to select a rectangle (purple outline). Fill colors every selected cell; Merge combines them (and leaves the merged cell selected so Split is one click away). - Add/remove rows and columns, header rows, repeat-header for multi-page tables. - **Borders** — All / Outline / Rows / None styles, plus a thickness picker (Hairline to Heavy) and a border color; changing either restyles the table under your cursor live. - Rows carry `page-break-inside: avoid` automatically, so a PDF page never splits mid-row. **Images:** assets render as real images on the canvas. Drag the image body to move it anywhere in the text flow (a drop marker tracks your pointer); drag the bottom-right corner to resize (the size is written back into the merge tag); click it and use Left / Center / Right to align it; double-click it to edit the underlying tag (`{%asset:logo:120x}`) by hand. In header/footer HTML you can write `` and DocGen substitutes the image URL; unsized header/footer images are automatically clamped to fit the margin box. **Query panel:** the designer's Query button opens the full visual query builder (see [§6.4](#64-using-the-visual-builder)) in a wide flyout — walk from the base object into fields, parent lookups, and related lists without leaving the designer. Changes flow straight into the tag palettes. **The canvas owns the scroll** — the toolbar stays fixed above the page while the document scrolls, Google-Docs style. **Watermark:** upload a background image on the Watermark panel and pick a **strength** (Light 15% / Medium 30% / Strong 50% / Original). The opacity is baked into the image at upload, so the PDF and the canvas preview match exactly. Requires a saved version first (the file attaches to the version record). **Right-click** anywhere on the page for a searchable menu of every insert and table action. Where you'll see it (v3.29+): - **Create wizard** — the API Name auto-fills from the Template Name as you type (`Opp Close Summary` → `Opp_Close_Summary`). Edit it to take manual control, or clear it to re-sync with the name. - **Template editor → Settings tab** — shown right under the Template Name, in the Template section. - **Template record page** — on the DocGen Template layout, next to Name. - **Cloning** (§5.2.1) gives the copy its own unique API Name automatically; **Export/Import** (§5.2.2) carries the API Name across orgs and only drops it if a template in the target org already claims it. ### 5.2 Template versions Each save creates a new `DocGen_Template_Version__c` record. Only the version marked **Active** (`Is_Active__c = true`) is used by the runner. - Older versions are preserved for rollback. - When you save a new version, DocGen pre-extracts images from the DOCX/PPTX ZIP and caches them as ContentVersions for fast PDF rendering at generation time. - The XML parts are also pre-cached so PDF generation can skip ZIP decompression at runtime. **Version-pinned render config (v1.97+).** Editing a template's Output Format / Header HTML / Footer HTML / Document Title Format / Page Size / Page Orientation / Page Margins / Custom Margins used to silently rewrite the rendering of every previously-saved version, because the runner always read live template values. From v1.97 on, those eight fields are snapshotted onto the version at save time, and the render path overlays a non-null snapshot onto the template values. Practical effect: roll back to an older active version and you get the rendering that was authored at the time of that save, not whatever you've since changed the template to. Versions saved before v1.97 have no snapshot — those still fall back to the live template, so upgrading is non-breaking; the first re-save populates the snapshot. **Changing a template's format (v3.29+).** Switching **Type** (e.g. Word → HTML) requires uploading a new body file in the new format: change the Type, upload the file on the Document tab, and click **Save as New Version** — format and body update together. A details-only save that changes the Type is **blocked with an error**, because the previous-format body and its cached artifacts would keep rendering (that's the stale-output bug fixed in v3.29). Relatedly, if you've uploaded a file but use a details-only save, DocGen warns that the upload isn't included — documents keep generating from the previous file until you Save as New Version. **Deleting old versions (v1.92+).** Heavy iteration accumulates versions fast — each save creates the version record plus 5–7 ContentVersions (the body file + pre-decomposed XML parts). DocGen Admin → click into the template → **Versions** tab now exposes a **Delete** button next to each non-active version. Confirm the dialog, and the version record _plus_ its body and pre-decomposed files are cascade-deleted in one transaction. The currently active version can't be deleted — activate a different version first if you need to remove the current one. ### 5.2.1 Cloning a template (v3.29+) **Your Templates → row menu → Clone** copies everything in one click: the template record (all settings, query config, signer inputs, e-signature defaults), the active version and its file, inline images, the watermark, and saved queries. Image extraction and pre-decomposition re-run automatically on the copy, so it's immediately generation-ready. The copy is deliberately conservative: - Named "` (Copy)`" and opened in the editor right away so you can rename it. - Starts **Inactive** and never **Default**, so it stays out of every picker until you flip it on. - Gets its own unique API Name derived from the new name (the original's key stays untouched). Use it to iterate safely on a production template — clone, edit the copy, verify with Generate Sample, then activate the copy and deactivate the original. ### 5.2.2 Exporting & importing templates **Your Templates → row menu → Export** downloads a `.docgen.json` bundle containing the template settings, the active version's file, referenced inline-image assets, the watermark, and saved queries. **Import Template** (button above the list) recreates the whole thing in any org — shepherd image URLs are rewritten to the new org's files automatically, and image extraction/pre-decomposition re-run on arrival. Notes: - Only the **active** version travels; version history stays behind. - The API Name is preserved on import (that's the point of a stable key) unless a template in the target org already uses it, in which case it's left blank. - v3.29+ bundles include the newer fields (API Name, signer verification defaults, default e-sign email message, signer form-field config). Bundles exported from older versions still import fine — those fields just start blank. ### 5.3 Test record Set `Test_Record_Id__c` on the template to pin a specific record for preview/validation. Useful during template development — you can always preview against a known-good record without picking it each time. ### 5.4 Output format locking Check `Lock_Output_Format__c` on the template to prevent users from overriding the output format at runtime. If locked, the runner's "output as PDF/Word" toggle is hidden and any attempt to override via the Flow action or API throws a validation error. Use this for compliance-sensitive documents where only one format is allowed (e.g., signed contracts must always be PDF). ### 5.5 Template visibility (audience control) **Active / Inactive (v1.92+).** The simplest visibility control: each template has an **Active** checkbox (defaults to checked on new templates). When unchecked, the template is hidden from the document picker on every record page _while remaining fully editable_ in DocGen Admin. Use this to keep time-locked, seasonal, or work-in-progress templates from cluttering the daily picker without deleting them — the template list view in Admin shows an **Inactive** badge in the Status column so admins can find and re-activate them easily. Restrict more narrowly which users see a template in their picker: - **`Required_Permission_Sets__c`** (comma-separated permission-set names): only users with _at least one_ of these permission sets see the template. - **`Specific_Record_Ids__c`** (comma-separated record IDs): only show the template for these specific records. - **`Record_Filter__c`** (SOQL `WHERE` clause — for example, `StageName = 'Negotiation/Review' AND Amount > 10000`): dynamically show/hide based on the record's field values. These can be combined. All three must match for the template to appear. ### 5.6 Template sharing **v3.29+: no sharing setup needed.** The **DocGen User** permission set now includes read-only _View All_ on templates, so every DocGen user can see all active templates in the picker without manual shares, public groups, or sharing rules. Who should see which template is controlled by the visibility features built for exactly that (§5.5): the **Active** flag, **Required Permission Sets**, **Specific Record Ids**, and **Record Filter**. Write access is unchanged — only admins (DocGen Admin) can create or edit templates, and field-level security is still enforced on the merged data. On older versions (≤3.28), template records default to Private sharing, so non-owner users saw an empty picker until an admin either shared the templates (manual share, public group, or sharing rule) or set Setup → Sharing Settings → _DocGen Template_ → Default Internal Access = **Public Read Only**. That workaround is no longer necessary after upgrading, but it's harmless to leave in place. > **If you were relying on record-level sharing to hide templates from specific users:** that no longer restricts the picker. Move those rules onto **Required Permission Sets** (§5.5) — it's deploy-safe, visible on the template itself, and enforced consistently across the runner, bulk runner, signature sender, and Flow. Tip: the permission set doesn't need to grant anything — an empty "shell" permission set works as a pure membership token (create one per audience, list it on the template, assign it to the group). **Keeping templates truly private (record-level sharing).** The sharing-based gate still exists — the packaged _View All_ is what bypasses it. If your org requires share-controlled template visibility, don't assign the packaged **DocGen User** permission set; clone its grants into your own permission set _without_ View All on DocGen Template. Those users get the pre-v3.29 behavior: sharing rules, manual shares, and role hierarchy govern their picker. Also note Required Permission Sets hides templates from _pickers_, not from the DocGen Templates object tab — a user with View All can still see that template records exist (names, descriptions, query configs). Use the custom-permset approach if template definitions themselves are sensitive. ### 5.7 HTML templates (Google Docs, Notion, any HTML source) HTML templates let you author in any tool that produces HTML — Google Docs is the flagship workflow — and render to PDF. Every merge tag that works in Word templates works identically: `{Name}`, loops, conditionals, aggregates, images, `{Today}`, `{Now}`, `{%Image:N}`, etc. **Why use them?** Word templates lock authors into Microsoft Word. HTML templates let content teams design where they already work (Google Docs, Notion, ChatGPT, Apple Pages, any rich-text editor that emits HTML). Same merge engine, much wider authoring surface. #### 5.7.1 Authoring in Google Docs 1. Design the document in Google Docs. Normal formatting — headings, tables, images, colors, fonts. 2. Add merge tags as plain text: `{Name}`, `{Account.Name}`, `{Amount:currency}`, loops like `{#Contacts}...{/Contacts}`. Full syntax reference in [§7](#7-merge-tag-reference). 3. **File → Download → Web Page (.html, zipped)**. Google Docs produces a `.zip` with your HTML plus an `images/` folder. 4. In the Command Hub, create a template with **Type = HTML** (Output Format is forced to PDF). Upload the `.zip`. 5. DocGen unzips the file in your browser, saves each image as a ContentVersion linked to the template, and rewrites the HTML's `` references to `/sfc/servlet.shepherd/version/download/` URLs that `Blob.toPdf` resolves natively. 6. Click **Save as New Version** — the template is live. **Why unzip in the browser?** Salesforce's default File Upload Security blocks `.zip` uploads. DocGen's LWC reads zip bytes with a pure-JavaScript reader (native `DecompressionStream` + manual central-directory parse, zero dependencies), extracts just the HTML + images, and uploads those via Apex. The zip itself never becomes a ContentVersion, so the org setting never sees it. Bonus: unzipping client-side keeps Apex heap flat regardless of template size. #### 5.7.2 Other authoring tools - **Notion / Confluence** — export page as HTML - **ChatGPT / Claude** — ask for HTML, save to a `.html` file - **Apple Pages** — File → Export To → HTML - **Hand-written HTML** — any text editor For single-file uploads (`.html` / `.htm`), DocGen scans for inline `` URIs — common in Notion / ChatGPT / rich-text paste output — and extracts each to a ContentVersion with the `src` rewritten. `Blob.toPdf` can't decode data URIs directly, so this conversion is what makes those images render. #### 5.7.3 CSS rules — what works, what doesn't, and an LLM prompt PDF rendering goes through Salesforce's `Blob.toPdf()`, which is a Flying Saucer engine under the hood. **Flying Saucer is essentially a CSS 2.1 renderer with a small CSS 3 subset.** Modern layout primitives are silently ignored — the page still renders, but your layout collapses to default block flow. The result is a PDF that "looks wrong" without any error message. This section gives you the rules and a paste-ready prompt for ChatGPT / Claude / Gemini so you can have an LLM produce templates that render correctly the first time. ##### Quick reference | Use | Don't use | Replacement | | ---------------------------------------- | -------------------------------------------------- | ----------------------------------------------------- | | `` for side-by-side layout | `display: flex`, `display: grid` | One `
` with one ``, columns become `
`s | | Solid `background-color` | `linear-gradient(...)`, `radial-gradient(...)` | Pick the dominant color, drop the gradient | | `padding`, `margin` | `gap` (CSS 3 grid/flex gap) | `padding` on cells, `margin` on blocks | | Fixed `width`/`height` in `pt`/`in`/`px` | `calc(...)`, CSS variables (`--foo`, `var(--foo)`) | Compute the literal value in your template | | `font-size` in `pt` | `rem`, `em` based on a non-default root | Pt is most predictable for print | | `border` — incl. `dashed` and `dotted` | `border-radius`, `box-shadow`, `text-shadow` | Corners stay square; see "Rounded corners" below | | Hex colours (`#eaf2fb`) | `rgba(...)`, `hsla(...)` | Pre-compute the tint as a flat hex | | Full-strength colour | `opacity` | Pick a lighter hex instead | | `:nth-child(even)` for zebra striping | `:has(...)`, `:is(...)`, container queries | nth-child + nth-of-type are supported | | ``-based two/three-column layouts | `column-count`, `columns` | Tables work everywhere | | `text-align`, `vertical-align` on `
` | `place-items`, `align-self` | Old-school alignment on cells | ##### Rounded corners, shadows and tints — the honest answer **There are no rounded corners.** `border-radius` is ignored in _every_ form: on a `
`, on a `
`, on a `` with `border-collapse: separate`, in the four-value corner shorthand, and via the engine's own `-fs-border-radius`. Boxes render square. This is measured, not inferred — `scripts/css-capability-probe.apex` renders a labelled swatch per property and compares each against a control, and the rendered proof is in `docs/css-capability-probe.png`. An earlier version of this table listed "`border-radius` (basic)" as supported. It is not, and a template built on that advice renders square with no warning. What you can use for visual interest, all CSS 2.1 and all confirmed working: - **`border: 2pt dashed #2b6cb0`** and **`border: 2pt dotted #b02b2b`** — these genuinely render. They are the only border decoration available. - Solid fills, contrasting borders, and `:nth-child(even)` zebra striping. - A background **image** if you truly need a rounded shape — draw it once and reference it; the engine will place it faithfully. Two failure modes that look similar and are not: - **`linear-gradient(...)` is discarded when the stylesheet is parsed.** The cascade then falls back to any solid `background` set earlier, so the element keeps a sensible colour. Harmless. - **`rgba(...)` is not.** It parses, resolves to nothing, and the background disappears entirely — so a tinted panel renders **invisible** rather than falling back to a colour. This is the one that quietly ruins a layout. Compute the tint yourself and write it as a flat hex. `opacity`, `box-shadow`, `transform`, `calc()` and `outline` are all ignored too. ##### Long values overflow a table cell — and no CSS fixes it A value with no spaces in it (an external id, a URL, a base64 fragment, a long product code) will **run straight through the cell border**. This is not a styling mistake; the engine cannot break an unbroken run of characters at all. Measured across every candidate: | Technique | Result | | --------------------------- | ------------------------------------------------------------------------ | | `word-wrap: break-word` | **ignored** — the engine injects this on every cell and it does nothing | | `overflow-wrap: break-word` | **ignored** | | `word-break: break-all` | **ignored** | | `table-layout: fixed` | the column stops growing, so the text overflows the border instead | | `​` zero-width space | **ignored** | | `­` soft hyphen | breaks, but prints VISIBLE hyphens into the value — it corrupts the data | | **``** | **works** — breaks cleanly, and the text is unchanged | So the only correct fix is a `` at each allowed break point: ```html ``` For merged values you cannot pre-break by hand, keep the column wide enough for the longest value you expect, or split the value into its own row beneath the label rather than beside it. Do **not** reach for `­` — a hyphen that was never in the data is worse than a column that is too narrow. ##### How to draw a circle (bullets, status dots, timeline markers) Since `border-radius` does nothing, a circle has to be a **character**, not a box. Unicode circle glyphs render correctly and scale with `font-size`, so you can size one anywhere from a bullet to a display graphic: | Want | Use | Character | | ----------------- | ----------------------------- | --------- | | Small filled dot | `•` (U+2022 bullet) | • | | Filled circle | `●` (U+25CF) | ● | | Hollow circle | `○` (U+25CB) | ○ | | Large hollow ring | `◯` (U+25EF) | ◯ | | Small hollow ring | `°` (U+00B0 degree sign) | ° | ```html Delivered ``` Colour comes from `color`, size from `font-size`, and a light glyph on a dark filled table cell reverses cleanly (white `•` on a solid background). **Never use ZapfDingbats or Symbol for this.** Those fonts are not present in the rendering engine, so `font-family: ZapfDingbats` with the letter `l` or `m` — the usual trick for a filled/hollow circle — falls back to a serif face and prints a literal **"l"** or **"m"** into the document. That is worse than an unsupported property, because nothing fails: a wrong character is silently typeset where a circle was intended. Stick to the Unicode glyphs above, which need no particular font. ##### Paste-ready LLM prompt Copy this verbatim into ChatGPT / Claude / Gemini. Replace the bracketed sections with what you want: ``` Generate a single self-contained HTML file for Salesforce DocGen. Audience: rendered to PDF by Flying Saucer (CSS 2.1 + small CSS 3 subset). Modern CSS layout features are silently ignored. HARD RULES — never use these (Flying Saucer drops them): - display: flex, display: grid, gap - linear-gradient(...), radial-gradient(...), conic-gradient(...) - calc(...), CSS variables (--name, var(--name)) - transform, transition, animation, @keyframes - position: absolute or position: fixed (use @page running elements only) - box-shadow, text-shadow, outline - border-radius — in EVERY form, including on a
{External_Reference__c} ORD-2026-000148-REV3-APPROVED , on a with border-collapse: separate, and the -fs-border-radius variant. There are no rounded corners. Every box is square. Do not attempt a workaround. - rgba(...) and hsla(...) — these do not fall back to a solid colour, the background disappears completely. Write a pre-computed flat hex instead. - opacity — pick a lighter hex colour instead. - :has(), :is(), :where(), container queries USE INSTEAD: -
for any side-by-side layout. One , columns are
s with explicit widths. - Solid background-color as a HEX value (no gradients, no rgba). - padding/margin in pt or in. gap is not a thing. - font-size in pt. Standard fonts: Helvetica, Arial, "Times New Roman", Courier. - text-align / vertical-align on for alignment. - Fixed width/height in pt or in. - For visual interest use solid fills, contrasting borders, and :nth-child(even) zebra striping. border: dashed and border: dotted DO work and are the only border decoration available. - CIRCLES (bullets, status dots, markers) must be CHARACTERS, never CSS boxes, because border-radius does nothing. Use these and size them with font-size, colour them with color: • small filled dot (U+2022) ● filled circle (U+25CF) ○ hollow circle (U+25CB) ◯ large hollow ring (U+25EF) ° small hollow ring (U+00B0) e.g. Delivered NEVER use font-family: ZapfDingbats or Symbol to make a circle. Those fonts are absent, so the letter falls back to a serif face and prints a literal "l" or "m" in the document instead of a shape. PAGE SETUP — put a single
Quote #: {Name}
Date: {Today}
Prepared For
{Account.Name}
{Account.ShippingAddress}
Prepared By
{RunningUser.Name}
{RunningUser.Email}
Line Items
{#OpportunityLineItems} {/OpportunityLineItems}
Product Qty Price Total
{Product2.Name} {Quantity} {UnitPrice:currency} {TotalPrice:currency}
Total {Amount:currency}
Customer Signature
{@Signature_Buyer}
Date
{@Signature_Buyer:1:Date}
``` ##### Common conversion patterns When an LLM (or a designer) hands you a template using modern CSS, here are the mechanical rewrites: **Flex header → table header** ```html
Date: {Today}
ACME Date: {Today}
``` **Grid columns → table columns** ```html
Left content
Right content
Left content Right content
``` **Gradient → solid color** ```css /* BEFORE */ .accent-bar { background: linear-gradient(to right, #004693, #35c6f4); } /* AFTER */ .accent-bar { background-color: #35c6f4; } ``` **`gap` between rows → margin** ```css /* BEFORE */ .stack { display: flex; flex-direction: column; gap: 12px; } /* AFTER */ .stack > * { margin-bottom: 12px; } ``` ##### `@page` rules — don't double-declare Two ways to control the page size, margins, and orientation: 1. **Template fields** — `Page_Size__c`, `Page_Orientation__c`, `Page_Margins__c`, `Custom_Margins__c` on the DocGen Template record. DocGen wraps the template's HTML with an engine `