The user asked us to confirm we copied the Documenso template's auto-fill schema verbatim from the legacy system. Confirmed and documented in the canonical mapping file: - Every legacy formValues key (Name, Email, Address, Yacht Name, Length, Width, Draft, Berth Number, Lease_10, Purchase) is still emitted with identical names and types — single-berth EOIs are byte-for-byte compatible with template id 8. - Phase 5 added one new field: `Berth Range` (compact range string for multi-berth EOIs from the is_in_eoi_bundle junction rows). Documenso silently drops unknown formValues, so the live template will simply not render the range until someone adds the field. The doc now flags this explicitly. - Verified buildDocumensoPayload() populates all 11 fields from the resolved EoiContext; tests at tests/unit/services/documenso-payload cover every field. The "rest is handled inside Documenso" (signature, date, terms) - those fields live on the template itself and don't appear in our formValues map. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
13 KiB
Documenso EOI Template — Field Mapping
Purpose: This doc is the canonical reference for mapping the Documenso EOI template's formValues keys to the new data model's EoiContext shape. It drives buildDocumensoPayload() (Task 11.2), the in-app Standard EOI HTML tokens (Task 11.3), and the Spec 2 importer's yacht/company hydration.
Source
The legacy field list comes from client-portal/server/api/eoi/generate-quick-eoi.ts, specifically the POST body sent to POST /api/v1/templates/{templateId}/generate-document (Documenso template 8). The relevant lines in that file are around the createDocumentPayload.formValues object.
Documenso template formValues keys
Documenso template IDs and recipient IDs are configured via env vars:
NUXT_DOCUMENSO_TEMPLATE_ID(default:8)NUXT_DOCUMENSO_CLIENT_RECIPIENT_ID(default:192) — signing order 1NUXT_DOCUMENSO_DEVELOPER_RECIPIENT_ID(default:193) — signing order 2NUXT_DOCUMENSO_APPROVAL_RECIPIENT_ID(default:194) — APPROVER, signing order 3
The template exposes eight text fields (formValues keys) and two boolean checkboxes.
Field mapping
The legacy template (Documenso template 8, configured in production) auto-fills exactly the fields below. All eight text fields + two booleans are populated by buildDocumensoPayload() from the resolved EoiContext. Anything else on the form (signature, date, terms acknowledgment) is filled in by the client inside Documenso.
| Documenso key | Type | Legacy source | New EoiContext path |
Notes |
|---|---|---|---|---|
Name |
text | interest['Full Name'] |
context.client.fullName |
The interest's point-of-contact client (billing signer). |
Email |
text | interest['Email Address'] |
context.client.primaryEmail |
Primary email contact from client_contacts. |
Address |
text | interest['Address'] |
concat context.client.address.{street,city,country} |
Concatenate street, city, country with ', '. Empty if address is null. |
Yacht Name |
text | interest['Yacht Name'] |
context.yacht.name |
Yacht is now a first-class row; pulled via interest.yachtId. Empty string when no yacht is linked yet. |
Length |
text | interest['Length'] |
context.yacht.lengthFt |
Boat dimension. Send as string. Documenso doesn't enforce numeric format. Empty string when not applicable. |
Width |
text | interest['Width'] |
context.yacht.widthFt |
Same. |
Draft |
text | interest['Depth'] |
context.yacht.draftFt |
Legacy field was named "Depth" in NocoDB; Documenso key is "Draft". |
Berth Number |
text | berthNumbers (joined) |
context.berth.mooringNumber |
The interest's PRIMARY berth (resolved via interest_berths.is_primary=true). Empty string when no primary set. |
Berth Range |
text | (new) | context.eoiBerthRange |
NEW IN PHASE 5 — compact range string for multi-berth EOIs (e.g. "A1-A3, B5-B7") covering every junction row marked is_in_eoi_bundle=true. Empty string when the bundle is empty. The live Documenso template (id 8) does NOT yet have this field. Add a Berth Range text field to the template before multi-berth EOIs render the range; until then Documenso silently drops the value and only Berth Number (the primary mooring) renders. |
Lease_10 |
boolean | hardcoded false |
false |
Hardcoded — legacy flow defaults to Purchase (not Lease). |
Purchase |
boolean | hardcoded true |
true |
Hardcoded — legacy flow defaults to Purchase. |
Backwards-compatibility guarantee: every legacy formValues key is still emitted with the same name and type. The only addition is Berth Range (Phase 5). Documenso silently ignores unknown formValues keys, so old templates that don't have Berth Range will simply not render it — single-berth EOIs continue to work identically. No template changes are required for legacy use.
Document meta fields (non-formValues)
| Documenso key | Type | Legacy source | New source |
|---|---|---|---|
meta.message |
text | Dear ${interest['Full Name']}... |
Dear ${context.client.fullName}, ...port name interpolated |
meta.subject |
text | "Your LOI is ready to be signed" |
Same — constant. |
meta.redirectUrl |
text | "https://portnimara.com" |
context.port.redirectUrl if per-port; otherwise global app URL. |
meta.distributionMethod |
text | "NONE" |
Same — constant. We use manual send flow (Documenso webhook). |
title |
text | `${interest['Full Name']}-EOI-NDA` |
`${context.client.fullName}-EOI-NDA` |
externalId |
text | `loi-${interestId}` |
Same. |
Recipients (non-formValues)
| Recipient | Role | Name | Signing order | |
|---|---|---|---|---|
| Client (signer) | SIGNER | context.client.fullName |
context.client.primaryEmail |
1 |
| Developer (signer) | SIGNER | "David Mizrahi" |
"dm@portnimara.com" |
2 |
| Approval (approver) | APPROVER | "Abbie May" |
"sales@portnimara.com" |
3 |
The Developer and Approval recipients are currently hardcoded in the legacy flow. In the new system these should eventually come from port-level settings (e.g., ports.settings.eoi.developerName + email). For Task 11.2, keep them hardcoded as the legacy system does — tracking as TODO: "Replace hardcoded Developer/Approval recipients with port-level configuration."
Company-owned yacht handling
The legacy flow has no concept of company ownership — the signer is always the interest's client. In the new system:
- If
context.yacht.ownerType === 'client': behavior unchanged. - If
context.yacht.ownerType === 'company': the interest's point-of-contact client still signs (they're the representative of the yacht's owning company), but an extra block should appear in the message body:"On behalf of ${context.company.legalName ?? context.company.name} (representing the yacht's owner).". This isn't a separate Documenso field — it's woven intometa.message.
Tracking this in the mapping doc rather than as a hard TODO because company-owned EOIs were rare in the legacy system and need product input before committing to the final wording.
Deprecated fields (no longer sourced from clients)
The legacy system read these fields from the client row. They are now sourced elsewhere:
| Legacy source | New source |
|---|---|
client.yachtName |
yachts.name via interest.yachtId |
client.yachtLengthFt |
yachts.lengthFt via interest.yachtId |
client.yachtWidthFt |
yachts.widthFt via interest.yachtId |
client.yachtDraftFt |
yachts.draftFt via interest.yachtId |
client.companyName |
companies.name via polymorphic owner resolution |
client.berthSizeDesired |
Removed. Berth is picked via reservation, not text. |