--- name: ria-sign-document-authoring description: Authoring playbook for sending contracts and legal documents through the RIA Sign MCP. Use when drafting any contract, NDA, power of attorney, receipt, declaration, lease, service agreement, or similar document to be signed via WhatsApp/electronic signature. Covers placeholder naming, address rules, monetary values, signer ordering, and Brazilian legal-document edge cases. related_mcps: - name: RIA Sign url: https://sign.riasistemas.com.br/mcp related_tools: - send_structured_contract - send_template - send_contract_text - send_document license: MIT version: 1.0.0 --- # RIA Sign — Document Authoring Skill When the user asks you to draft a contract, NDA, power of attorney, receipt, declaration, lease, service agreement, or similar legal document and send it for signature, **author the document yourself** with `[PLACEHOLDERS]` inline and call `send_structured_contract` on the RIA Sign MCP. The server skips its own AI analysis (faster, deterministic, cheaper). This Skill encodes the Brazilian-legaltech rules that make the resulting documents valid and the WhatsApp signing UX smooth. --- ## 0. Prime Directive — Minimize manual typing **Every keystroke the signer types is a chance to abandon the Flow.** This is THE single most important rule. Optimize every placeholder to need ZERO or ONE manual input. Manual is the exception, never the default. ### Hierarchy of preference for any data point (apply IN THIS ORDER) 1. **Real data is known → write it inline in `template_text`.** No placeholder at all. Signer never sees a field. Example: user said "locador é João Silva, CPF 987.654.321-00" → that string goes literally into the text. 2. **Belongs to the signer's own profile → use `auto`.** Server pre-fills, signer doesn't see a field. Example: signer's own name → `auto: "name"`. Signer's own CPF → `auto: "cpf"`. Signing date → `auto: "date"` + `shared: true`. 3. **Address → ALWAYS use the 7-placeholder CEP pattern.** Signer types only the CEP (8 digits), ViaCEP fills street/neighborhood/city/state, signer enters the number, and complement (optional). **Never make manual fields for a Brazilian address.** This is the single biggest typing reduction available. 4. **Reusable from other signer → use `shared: true`.** Single value used by all parties. Comarca/foro derived from 1st signer's address city (`type: "address_city"` + `shared: true`). Signing city (`shared: true`). Contract value `[VALOR]` once. 5. **Future/expiry date → `input_type: "date"`** → native date picker, no free-text. 6. **Free-text manual field → LAST RESORT.** Use only when none of the above apply (e.g. profession, marital status when not in profile). ### Checklist: how many manual text inputs would the signer face? Before sending, mentally count: how many `[PLACEHOLDERS]` does the signer have to TYPE manually (free-text, not auto/type/input_type)? If the answer is more than 2–3 per signer for a typical contract, you are over-asking. Review and replace with auto / type / shared whenever possible. The ideal: signer answers profile prompts once (name, CPF), types the CEP, and the document is complete. --- ## 1. When to use which tool | Situation | Tool | |---|---| | **You are drafting the document** in the conversation (most cases) | `send_structured_contract` | | User has a **saved template** they want to reuse | `send_template` (pass `template_id` + `fields`) | | User pasted **raw external text** they didn't write themselves | `send_contract_text` (server analyzes) | | User has an **existing PDF** attached | `send_document` (pass `file_id` / `file_url`) | `send_structured_contract` is the preferred path whenever you can structure the document yourself. It eliminates server-side LLM round-trip and produces deterministic, predictable output. --- ## 2. Authoring `template_text` - Plain text. Preserve line breaks as `\n` in the JSON string. - Insert placeholders in **UPPER_SNAKE_CASE without accents**, wrapped in square brackets: `[NOME_CONTRATANTE]`, `[CPF_LOCADOR]`, `[VALOR]`, `[DATA_ASSINATURA]`. - If the same data appears multiple times, **reuse the same placeholder name** in every location. - **Already-known real data → write inline as plain text, NO placeholder.** Example: if the user gives "Maria Silva, CPF 987.654.321-00", that signer's name and CPF appear literally in the text; do not create `[NOME_*]` / `[CPF_*]` for them. - **Generic/empty data → placeholder.** Example: "LOCATÁRIO: _____, CPF _____" → `LOCATÁRIO: [NOME_LOCATARIO], CPF [CPF_LOCATARIO]`. - Preserve all original clauses, formatting, line breaks. Do not invent or remove clauses. ### Fictitious CPFs are placeholders CPFs with repetitive patterns (`000.000.000-00`, `111.111.111-11`, `123.456.789-00`, `999.999.999-99`) are placeholders, not real data. Replace them with `[CPF_*]`. --- ## 3. Placeholder config: `auto`, `type`, `shared` Each placeholder entry in the `placeholders` object has these optional fields: ```json { "PLACEHOLDER_NAME": { "label": "Display label (≤20 chars, accents OK)", "auto": "name" | "cpf" | "date", "type": "cep" | "address_street" | "address_number" | "address_complement" | "address_neighborhood" | "address_city" | "address_state", "shared": true | false } } ``` ### `auto`: pre-filled from signer profile (signer never sees the field) - `auto: "name"` → signer's full name. **Always use** for name fields of the signer. - `auto: "cpf"` → signer's CPF or CNPJ. **Always use** for the signer's own document number. - `auto: "date"` → today's date. Combine with `shared: true` if it's the document's single date (e.g. `DATA_ASSINATURA`, `DATA_CELEBRACAO`). **Priority rule:** any field that maps to known signer data → use `auto`. Manual fill is the exception. ### `type`: address fields — CEP IS MANDATORY, ViaCEP fills the rest **Whenever an address is generic in the source text, the answer is ALWAYS the same: 7 placeholders, one of them `type: "cep"`.** The WhatsApp Flow has a dedicated address screen where the signer types the CEP (8 digits, single input) and ViaCEP automatically fills rua, bairro, cidade, UF. Signer only types CEP + número + (optional) complemento. **This pattern reduces address input from ~6 manual fields to effectively 1.5 inputs (CEP + número). NEVER manually ask for a Brazilian address.** #### The canonical 7-placeholder set For every party whose address is generic: ```json "CEP_LOCATARIO": { "label": "CEP", "type": "cep" }, "RUA_LOCATARIO": { "label": "Rua", "type": "address_street" }, "NUMERO_LOCATARIO": { "label": "Número", "type": "address_number" }, "COMPLEMENTO_LOCATARIO": { "label": "Complemento", "type": "address_complement" }, "BAIRRO_LOCATARIO": { "label": "Bairro", "type": "address_neighborhood" }, "CIDADE_LOCATARIO": { "label": "Cidade", "type": "address_city" }, "UF_LOCATARIO": { "label": "UF", "type": "address_state" } ``` Replace `LOCATARIO` with the appropriate role suffix (`_OUTORGADO`, `_COMPRADOR`, etc.). #### Address decision matrix | Address state in source text | What to do | |---|---| | **Generic / empty** (`"residente em ____"`, `"Rua ___, nº ___"`, `"endereço a definir"`) | Create ALL 7 placeholders. Signer enters CEP, ViaCEP does the rest. | | **Fully real** (`"Rua A, 100, Centro, Curitiba/PR, CEP 80000-000"`) | NONE. Keep inline as plain text. | | **Partial** (real street + empty number) | Only the placeholders for the empty parts. But if the address looks mostly empty, prefer the full 7 — ViaCEP will simply confirm the existing data when signer enters CEP. | #### Hard prohibitions on address handling - ❌ Creating `[ENDERECO]` as a single free-text placeholder asking the signer to type the whole thing. **PROHIBITED.** - ❌ Creating address fields without proper `type` values (they become manual standalone fields). **PROHIBITED.** - ❌ Creating a CEP placeholder alone, without the 6 sibling address fields. **PROHIBITED.** - ❌ Asking the signer to retype rua / bairro / cidade / UF when ViaCEP can fill them. **PROHIBITED.** ### Comarca / Foro — derived from the 1st signer's address, never asked When `comarca` / `foro` appears as a placeholder in the document, classify it as: ```json "COMARCA_FORO": { "label": "Comarca", "type": "address_city", "shared": true } ``` **The server reuses the city the 1st signer entered as their own address city.** Nobody types comarca manually. Always assign `COMARCA_FORO` to the FIRST signer in `suggested_signers.placeholders`. **FORBIDDEN: asking comarca / foro as a free-text field.** Always `type: "address_city"` + `shared: true`. If the comarca is geographically different from the 1st signer's address (rare — e.g. the parties chose a foro de eleição in another city), the user must say so explicitly before authoring — and in that case the user provides the comarca name and you write it inline (no placeholder). ### `shared`: single value used by all signers (legal integrity) Fields that **define the terms of the contract** (not personal data) must have `shared: true` and be attributed to the **first signer**. This preserves mutual consent — all signers see the same final text. Always shared: - `VALOR` (monetary amount, price, rent, fees) - `PRAZO`, `VIGENCIA`, `DURACAO` - `MULTA`, `JUROS`, `PENALIDADES` - Generic `OBJETO` (asset description, service scope, powers granted) - `CONDICOES` (rescission clauses, etc.) - `COMARCA_FORO` (already covered above) - `DATA_ASSINATURA`, `CIDADE_ASSINATURA` (signing date/place) Never shared: - Personal data of each signer (name, CPF, address, profession, marital status, RG) — each signer fills their own. --- ## 4. Monetary values — ONE placeholder, never split **Rule of gold:** monetary values ALWAYS appear in Brazilian legal documents as `"R$ X.XXX,XX (valor por extenso)"` — numeric and written-out forms juntos entre parênteses. The signer fills BOTH in a single text field, exactly once. **FORBIDDEN:** - `[VALOR_NUMERICO]` + `[VALOR_EXTENSO]` separated. ❌ - `[VALOR]` + `[VALOR_EXTENSO]`. ❌ - Any variant that asks for numeric and written-out in different fields. ❌ This double-input pattern previously caused massive abandonment in the WhatsApp Flow — the signer types the number, then doesn't know how to spell out the written form, and gives up. ONE input. ONE placeholder. The signer writes both forms together. ### Decision matrix | Situation | What to do | |---|---| | Value is **known** (user said "R$ 5.000" or it's in the source text fully) | Write inline with both forms YOU calculate: `"R$ 5.000,00 (cinco mil reais) mensais"`. **NO placeholder.** | | Value is **generic** in source text (`"R$ ___"`, `"R$ XXXXX"`) | ONE placeholder `[VALOR]` with `shared: true`, `label: "Valor (R$ + extenso)"`. In text: `"o valor de [VALOR] mensais"`. First signer types `"R$ 5.000,00 (cinco mil reais)"` (both forms) once. | ### Worked example — wrong vs right **❌ WRONG:** ```json { "VALOR_NUMERICO": { "label": "Valor (R$)", "shared": true }, "VALOR_EXTENSO": { "label": "Valor por extenso", "shared": true } } ``` template_text: `"o valor de R$ [VALOR_NUMERICO] ([VALOR_EXTENSO])"` Signer sees 2 fields. Types "R$ 5.000,00". Gets to "Valor por extenso" field, doesn't know what to write. Abandons. **✅ RIGHT:** ```json { "VALOR": { "label": "Valor (R$ + extenso)", "shared": true } } ``` template_text: `"o valor de [VALOR] mensais"` Signer sees 1 field. Types `"R$ 5.000,00 (cinco mil reais)"` once. Completes. --- ## 4b. Dates — two different component types Dates use **two different placeholder configurations** depending on intent. **Choose deliberately.** ### Type 1 — `auto: "date"` (auto-filled with TODAY, signer doesn't see field) For the **document's signing date** — the date the parties celebrate the contract. The server fills today's date automatically when the first signer signs. ```json { "DATA_ASSINATURA": { "label": "Data", "auto": "date", "shared": true } } ``` Always pair with `shared: true` so all signers see the same date (the date the first signer locked in). Use for: `DATA_ASSINATURA`, `DATA_CELEBRACAO`, `DATA` (when meaning "today's date"). ### Type 2 — `input_type: "date"` (date picker, signer chooses) For dates the signer must **choose** — typically validity, expiry, due dates, duration limits, deadlines. ```json { "DATA_VALIDADE": { "label": "Validade", "input_type": "date" } } ``` The Flow shows a native date picker. Signer taps a calendar and picks. Usually NOT shared (each context may have its own date), but can be `shared: true` when the deadline applies to all parties (e.g. NDA validity). Use for: `DATA_VALIDADE`, `DATA_VENCIMENTO`, `DATA_INICIO_VIGENCIA`, `DATA_FIM_VIGENCIA`, `PRAZO_FINAL`. ### Decision quick reference | What date represents | Config | Shared? | |---|---|---| | Signing date (today) | `auto: "date"` | `shared: true` always | | NDA / contract validity end | `input_type: "date"` | `shared: true` (applies to both parties) | | Lease duration end | `input_type: "date"` | `shared: true` | | Payment due date | `input_type: "date"` | `shared: true` if same for all; per-signer otherwise | | Personal birthday in declaration | `input_type: "date"` | `shared: false` (per signer) | **FORBIDDEN:** asking for a date via free-text input when one of these patterns fits. The native date picker is always better UX. --- ## 5. `suggested_signers`: ordering and assignment ```json "suggested_signers": [ { "role": "locador", "description": "dono do imóvel", "placeholders": [...] }, { "role": "locatario", "description": "quem aluga o imóvel", "placeholders": [...] } ] ``` ### Order = signing order. First signer = defines the terms. | Document type | Canonical order | |---|---| | Procuração (POA) | outorgante → outorgado | | Locação | locador → locatário | | Prestação de serviços | contratante → contratado | | Compra e venda | vendedor → comprador | | Recibo | pagador → recebedor | | Empréstimo | credor → devedor | | NDA unilateral | divulgador → receptor | | NDA bilateral / consórcio (3+ partes) | flexible — whoever defines terms first | | Declaração | declarante (single signer) | ### Role identifiers `role` = lowercase, no accents: `contratante`, `contratado`, `locador`, `locatario`, `outorgante`, `outorgado`, `vendedor`, `comprador`, `credor`, `devedor`, `divulgador`, `receptor`, `declarante`, `pagador`, `recebedor`. ### `description` 3–6 simple words: `"dono do imóvel"`, `"quem presta serviço"`, `"quem recebe poderes"`. ### Placeholder assignment per role - Each placeholder name appears in **exactly one** role's `placeholders` array. - `auto` and `shared` placeholders go on the **first signer**. - Signers with already-real data in the text → `placeholders: []` (plus their shared/auto fields if first signer). ### Witnesses **TESTEMUNHAS NEVER appear in `suggested_signers`.** They sign separately or optionally. You may create their placeholders in the text but exclude them from the array. ### Legal representative (PJ) If "João Silva, representante legal da Empresa X Ltda" appears, create a role `empresa_x` with the representative's placeholders (NOME, CPF) **plus** the PJ's placeholders (CNPJ, RAZAO_SOCIAL). `description: "empresa representada por João"`. ### Tripartite / 3+ parties Create 3+ distinct roles. Order follows who defines terms. ### The sender (who initiated the document) The sender **always** signs. If the sender is also the party that defines the terms, they go as **first signer** with their real data inline in the text (so `placeholders: []` for their own personal data, but the `shared` fields still go on them). --- ## 5b. Avoiding duplicate input (UX rule of gold) **Every keystroke the signer types is a chance to abandon.** The minimum-typing principle is the single biggest driver of Flow completion rate. These rules consolidate where typing must be minimized: ### Anti-duplication patterns by data type | Data type | Wrong (asks for twice) | Right (asks once or auto) | |---|---|---| | **Monetary value** | `[VALOR_NUMERICO]` + `[VALOR_EXTENSO]` | ONE `[VALOR]` with format `"R$ X.XXX,XX (extenso)"` | | **Address** | 7 separate manual fields | ONE `[CEP_*]` of type `cep` → ViaCEP fills street/neighborhood/city/state automatically | | **Comarca / foro** | Manual field for comarca | `type: "address_city"` + `shared: true` → reuses 1st signer's address city | | **Signing date** | Free-text "Data" field | `auto: "date"` + `shared: true` → server fills today | | **Signer name** | Manual field | `auto: "name"` → server fills from signer profile | | **Signer CPF** | Manual field | `auto: "cpf"` → server fills from signer profile | | **Cidade de assinatura** | Manual field | `shared: true` → 1st signer fills once for all | | **Same datum repeated** in template_text (e.g. nome aparece 3 vezes) | 3 different placeholders | ONE placeholder used 3 times in `template_text` | | **Future/validity date** | Free-text "DD/MM/AAAA" | `input_type: "date"` → native date picker | ### Rule of repetition If a piece of data appears multiple times in `template_text`, use **the same placeholder name in every location**. The signer fills it once; the server substitutes everywhere. **❌ Wrong:** ``` LOCATÁRIO: [NOME_LOCATARIO_1], ... Pelo presente instrumento, [NOME_LOCATARIO_2] se compromete... Para fins de cobrança, [NOME_LOCATARIO_3]... ``` **✅ Right:** ``` LOCATÁRIO: [NOME_LOCATARIO], ... Pelo presente instrumento, [NOME_LOCATARIO] se compromete... Para fins de cobrança, [NOME_LOCATARIO]... ``` Same placeholder name = same value, one input. ### When the user already knows a value — write it inline If the user has told you a specific value in the conversation ("aluguel é R$ 2.500" / "Locador é João Silva, CPF 987.654.321-00"), DO NOT make a placeholder for it. Write the literal value inline in `template_text`. The signer never sees these as fields. Only create placeholders for data that is genuinely unknown to you at authoring time. --- ## 6. Pronouns (gender inference) Infer grammatical gender from signer names: - Maria → feminine: `"a locatária compromete-se..."`, `"ela pagará..."` - Reinaldo → masculine: `"o vendedor compromete-se..."`, `"ele entregará..."` - Ambiguous (Alex, Darci, foreign names) → neutral: `"o(a) contratante compromete-se..."`, `"a parte..."` --- ## 7. Required output structure Call `send_structured_contract` with: ```json { "subject": "Contrato de Locação Residencial", "template_text": "CONTRATO DE LOCAÇÃO RESIDENCIAL\n\nLOCADOR: João Silva, ...", "placeholders": { "NOME_LOCATARIO": { "label": "Nome completo", "auto": "name" }, "CPF_LOCATARIO": { "label": "CPF", "auto": "cpf" }, "VALOR": { "label": "Valor", "shared": true }, "DATA_ASSINATURA":{ "label": "Data", "auto": "date", "shared": true }, "CEP_LOCATARIO": { "label": "CEP", "type": "cep" }, "RUA_LOCATARIO": { "label": "Rua", "type": "address_street" }, "NUMERO_LOCATARIO":{ "label": "Número", "type": "address_number" }, "COMPLEMENTO_LOCATARIO": { "label": "Complemento", "type": "address_complement" }, "BAIRRO_LOCATARIO":{ "label": "Bairro", "type": "address_neighborhood" }, "CIDADE_LOCATARIO":{ "label": "Cidade", "type": "address_city" }, "UF_LOCATARIO": { "label": "UF", "type": "address_state" } }, "suggested_signers": [ { "role": "locador", "description": "dono do imóvel", "placeholders": ["VALOR", "DATA_ASSINATURA"] }, { "role": "locatario", "description": "quem aluga o imóvel", "placeholders": ["NOME_LOCATARIO", "CPF_LOCATARIO", "CEP_LOCATARIO", "RUA_LOCATARIO", "NUMERO_LOCATARIO", "COMPLEMENTO_LOCATARIO", "BAIRRO_LOCATARIO", "CIDADE_LOCATARIO", "UF_LOCATARIO"] } ], "signers": [ { "name": "João Silva", "phone": "+5541999998888", "role": "locador" }, { "name": "Maria Santos", "phone": "+5511988887777", "role": "locatario" } ], "fields": { "VALOR": "R$ 2.500,00 (dois mil e quinhentos reais)" } } ``` --- ## 8. Gold-standard examples ### Example A — Residential lease (locador with real data + empty locatário) **User request:** "Cria contrato de locação. Locador: João Silva (dados completos cadastrados). Locatário: vazio. Imóvel: Rua B, 200, Curitiba/PR. Aluguel: R$ 2.500. Manda assinar pra +5511988887777 (Maria)." **`template_text`:** ``` CONTRATO DE LOCAÇÃO RESIDENCIAL LOCADOR: João Silva, brasileiro, casado, CPF 987.654.321-00, residente à Rua A, 100, Centro, Curitiba/PR, CEP 80000-000. LOCATÁRIO: [NOME_LOCATARIO], [NACIONALIDADE_LOCATARIO], [ESTADO_CIVIL_LOCATARIO], CPF [CPF_LOCATARIO], residente à [RUA_LOCATARIO], [NUMERO_LOCATARIO], [COMPLEMENTO_LOCATARIO], [BAIRRO_LOCATARIO], [CIDADE_LOCATARIO]/[UF_LOCATARIO], CEP [CEP_LOCATARIO]. Objeto: Locação do imóvel situado à Rua B, 200, Curitiba/PR. Valor do aluguel: R$ 2.500,00 (dois mil e quinhentos reais) mensais. Foro: comarca de [COMARCA_FORO]. [CIDADE_ASSINATURA], [DATA_ASSINATURA]. ``` **Why these decisions:** - Locador has all real data → all inline, no placeholders for him. - Locatário is generic → personal data + 7 address placeholders. - Aluguel is known (R$ 2.500) → inline with extenso, no placeholder. - Comarca is generic → `COMARCA_FORO` with `type: "address_city"` + `shared: true`. - Locador is first signer (defines terms) but has empty `placeholders: []` for own data; he gets the shared fields (DATA_ASSINATURA, CIDADE_ASSINATURA, COMARCA_FORO). ### Example B — Receipt with generic VALOR **User request:** "Recibo de pagamento — pagador é Maria Santos (+5561987654321), eu sou recebedor, valor a definir." **`template_text`:** ``` RECIBO DE PAGAMENTO Recebi de [NOME_PAGADOR], CPF [CPF_PAGADOR], a quantia de [VALOR], referente a [REFERENCIA]. Por ser verdade, firmo o presente recibo. [CIDADE_ASSINATURA], [DATA_ASSINATURA]. Reinaldo Oliveira Recebedor ``` **`placeholders`:** ```json { "NOME_PAGADOR": { "label": "Nome completo", "auto": "name" }, "CPF_PAGADOR": { "label": "CPF", "auto": "cpf" }, "VALOR": { "label": "Valor (R$ + extenso)", "shared": true }, "REFERENCIA": { "label": "Referente a", "shared": true }, "CIDADE_ASSINATURA":{ "label": "Cidade", "shared": true }, "DATA_ASSINATURA": { "label": "Data", "auto": "date", "shared": true } } ``` **`suggested_signers`:** ```json [ { "role": "pagador", "description": "quem paga o valor", "placeholders": ["NOME_PAGADOR", "CPF_PAGADOR", "VALOR", "REFERENCIA", "CIDADE_ASSINATURA", "DATA_ASSINATURA"] }, { "role": "recebedor", "description": "quem recebe o valor", "placeholders": [] } ] ``` **Why these decisions:** - Recebedor is the sender with real data → inline, `placeholders: []`. - Pagador is generic → auto:name + auto:cpf. - VALOR is generic → ONE placeholder, shared, assigned to first signer (pagador). - Single date → `auto: "date"` + `shared: true`. ### Example C — Simple Power of Attorney (POA) **User request:** "Procuração simples. Outorgante: eu (dados cadastrados). Outorgado: vazio. Poderes: representar em processo judicial 0123456-78.2026.8.16.0001." **`template_text`:** ``` PROCURAÇÃO OUTORGANTE: Reinaldo Oliveira, brasileiro, solteiro, advogado, CPF 123.456.789-00, residente à Rua X, 100, Curitiba/PR. OUTORGADO: [NOME_OUTORGADO], [NACIONALIDADE_OUTORGADO], [ESTADO_CIVIL_OUTORGADO], [PROFISSAO_OUTORGADO], CPF [CPF_OUTORGADO], residente à [RUA_OUTORGADO], [NUMERO_OUTORGADO], [COMPLEMENTO_OUTORGADO], [BAIRRO_OUTORGADO], [CIDADE_OUTORGADO]/[UF_OUTORGADO], CEP [CEP_OUTORGADO]. Pelo presente instrumento particular de procuração, o outorgante nomeia e constitui o(a) outorgado(a) seu procurador, com poderes específicos para representá-lo no processo judicial nº 0123456-78.2026.8.16.0001, podendo praticar todos os atos necessários ao bom e fiel cumprimento deste mandato. [CIDADE_ASSINATURA], [DATA_ASSINATURA]. ``` **Why:** outorgante has real data inline; outorgado has 5 personal placeholders + 7 address placeholders; processo nº is specific → inline. ### Example D — Bilateral NDA **User request:** "NDA bilateral entre Empresa A (CNPJ a definir) e Empresa B (CNPJ a definir)." **`suggested_signers`:** create two roles, e.g. `parte_a` and `parte_b`, each with their company placeholders. Order is flexible — whoever defines the terms first. --- ## 9. Anti-patterns (NEVER do these) ### Duplicate-input failures (highest UX impact) - ❌ Creating `[VALOR_NUMERICO]` + `[VALOR_EXTENSO]` separately. Always ONE `[VALOR]` with combined format `"R$ X.XXX,XX (por extenso)"` (see Section 4). - ❌ Asking for a future date via free-text field. Use `input_type: "date"` for the native date picker (see Section 4b). - ❌ Asking for signing date via manual field. Always `auto: "date"` + `shared: true`. - ❌ Asking for comarca / foro manually. Always `type: "address_city"` + `shared: true` — reuses 1st signer's city. - ❌ Using different placeholder names for the same datum used twice in the text. Same datum = same placeholder, repeated in `template_text`. - ❌ Creating manual fields for signer name / CPF. Always `auto: "name"` / `auto: "cpf"`. - ❌ Address fields without `type` defined (would become manual standalone fields). - ❌ Creating a CEP placeholder without the other 6 address fields. - ❌ Including real data already in the text as placeholders (user gave you the value → write it inline). ### Document integrity failures - ❌ Placing witnesses in `suggested_signers`. - ❌ Mixing one signer's personal placeholders into another signer's role. - ❌ Labels longer than 20 characters (WhatsApp Flow limit). - ❌ Using accented characters in placeholder names (`NÚMERO` ❌ — use `NUMERO`). ### Workflow failures - ❌ **Calling `send_structured_contract` without showing the user the final document and getting explicit approval first** (see Section 12). - ❌ **Sending a 2-party contract with only 1 signer** — the document can never reach `completed` status (see Section 10). - ❌ **Ignoring signing order for sequential documents** (locação, compra-e-venda, procuração) — the party that defines terms must sign first (see Section 11). --- ## 10. Minimum signers per document type Documents are legally meaningless without all required signing parties. **If the user gives fewer signers than the type requires, STOP and ask for the missing party (name + phone/email) before generating the document.** | Document type | Minimum signers | Roles | |---|---|---| | Declaração unilateral | 1 | declarante | | Recibo | 2 | pagador, recebedor | | Procuração simples | 2 | outorgante, outorgado | | Locação | 2 | locador, locatário | | Compra e venda | 2 | vendedor, comprador | | Prestação de serviços | 2 | contratante, contratado | | Empréstimo | 2 | credor, devedor | | NDA unilateral | 2 | divulgador, receptor | | NDA bilateral / mútuo | 2 | parte_a, parte_b | | Termo de confidencialidade simples | 1–2 | declarante (ou parte_a/parte_b) | | Consórcio / contrato tripartite | 3+ | distintos por contexto | | Distrato | mesmo nº das partes do contrato original | reaproveita roles | **Failure mode to avoid:** sending a 2-party contract with only 1 signer creates an envelope that can never reach `completed` status — the document stays in `sent` forever, wasting WhatsApp messages and confusing the recipient. If the user request is ambiguous about who else needs to sign, ASK explicitly. Example: > "Esse contrato de locação precisa de 2 signatários: o locador (dono do imóvel) e o locatário (quem aluga). Você me passou só o locatário (Maria Santos, +5511988887777). Quem é o locador e qual o WhatsApp dele?" --- ## 11. Signing order — sequential vs parallel The system supports per-signer `signing_order` (integer). Default = order of the `signers` array. | Mode | When to use | How to pass | |---|---|---| | **Sequential** (default) | Most contracts. The party that defines terms signs first, then the accepting party. Locador → locatário. Outorgante → outorgado. | Order of the `signers` array, OR explicit `order: 1, order: 2`. | | **Parallel** | NDA mútuo, declarações conjuntas, any contract where the order doesn't matter legally. | Pass the same `order` integer for both: `[{...order:1}, {...order:1}]`. Both get the WhatsApp Flow simultaneously. | **Why this matters:** in sequential mode, the second signer doesn't receive the WhatsApp link until the first signs. In parallel, both can sign at the same time. When in doubt, default to sequential with the term-defining party first — it's the legally safer pattern. --- ## 12. MANDATORY preview step before sending **`send_structured_contract` dispatches a real WhatsApp message** to each signer's phone. This is destructive and visible. **NEVER call this tool without first showing the user the rendered document and getting explicit approval.** ### Required pre-send routine Before calling `send_structured_contract`, present this to the user in chat: ``` 📄 Vou enviar este documento. Confira antes: ═══════════════════════════════════════════════ ASSUNTO: ═══════════════════════════════════════════════ SIGNATÁRIOS (na ordem em que vão assinar): 1️⃣ — WhatsApp 2️⃣ — WhatsApp [etc] CAMPOS QUE CADA UM VAI PREENCHER: • : : CAMPOS COMPARTILHADOS (preenchidos pelo 1º signatário): • Pode disparar? Responda "ok" ou me diga o que ajustar. ``` Then **wait for the user's explicit "ok" / "sim" / "pode enviar" / equivalent affirmative response** before invoking `send_structured_contract`. ### Why this is mandatory - The WhatsApp message is **irreversible** — once dispatched, the signer sees a notification immediately. - Wrong phone number = wrong person gets a signing link with sensitive data. - Wrong placeholder = invalid contract or awkward UX for signer. - Wrong signer role = wrong fields assigned → broken Flow. ### Acceptable shortcuts (only if user explicitly waived preview) If, and ONLY if, the user has said something like *"não precisa confirmar"* / *"manda direto sempre"* / *"pula a confirmação para esse tipo"* in this exact conversation, you may skip the preview routine. **Do not infer this from past patterns or other conversations.** --- ## 13. Quick mental checklist before calling `send_structured_contract` ### Structural correctness 1. ☐ Every variable in the text is wrapped in `[BRACKETS]` and exists in `placeholders`. 2. ☐ Every placeholder name in `placeholders` appears at least once in `template_text`. 3. ☐ Every placeholder is assigned to exactly one role in `suggested_signers`. 4. ☐ `signers` array (with names + phones) matches `suggested_signers` count and order, or uses `role` to map. ### Anti-duplication (UX integrity) 5. ☐ Monetary value: inline (if known) **or** ONE `[VALOR]` with `shared: true` + label `"Valor (R$ + extenso)"`. **No split numeric/extenso.** 6. ☐ Signing date uses `auto: "date"` + `shared: true`. 7. ☐ Future/expiry/validity date uses `input_type: "date"` (picker), not free-text. 8. ☐ Name fields use `auto: "name"`. CPF fields use `auto: "cpf"`. 9. ☐ Address placeholders come in groups of 7 with proper `type` values. CEP is the single input. 10. ☐ Comarca / foro uses `type: "address_city"` + `shared: true`. 11. ☐ Cidade da assinatura uses `shared: true` on 1st signer. 12. ☐ Same datum used multiple times in text = SAME placeholder name everywhere. 13. ☐ Real data (already known) → inline in `template_text`, NOT as placeholder. ### Role and ordering 14. ☐ First signer in order = party defining terms. 15. ☐ Witnesses excluded from `suggested_signers`. ### Pre-send safety 16. ☐ **Signer count ≥ minimum for document type** (see Section 10). If less, ASK for missing parties — do not generate yet. 17. ☐ **Signing order chosen consciously** — sequential (default) or parallel (only for symmetric documents like NDA mútuo). 18. ☐ **Preview rendered to the user** and **explicit confirmation received** (see Section 12). --- ## Maintenance This Skill is versioned at https://github.com/RIA-Sistemas/ria-sign-skill. Open an issue or PR when conventions evolve. Internal reference: the server-side LLM prompt this Skill mirrors lives at `src/services/template-agent.mjs` in the [RIA Sign repo](https://github.com/RIA-Sistemas/ria-sign).