Export Templates
Canonical placeholder and inheritance behavior for sponsor/fund export templates (ISL, EF, MSL).Export kinds
| Kind | Name | Purpose |
|---|---|---|
isl | Individual Side Letter | Per-LP draft rendered from fund or sponsor context. |
ef | Election Form | MFN eligibility schedule for a selected fund profile. |
msl | Master Side Letter | Fund-level side-letter output. |
Template inheritance
At render time, Hugo resolves templates in this order:
- Fund override:
fund_export_settings.template_{kind}_r2_key. - Sponsor default:
sponsors.export_template_{kind}_r2_key. - Starter fallback: built-in default template.
Implemented by resolveFundTemplateKey(db, fundId, kind) in src/services/export-template-resolution.ts. It returns r2Key and source: 'fund' | 'sponsor', or null when no custom template exists.
Storage
sponsors.export_template_isl_r2_key
sponsors.export_template_ef_r2_key
sponsors.export_template_msl_r2_key
fund_export_settings.template_isl_r2_key
fund_export_settings.template_ef_r2_key
fund_export_settings.template_msl_r2_key
General partner details and inheritance
Template variables are sourced from firm_* fields in the database and rendered as general-partner placeholders.
| DB column | Output placeholder |
|---|---|
firm_name | general_partner_name |
firm_address | general_partner_address |
firm_country | general_partner_country |
sponsors.governing_law | governing_law (sponsor only) |
resolveFundFirmDetails(db, fundId) performs per-field COALESCE with these precedence rules:
- Fund-level
firm_*value if present. - Otherwise sponsor-level
firm_*value if present. - Otherwise
null.
governing_law is sponsor-only: there is no fund override column for this field.
Settings pages
/sponsors/:sponsorId/export/settings
- Sponsor master template upload for each of
isl,ef,msl. - General-partner details (firm fields) entered at sponsor scope.
- Supported placeholders table below applies to all sponsor uploads.
/funds/:fundSlug/fund-templates
- Fund-level template overrides for
isl,ef,msl. - Fund-level general-partner overrides with sponsor fallback badges per field.
- Empty fund fields inherit from sponsor defaults.
Supported placeholders
| Token | Description |
|---|---|
{fund_name} | Name of the fund. |
{lp_name} | Investor display name (the investor's `name`). |
{lp_legal_name} | LP's full legal name as registered. |
{lp_short_name} | LP's short / 'attn:' name. Empty if not set. |
{lp_country} | LP's country code (ISO-2). Empty if not set. |
{lp_category} | Investor category label. |
{commitment_amount} | Commitment amount formatted with separators. |
{commitment_currency} | Commitment currency code. |
{commitment_date} | LP admission date (YYYY-MM-DD), empty if not set. |
{today} | Today in long-form English (for example "11 April 2026"). |
{general_partner_name} | General partner / signing-entity name. Falls back to "[General Partner]" if unset. |
{general_partner_address} | General partner address. Newlines preserved with `linebreaks: true`. |
{general_partner_country} | General partner country. |
{governing_law} | Governing law / jurisdiction phrase. Empty if not configured. |
| Token | Description |
|---|---|
{#clauses}...{/clauses} | Loop over selected clauses. Inside: {clause_category}, {text}, {index}. |
Inside {#clauses}...{/clauses}, valid inner tags are {clause_category}, {text}, and {index}.
Upload validation
validateTemplate(buffer) scans all {...} tokens and rejects uploads with unknown placeholders. Unknown-tag uploads fail immediately.
ISL render behavior
On render, Hugo loads the resolved template bytes (or starter), runs docxtemplater with renderIslFromTemplate, and returns the result. exportTemplateDraftHandler reuses this path for Word add-in pro-forma drafts.
Uploaded ISL templates are normalized through ensureClausesBodyContentControl so the clauses-body content-control anchor exists for add-in insertion.