# 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 1 - `NUXT_DOCUMENSO_DEVELOPER_RECIPIENT_ID` (default: `193`) — signing order 2 - `NUXT_DOCUMENSO_APPROVAL_RECIPIENT_ID` (default: `194`) — APPROVER, signing order 3 The template exposes eight text fields (`formValues` keys) and two boolean checkboxes. ## Field mapping | 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`. | | `Length` | text | `interest['Length']` | `context.yacht.lengthFt` | Send as string. Documenso doesn't enforce numeric format. | | `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` | One berth per reservation. Multi-berth case was multi-interest in legacy. | | `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. | ## 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 | Email | 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 into `meta.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. |