This is the full developer documentation for Legaldoc.io API v2 # Legaldoc.io > Bienvenido a la documentación de la API v2 de Legaldoc.io. Una interfaz robusta y confiable para crear, distribuir y firmar documentos — desde firma simple hasta Firma Electrónica Avanzada. ## Empieza aquí [Sección titulada «Empieza aquí»](#empieza-aquí) [Guía de IntegraciónEl flujo completo: crear un envelope, agregar firmantes y campos, distribuir, y obtener el documento firmado.](/guides/integration-guide/)[Referencia de la APIEnvelope, Recipients, Fields, Items, Attachments, Folder y Embedding — cada endpoint con ejemplos de código.](/api/)[Firma Electrónica AvanzadaQué agrega FEA sobre el flujo general, y cómo entregar la evidencia: documento, certificado y auditoría.](/guides/fea-orquestacion/)[AutenticaciónCómo obtener y usar tu API Key.](/guides/authentication/)[WebhooksEntérate de cambios de estado sin consultar la API activamente.](/guides/webhooks/)[Web ComponentsIncorpora la firma de documentos en aplicaciones sin framework.](/guides/web-components/)[Variables CSSPersonaliza colores, espaciado y tipografía del widget embebido.](/guides/css-variables/) ## Documentación lista para agentes de IA  [Sección titulada «Documentación lista para agentes de IA »](#documentación-lista-para-agentes-de-ia) Todo el contenido se publica también como Markdown plano, `llms.txt` y OpenAPI, para que un LLM o un agente de desarrollo pueda leer la documentación completa sin ejecutar JavaScript. [Cómo usar estos docs con IAQué formato conviene según tu herramienta, y cómo apuntar un agente a esta documentación.](/ai/)[llms.txtEl índice del sitio en el formato estándar de llmstxt.org.](/llms.txt) # Página no encontrada > La página que buscas no existe o cambió de dirección. Usa el buscador o vuelve al inicio. # Documentación para agentes de IA > Cómo consumir la documentación de la API v2 de Legaldoc.io desde un LLM, un agente de desarrollo o cualquier herramienta automatizada. Esta documentación está pensada para que la lean tanto personas como agentes de IA. Todo el contenido se genera como HTML estático y Markdown plano. ## Qué formato conviene usar [Sección titulada «Qué formato conviene usar»](#qué-formato-conviene-usar) | Formato | Úsalo cuando… | | ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Markdown por página** — p. ej. [`/guides/integration-guide.md`](/guides/integration-guide.md) | Quieres pasarle a un LLM una sola página, o copiarla al chat sin el ruido del HTML. | | [**`/llms.txt`**](/llms.txt) | Quieres que el agente primero descubra qué contiene el sitio y después decida qué leer. Es el índice según el estándar [llmstxt.org](https://llmstxt.org/). | | [**`/llms-full.txt`**](/llms-full.txt) | Quieres cargar todas las guías completas en el contexto de una sola vez. | | [**`/llms-small.txt`**](/llms-small.txt) | Lo mismo, pero en versión reducida para modelos con ventana de contexto chica. | | [**OpenAPI 3.1**](/openapi/legaldoc-v2.json) | Tu herramienta entiende OpenAPI: es la fuente de verdad de la referencia de API (Envelope, Recipients, Fields, Items, Attachments, Folder, Embedding — con schemas y autenticación). | Cómo se reparte el contenido `llms-full.txt` contiene las **guías** (Guía de Integración y Firma Electrónica Avanzada). La **referencia de API** no se duplica ahí: para eso está la especificación OpenAPI, que ya es machine-readable y siempre está sincronizada con la documentación publicada. ## Markdown de cada página [Sección titulada «Markdown de cada página»](#markdown-de-cada-página) Cada página de guía se publica también como Markdown plano: agrega `.md` a su ruta. ```plaintext https://developers.legaldoc.io/guides/integration-guide.md https://developers.legaldoc.io/guides/fea-orquestacion.md ``` Además, arriba de cada guía hay botones para **copiar el Markdown** al portapapeles, **verlo** en crudo, o abrir esa página directamente en **Claude**, **ChatGPT** o **Gemini** con el contexto ya cargado. ## Cómo apuntar un agente a esta documentación [Sección titulada «Cómo apuntar un agente a esta documentación»](#cómo-apuntar-un-agente-a-esta-documentación) Para un asistente con acceso a internet, el punto de entrada recomendado es `llms.txt`: ```text Lee https://developers.legaldoc.io/llms.txt y luego los documentos que necesites de ahí para ayudarme a integrar la API v2 de Legaldoc.io. ``` Si va a generar código contra la API, conviene darle directamente la especificación OpenAPI: ```text Usa https://developers.legaldoc.io/openapi/legaldoc-v2.json como referencia de la API v2 de Legaldoc.io y ayúdame a implementar el flujo de firma (y Firma Electrónica Avanzada si corresponde). ``` ## Autenticación [Sección titulada «Autenticación»](#autenticación) Cualquier código que genere un agente necesita autenticarse. Todas las peticiones a la API llevan la API Key en el header `Authorization`, sin prefijo `Bearer`: ```http Authorization: {API_KEY} Content-Type: application/json ``` El flujo completo está en la [Guía de Integración](/guides/integration-guide/), y lo que agrega Firma Electrónica Avanzada en su [capítulo dedicado](/guides/fea-orquestacion/). # Autenticación > Cómo autenticar tus peticiones a la API v2 de Legaldoc.io. Legaldoc.io usa API Keys para autenticar las peticiones a la API. Cada key es un token único asociado a tu cuenta o equipo — la API la usa para identificar quién hace la solicitud y autorizar su acceso. ## Obtener una API Key [Sección titulada «Obtener una API Key»](#obtener-una-api-key) Puedes generar y administrar tus API Keys desde la configuración de tu cuenta o equipo en la plataforma de Legaldoc. Al crear una, elige un nombre descriptivo y, opcionalmente, una fecha de expiración. Guarda la key de forma segura apenas se genera: no vuelve a mostrarse completa después. ## Usar la API Key [Sección titulada «Usar la API Key»](#usar-la-api-key) Incluye la key en el header `Authorization` de cada petición, sin prefijo `Bearer`: ```http Authorization: {API_KEY} Content-Type: application/json ``` Ejemplo con cURL: ```bash curl --location 'https://app.legaldoc.com/api/v2/envelope' \ --header 'Authorization: {API_KEY}' ``` ## Seguridad [Sección titulada «Seguridad»](#seguridad) La API Key tiene acceso a tu cuenta y todos sus recursos. Mantenla segura y no la compartas. Si sospechas que fue comprometida, revócala y genera una nueva desde la misma sección de configuración. # Variables CSS > Personaliza la apariencia de la experiencia de firma integrada usando variables CSS. Estas variables aplican al widget de firma embebido descrito en [Web Components](/guides/web-components/) — son la forma de personalizar su apariencia, no un mecanismo aparte. Los clientes de la plataforma tienen acceso a un conjunto completo de variables CSS que pueden usarse para personalizar la apariencia de la experiencia de firma integrada. Estas variables controlan todo, desde colores hasta espaciado, y pueden usarse para coincidir con el sistema de diseño de tu aplicación. ## Variables Disponibles [Sección titulada «Variables Disponibles»](#variables-disponibles) ### Colores [Sección titulada «Colores»](#colores) | Variable | Descripción | Predeterminado | | ----------------------- | ----------------------------------------- | ----------------------- | | `background` | Color de fondo base | Por defecto del sistema | | `foreground` | Color de texto base | Por defecto del sistema | | `muted` | Color de fondo tenue/sutil | Por defecto del sistema | | `mutedForeground` | Color de texto tenue/sutil | Por defecto del sistema | | `popover` | Color de fondo de popover/dropdown | Por defecto del sistema | | `popoverForeground` | Color de texto de popover/dropdown | Por defecto del sistema | | `card` | Color de fondo de tarjeta | Por defecto del sistema | | `cardBorder` | Color de borde de tarjeta | Por defecto del sistema | | `cardBorderTint` | Color de borde resaltado de tarjeta | Por defecto del sistema | | `cardForeground` | Color de texto de tarjeta | Por defecto del sistema | | `fieldCard` | Color de fondo de tarjeta de campo | Por defecto del sistema | | `fieldCardBorder` | Color de borde de tarjeta de campo | Por defecto del sistema | | `fieldCardForeground` | Color de texto de tarjeta de campo | Por defecto del sistema | | `widget` | Color de fondo de widget | Por defecto del sistema | | `widgetForeground` | Color de texto de widget | Por defecto del sistema | | `border` | Color de borde predeterminado | Por defecto del sistema | | `input` | Color de borde de campo de entrada | Por defecto del sistema | | `primary` | Color de acción/botón principal | Por defecto del sistema | | `primaryForeground` | Color de texto de acción/botón principal | Por defecto del sistema | | `secondary` | Color de acción/botón secundario | Por defecto del sistema | | `secondaryForeground` | Color de texto de acción/botón secundario | Por defecto del sistema | | `accent` | Color de acento/resaltado | Por defecto del sistema | | `accentForeground` | Color de texto de acento/resaltado | Por defecto del sistema | | `destructive` | Color de acción destructiva/peligrosa | Por defecto del sistema | | `destructiveForeground` | Color de texto destructivo/peligroso | Por defecto del sistema | | `ring` | Color del anillo de enfoque | Por defecto del sistema | | `warning` | Color de advertencia/alerta | Por defecto del sistema | ### Espaciado y Diseño [Sección titulada «Espaciado y Diseño»](#espaciado-y-diseño) | Variable | Descripción | Predeterminado | | -------- | ------------------------------------------ | ----------------------- | | `radius` | Tamaño del radio del borde en unidades REM | Por defecto del sistema | ## Ejemplo de Uso [Sección titulada «Ejemplo de Uso»](#ejemplo-de-uso) Así es como usar estas variables en tu implementación de integración: ```jsx const cssVars = { // Colores background: '#ffffff', foreground: '#000000', primary: '#0000ff', primaryForeground: '#ffffff', accent: '#4f46e5', destructive: '#ef4444', // Espaciado radius: '0.5rem' }; // ``` ## Formato de Color [Sección titulada «Formato de Color»](#formato-de-color) Los colores pueden especificarse en cualquier formato de color CSS válido: * Hexadecimal: `#ff0000` * RGB: `rgb(255, 0, 0)` * HSL: `hsl(0, 100%, 50%)` * Colores con nombre: `red` Los colores se convertirán automáticamente al formato apropiado internamente. ## Mejores Prácticas [Sección titulada «Mejores Prácticas»](#mejores-prácticas) 1. **Mantener Contraste**: Al personalizar colores, asegúrate de que haya suficiente contraste entre los colores de fondo y primer plano para la accesibilidad. 2. **Probar Modo Oscuro**: Si no has deshabilitado el modo oscuro, prueba tus variables de color tanto en modo claro como oscuro. 3. **Usar Colores de Marca**: Alinea los colores primarios y de acento con el esquema de colores de tu marca para una apariencia cohesiva. 4. **Radio Consistente**: Usa un valor de radio de borde consistente que coincida con el sistema de diseño de tu aplicación. ## Objetivos de Clase CSS [Sección titulada «Objetivos de Clase CSS»](#objetivos-de-clase-css) Además de las variables CSS, los componentes específicos en la experiencia integrada pueden ser dirigidos usando clases CSS para un estilo más granular: ### Clases de Componentes [Sección titulada «Clases de Componentes»](#clases-de-componentes) | Nombre de Clase | Descripción | | --------------------------------- | ------------------------------------------------------------------------ | | `.embed--Root` | Contenedor principal para la experiencia de firma integrada | | `.embed--DocumentContainer` | Contenedor para el documento y widget de firma | | `.embed--DocumentViewer` | Contenedor para el visor de documentos | | `.embed--DocumentWidget` | Contenedor del widget de firma | | `.embed--DocumentWidgetContainer` | Contenedor externo para el widget de firma, maneja el posicionamiento | | `.embed--DocumentWidgetHeader` | Sección de encabezado del widget de firma | | `.embed--DocumentWidgetContent` | Área de contenido principal del widget de firma | | `.embed--DocumentWidgetForm` | Sección de formulario dentro del widget de firma | | `.embed--DocumentWidgetFooter` | Sección de pie de página del widget de firma | | `.embed--WaitingForTurn` | Contenedor para la pantalla de espera cuando no es el turno del usuario | | `.embed--DocumentCompleted` | Contenedor para la pantalla de finalización después de firmar | | `.field--FieldRootContainer` | Contenedor base para campos de documento (firmas, texto, casillas, etc.) | Los componentes de campo también exponen varios atributos de datos que pueden usarse para dar estilo a diferentes estados: | Atributo de Datos | Valores | Descripción | | ------------------- | ---------------------------------------------- | -------------------------------- | | `[data-field-type]` | `SIGNATURE`, `TEXT`, `CHECKBOX`, `RADIO`, etc. | El tipo de campo | | `[data-inserted]` | `true`, `false` | Si el campo ha sido llenado | | `[data-validate]` | `true`, `false` | Si el campo está siendo validado | ### Ejemplo de Estilo de Campo [Sección titulada «Ejemplo de Estilo de Campo»](#ejemplo-de-estilo-de-campo) ```css /* Estilo para todos los contenedores de campo */ .field--FieldRootContainer { transition: all 200ms ease; } /* Estilo para tipos de campo específicos */ .field--FieldRootContainer[data-field-type='SIGNATURE'] { background-color: rgba(0, 0, 0, 0.02); } /* Estilo para campos insertados */ .field--FieldRootContainer[data-inserted='true'] { background-color: var(--primary); opacity: 0.2; } /* Estilo para campos siendo validados */ .field--FieldRootContainer[data-validate='true'] { border-color: orange; } ``` ### Ejemplo de Uso [Sección titulada «Ejemplo de Uso»](#ejemplo-de-uso-1) ```css /* Estilos personalizados para el widget de documento */ .embed--DocumentWidget { background-color: #ffffff; box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1); } /* Estilos personalizados para la pantalla de espera */ .embed--WaitingForTurn { background-color: #f9fafb; padding: 2rem; } /* Ajustes responsivos para el contenedor de documento */ @media (min-width: 768px) { .embed--DocumentContainer { gap: 2rem; } } ``` # Firma Electrónica Avanzada > Qué agrega la Firma Electrónica Avanzada (FEA) sobre el flujo general de la API v2 de Legaldoc.io, y cómo orquestarlo. Este capítulo explica cómo integrar Firma Electrónica Avanzada (FEA) en tu aplicación: qué construyes tú, qué hace Legaldoc, y qué le entregas a tu usuario al final. Se apoya en el flujo general que ya viste en la [Guía de Integración](/guides/integration-guide/) — crear un envelope, agregar firmantes y campos, distribuirlo — y describe qué cambia cuando ese envelope necesita FEA. Si nunca has creado un documento con la API, empieza por la [Guía de Integración](/guides/integration-guide/). Este capítulo asume que ya sabes crear un envelope, agregar firmantes y campos, y distribuirlo. *** ## 1. Qué es FEA y qué cambia [Sección titulada «1. Qué es FEA y qué cambia»](#1-qué-es-fea-y-qué-cambia) La Firma Electrónica Avanzada certifica la identidad del firmante contra una entidad certificadora autorizada, usando el RUT como credencial. A diferencia de la firma simple, el firmante debe verificar su identidad y confirmar con un segundo factor antes de que la firma quede aplicada. Para tu integración, esto significa tres diferencias concretas respecto a un envelope normal: * El firmante necesita un **RUT válido**. * El **orden de firma debe ser secuencial**, aunque tengas un solo firmante. * Cada firmante tiene **exactamente un campo de firma**. El resto del ciclo de vida —crear, distribuir, monitorear, completar— es igual al de cualquier documento. La preparación del documento (sección 3 de la Guía de Integración) también aplica igual: con FEA, con más razón, el PDF debe estar en su versión final antes de crear el envelope. *** ## 2. Crear el envelope y los firmantes [Sección titulada «2. Crear el envelope y los firmantes»](#2-crear-el-envelope-y-los-firmantes) Sobre el ejemplo general, un envelope con FEA agrega el RUT del firmante y fuerza el orden secuencial: ```jsonc { "type": "DOCUMENT", "globalActionAuth": ["FAO_HASH"], "recipients": [ { "name": "Nombre del firmante", "email": "firmante@ejemplo.cl", "rut": "12345678-9", "role": "SIGNER", "signingOrder": 1 } ], "meta": { "signingOrder": "SEQUENTIAL" } } ``` ### El nombre que envías y el nombre verificado [Sección titulada «El nombre que envías y el nombre verificado»](#el-nombre-que-envías-y-el-nombre-verificado) El campo `name` que envías es informativo: identifica al destinatario para efectos de envío de correos y presentación en tu propia interfaz. **No es la identidad legal de la firma.** Cuando el firmante completa la verificación con su entidad certificadora, Legaldoc obtiene el nombre completo asociado a ese RUT y es **ese** el que queda estampado en el documento y en el certificado de firma. Si el nombre que enviaste difiere del verificado, el certificado de firma muestra ambos, para que quede explícito cuál es cuál. Qué hacer No asumas que el nombre visible en el documento final es el mismo que enviaste. Si tu aplicación necesita mostrar “quién firmó” después del hecho, consulta el certificado de firma ([sección 4](#4-entregar-la-evidencia)) en vez de tu propio registro. ### Validaciones que ocurren al distribuir [Sección titulada «Validaciones que ocurren al distribuir»](#validaciones-que-ocurren-al-distribuir) Legaldoc valida las reglas de FEA (RUT, orden secuencial, un campo por firmante, roles permitidos) en el momento de **distribuir** el envelope, no al crearlo. Si tu integración construye el envelope en varios pasos, verifica estas condiciones en tu propio código antes de intentar distribuir, para darle a tu usuario un error temprano y claro en vez de uno tardío. ### Ubicación del campo de firma [Sección titulada «Ubicación del campo de firma»](#ubicación-del-campo-de-firma) La [Guía de Integración](/guides/integration-guide/#2-agregar-campos-de-firma) explica las dos formas de indicar dónde va un campo: placeholder o coordenadas. Con FEA, el riesgo de usar coordenadas es distinto —y menor— gracias a la misma validación al distribuir que se menciona arriba. En un envelope estándar, un `page` inválido en una llamada por coordenadas no falla hasta que se sella el documento, con el firmante ya habiendo firmado. Con FEA, Legaldoc prepara el documento completo —marca de agua, QR y los campos de firma ya ubicados— en el momento de `POST /envelope/distribute`, no al sellar. Si una coordenada no calza, `distribute` falla ahí mismo, todavía sin firmantes. Sigue siendo un error evitable —el placeholder lo elimina antes incluso de llegar a distribuir— pero si tu PDF es fijo y ya mediste la posición a mano, usar coordenadas en FEA no tiene el riesgo catastrófico que tiene en el flujo estándar. Un detalle propio de FEA: recuerda que cada firmante debe tener **exactamente un** campo de firma (sección 1). Si usas `matchAll: true` con un placeholder que aparece más de una vez en el documento, se crea un campo por cada aparición — evita `matchAll` en FEA a menos que el placeholder aparezca una sola vez por firmante. *** ## 3. La experiencia de firma [Sección titulada «3. La experiencia de firma»](#3-la-experiencia-de-firma) Una vez distribuido, redirige a tu usuario a la URL de firma que devuelve la API (`recipients[].signingUrl`), igual que en el flujo general. A partir de ahí, **todo el proceso de verificación de identidad y confirmación ocurre dentro de la página de Legaldoc**: el enrolamiento con la entidad certificadora (si es la primera vez) y el segundo factor de autenticación. Tu aplicación no participa en ese intercambio y no necesita construir ninguna pantalla para él. Si tu usuario cierra el navegador antes de completar el enrolamiento o la firma, el envelope queda `PENDING` y puede retomarlo volviendo a la misma URL de firma, si aún es válida, o solicitando que reenvíes la notificación. *** ## 4. Entregar la evidencia [Sección titulada «4. Entregar la evidencia»](#4-entregar-la-evidencia) Cuando el envelope está `COMPLETED`, con FEA hay tres artefactos disponibles, no uno: | Artefacto | Endpoint | Contenido | | --------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------- | | Documento firmado | `GET /envelope/item/{envelopeItemId}/download?version=signed` | El PDF con las firmas aplicadas. | | Certificado de firma | `GET /envelope/{envelopeId}/certificate/download` | Quién firmó, con qué identidad verificada, cuándo, y con qué nivel de autenticación. | | Registro de auditoría | `GET /envelope/{envelopeId}/audit-log/download` | La traza completa de eventos del proceso (envío, visualización, firma, finalización). | ### Por qué son archivos separados [Sección titulada «Por qué son archivos separados»](#por-qué-son-archivos-separados) En un flujo de firma simple, el certificado y la auditoría pueden anexarse como páginas adicionales del mismo PDF, porque se agregan antes de que exista ninguna firma. Con FEA eso no es posible: el certificado describe información que solo existe **después** de que el firmante firmó (su identidad verificada, la hora exacta). No hay forma de anexarlo al documento sin invalidar, en la práctica, la firma que ya se aplicó. Qué hacer Ofrece los tres como descargas independientes en tu interfaz. No intentes combinarlos en un solo archivo. El documento firmado es tu entregable principal; el certificado y la auditoría son su respaldo, y conviene presentarlos como tales —por ejemplo, como acciones secundarias junto a la descarga principal. Si necesitas la auditoría en formato de datos en vez de PDF (para integrarla a tu propio sistema en vez de mostrarla), está disponible también como JSON en `GET /envelope/{envelopeId}/audit-log`. *** ## 5. Rechazo [Sección titulada «5. Rechazo»](#5-rechazo) Un firmante puede rechazar el documento en lugar de firmarlo. Cuando eso ocurre: * El envelope pasa a estado `REJECTED`. * Recibes el evento `DOCUMENT_REJECTED` por webhook. * El documento y el certificado de firma (si corresponde) quedan disponibles reflejando el rechazo y su motivo. Si el rechazo ocurre antes de que exista alguna firma, no hay evidencia de firma que preservar. Si ocurre después de que otro firmante ya firmó (en un flujo con múltiples firmantes), esa firma previa se conserva intacta como parte del historial del documento. *** ## 6. Resumen de endpoints [Sección titulada «6. Resumen de endpoints»](#6-resumen-de-endpoints) Además de los endpoints generales (crear, agregar campos, distribuir, consultar estado — ver la [Guía de Integración](/guides/integration-guide/#10-resumen-de-endpoints)), FEA agrega la descarga del certificado y la auditoría: | Acción | Endpoint | | ------------------------------ | ------------------------------------------------- | | Descargar certificado de firma | `GET /envelope/{envelopeId}/certificate/download` | | Descargar auditoría (PDF) | `GET /envelope/{envelopeId}/audit-log/download` | | Consultar auditoría (JSON) | `GET /envelope/{envelopeId}/audit-log` | *** ## Errores frecuentes [Sección titulada «Errores frecuentes»](#errores-frecuentes) | Situación | Causa habitual | | --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | | La distribución falla con un error de validación | Falta el RUT de un firmante, el orden de firma no es secuencial, o hay más de un campo de firma por firmante. | | La descarga del certificado o la auditoría devuelve error | El envelope todavía no está en estado `COMPLETED`. | | El nombre en el documento no coincide con el que envié | Es esperable: el documento y el certificado muestran la identidad verificada, no la enviada. Ver [sección 2](#2-crear-el-envelope-y-los-firmantes). | # Guía de Integración > Guía para implementar un flujo de firma electrónica usando la API v2 de Legaldoc.io. Guía para implementar un flujo de firma electrónica usando la API v2 de Legaldoc.io: crear un documento (envelope), agregar firmantes y campos, distribuirlo, y obtener el resultado firmado. Este flujo aplica a cualquier tipo de firma. Si tu caso de uso requiere verificar la identidad del firmante contra una entidad certificadora, ver el capítulo de [Firma Electrónica Avanzada](/guides/fea-orquestacion/). *** ## Flujo de Integración [Sección titulada «Flujo de Integración»](#flujo-de-integración) 1. [Crearel envelope](#1-crear-el-envelope-y-los-firmantes) 2. [Agregarcampos de firma](#2-agregar-campos-de-firma) 3. [Distribuiry firmar](#4-distribuir-y-la-experiencia-de-firma) 4. [Consultarel estado](#5-saber-cuándo-terminó) 5. [Descargarel resultado](#6-descargar-el-documento-firmado) *** ## 1. Crear el envelope y los firmantes [Sección titulada «1. Crear el envelope y los firmantes»](#1-crear-el-envelope-y-los-firmantes) ```http POST /envelope/create ``` Un **envelope** es el objeto central de la API: representa el documento (o conjunto de documentos) que vas a hacer firmar, junto con sus destinatarios, sus campos y su estado actual. Todo el flujo de firma gira en torno a un envelope — se crea una sola vez, y el resto de las llamadas (agregar campos, distribuir, consultar estado, descargar) actúan sobre el `id` que devuelve esta creación. ```jsonc { "type": "DOCUMENT", "recipients": [ { "name": "Nombre del firmante", "email": "firmante@ejemplo.cl", "role": "SIGNER", "signingOrder": 1 } ] } ``` `role` acepta `SIGNER`, `VIEWER`, `APPROVER`, `CC` o `ASSISTANT`, según qué participación tenga ese destinatario en el documento. `signingOrder` es opcional para firma simple — si no lo defines, los firmantes pueden firmar en cualquier orden. Ver la especificación completa de este endpoint en la [Referencia de la API](/api/operations/envelope-create/). *** ## 2. Agregar campos de firma [Sección titulada «2. Agregar campos de firma»](#2-agregar-campos-de-firma) ```http POST /envelope/field/create-many ``` Cada firmante necesita al menos un campo para poder firmar. Además de `SIGNATURE`, la API soporta campos de texto, fecha, checkbox, radio, dropdown y otros — ver la [Referencia de la API](/api/) para el detalle de cada tipo. ### Dónde ubicar el campo en el PDF [Sección titulada «Dónde ubicar el campo en el PDF»](#dónde-ubicar-el-campo-en-el-pdf) Hay dos formas de indicarle a Legaldoc dónde va cada campo, y son excluyentes entre sí: | Forma | Campos | Cuándo usarla | | ----------------------------- | ------------------------------------------------------------------- | --------------------------------------------------------- | | **Placeholder** (recomendada) | `placeholder`, opcionalmente `width`, `height` | Siempre que puedas insertar un texto ancla en el PDF. | | Coordenadas | `page`, `positionX`, `positionY`, `width`, `height` (en porcentaje) | Solo cuando no tienes control sobre el contenido del PDF. | Con **placeholder**, le das a Legaldoc un texto ancla que buscar dentro del PDF: ```jsonc { "recipientId": 456, "type": "SIGNATURE", "placeholder": "{{FIRMA_MANDANTE}}", "width": 30, "height": 8 } ``` Legaldoc busca ese texto en el documento, ubica el campo en su posición exacta y tapa el texto original con un rectángulo blanco antes de guardar. No necesitas calcular en qué página cae ni medir coordenadas, y si el documento cambia de largo entre una generación y otra —una plantilla con párrafos variables, por ejemplo— el campo se sigue ubicando correctamente sin que toques nada. Si el placeholder no aparece en el PDF, la creación del campo falla de inmediato con un error claro (`Placeholder "..." not found in PDF`), antes de distribuir el documento y antes de que exista ningún firmante. Con **coordenadas**, en cambio, indicas directamente la página y la posición como porcentaje del ancho/alto de la página. Úsalas solo cuando no puedas insertar un texto ancla en el PDF —por ejemplo, un documento escaneado o de un tercero. Coordenadas: valida la página antes de distribuir Legaldoc no valida que la página exista al crear el campo — si indicas `page: 4` en un documento de 3 páginas, la llamada de creación no falla. El error recién aparece más adelante (el momento exacto depende del tipo de flujo — ver el detalle en el capítulo de [Firma Electrónica Avanzada](/guides/fea-orquestacion/#ubicaci%C3%B3n-del-campo-de-firma)), potencialmente con el firmante ya interactuando con el documento. Verifica tú mismo el número de páginas antes de crear campos por coordenadas. Si usas placeholder, ponlo en su propia línea de texto, sin justificar el párrafo y sin espaciado extra entre letras o palabras: si el motor que genera tu PDF lo parte en varios fragmentos de texto, Legaldoc no lo reconoce como una sola cadena. Ver la especificación completa de este endpoint en la [Referencia de la API](/api/operations/envelope-field-createmany/). *** ## 3. Preparar el documento [Sección titulada «3. Preparar el documento»](#3-preparar-el-documento) Antes de crear el envelope, decide el contenido final de tu PDF. **El documento no admite cambios de diseño una vez que empieza a firmarse**: no se pueden agregar páginas, marcas de agua, sellos ni ningún otro elemento visual después de la primera firma. Esto no es una limitación de Legaldoc: es una propiedad de cómo funcionan las firmas digitales sobre PDF. Una firma cubre criptográficamente los bytes del documento en el momento en que se aplica; cualquier cambio posterior —aunque no toque el contenido firmado— hace que lectores como Adobe Acrobat reporten la firma como inválida o el documento como modificado. Qué hacer Asegúrate de que tu PDF esté en su versión final —con toda la maquetación, numeración de páginas y contenido que quieras que el firmante vea— antes de crearlo en Legaldoc. Legaldoc aplica su propia marca de agua legal y el código de verificación antes de que exista cualquier firma, así que esa parte no requiere que hagas nada. *** ## 4. Distribuir y la experiencia de firma [Sección titulada «4. Distribuir y la experiencia de firma»](#4-distribuir-y-la-experiencia-de-firma) ```http POST /envelope/distribute ``` Una vez distribuido, redirige a tu usuario a la URL de firma que devuelve la API (`recipients[].signingUrl`). Tu responsabilidad termina en redirigir al usuario, y vuelve a empezar cuando el usuario regresa a tu aplicación (mediante el `redirectUrl` que configuraste) o cuando recibes la notificación de que el documento cambió de estado. Ver la especificación completa de este endpoint en la [Referencia de la API](/api/operations/envelope-distribute/). *** ## 5. Saber cuándo terminó [Sección titulada «5. Saber cuándo terminó»](#5-saber-cuándo-terminó) Usa **webhooks** para enterarte de cambios de estado sin tener que consultar activamente. Los eventos relevantes son `DOCUMENT_COMPLETED` y `DOCUMENT_REJECTED`. Si prefieres o necesitas confirmar el estado de forma síncrona, consulta: ```http GET /envelope/{envelopeId} ``` y revisa el campo `status`: | Estado | Significado | | ----------- | -------------------------------------------------------------- | | `PENDING` | Aún faltan firmas o el proceso está en curso. | | `COMPLETED` | Todos los firmantes completaron. El documento está disponible. | | `REJECTED` | Un firmante rechazó firmar. Ver [sección 7](#7-rechazo). | Ver la especificación completa de este endpoint en la [Referencia de la API](/api/operations/envelope-get/). *** ## 6. Descargar el documento firmado [Sección titulada «6. Descargar el documento firmado»](#6-descargar-el-documento-firmado) ```http GET /envelope/item/{envelopeItemId}/download?version=signed ``` El PDF devuelto incluye las firmas aplicadas y la traza de auditoría del proceso. Ver la especificación completa de este endpoint en la [Referencia de la API](/api/operations/envelope-item-download/). *** ## 7. Rechazo [Sección titulada «7. Rechazo»](#7-rechazo) Un firmante puede rechazar el documento en lugar de firmarlo. Cuando eso ocurre, el envelope pasa a estado `REJECTED` y recibes el evento `DOCUMENT_REJECTED` por webhook. El documento queda disponible reflejando el rechazo y su motivo. *** ## 8. Verificación por terceros [Sección titulada «8. Verificación por terceros»](#8-verificación-por-terceros) Todo documento generado por Legaldoc incluye un código QR y un código de verificación alfanumérico visibles en el documento. Cualquier persona que reciba una copia del documento —dentro o fuera de tu aplicación— puede escanear el código o visitar la página pública de verificación para confirmar su autenticidad, sin necesitar acceso a tu sistema ni al de Legaldoc. No necesitas construir nada para habilitar esto: viene incluido en el documento desde su creación. *** ## 9. Firma Electrónica Avanzada [Sección titulada «9. Firma Electrónica Avanzada»](#9-firma-electrónica-avanzada) Si tu caso de uso necesita certificar la identidad del firmante —por ejemplo, contratos que requieren validez legal reforzada— usa **Firma Electrónica Avanzada (FEA)**. Sobre el flujo que acabas de ver, FEA agrega: RUT del firmante, orden de firma secuencial, y evidencia adicional (certificado y auditoría) como descargas independientes. Ver el capítulo completo: [Firma Electrónica Avanzada](/guides/fea-orquestacion/). *** ## 10. Resumen de endpoints [Sección titulada «10. Resumen de endpoints»](#10-resumen-de-endpoints) | Acción | Endpoint | | --------------------------- | ------------------------------------------------------------- | | Crear envelope | `POST /envelope/create` | | Agregar campos de firma | `POST /envelope/field/create-many` | | Distribuir | `POST /envelope/distribute` | | Consultar estado | `GET /envelope/{envelopeId}` | | Descargar documento firmado | `GET /envelope/item/{envelopeItemId}/download?version=signed` | ### Otros recursos de la API v2 [Sección titulada «Otros recursos de la API v2»](#otros-recursos-de-la-api-v2) Esta guía cubre el flujo mínimo de creación y firma. La API v2 expone además otros recursos — ver la [Referencia de API](/api/) para el detalle completo de cada uno: | Recurso | Para qué sirve | | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | | `Envelope` (list, get-many, use, update, delete, cancel, duplicate, redistribute) | Gestión del ciclo de vida completo del envelope más allá de crear/distribuir/consultar: listar, cancelar, duplicar, reenviar, etc. | | `Envelope Recipients` (get, create-many, update-many, delete, reject-on-behalf-of) | Gestión de firmantes después de creado el envelope, incluyendo rechazar en nombre de un firmante. | | `Envelope Fields` (get, update-many, delete) | Gestión de campos individuales más allá de la creación masiva inicial. | | `Envelope Items` (create-many, update-many, delete) | Gestión de los documentos/archivos dentro de un envelope. | | `Envelope Attachments` | Adjuntos asociados a un envelope, independientes de los documentos a firmar. | | `Folder` | Organización de envelopes en carpetas. | | `Embedding` (presign tokens) | Generar y verificar tokens de firma para incrustar la experiencia de firma en tu propia aplicación, en vez de redirigir a `signingUrl`. | *** ## Errores frecuentes [Sección titulada «Errores frecuentes»](#errores-frecuentes) | Situación | Causa habitual | | -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | | La distribución falla con un error de validación | Falta un campo requerido, o un firmante no tiene ningún campo de firma asignado. | | La creación del campo falla con `Placeholder "..." not found in PDF` | El texto ancla no existe tal cual en el PDF, o quedó partido en varios fragmentos por el motor de generación (justificado, espaciado de letras). | | La descarga del documento devuelve error | El envelope todavía no está en estado `COMPLETED`. | # Integración de Componentes Web > Aprende cómo usar nuestra integración vía Componentes Web en tu aplicación web. Nuestros Componentes Web proporcionan una forma simple de integrar una experiencia de firma (el widget embebido) dentro de tu aplicación web sin framework. Soporta tanto plantillas de enlace directo como tokens de firma. Para personalizar su apariencia, ver [Variables CSS](/guides/css-variables/). ## Instalación [Sección titulada «Instalación»](#instalación) En tu archivo HTML, agrega lo siguiente para añadir el script, reemplazando la ruta con la ruta apropiada al script del componente web. ```html ``` ## Uso [Sección titulada «Uso»](#uso) Para integrar una experiencia de firma, necesitarás proporcionar el token para el documento que deseas integrar. Esto se puede hacer de diferentes maneras, dependiendo de tu caso de uso. ### Plantilla de Enlace Directo [Sección titulada «Plantilla de Enlace Directo»](#plantilla-de-enlace-directo) Si tienes una plantilla de enlace directo, puedes simplemente proporcionar el token para la plantilla al tag `legaldoc-embed-direct-template`. ```html ``` #### Atributos [Sección titulada «Atributos»](#atributos) | Atributo | Tipo | Descripción | | ------------------- | ------------------- | ------------------------------------------------------------------------------------------- | | token | string | El token para el documento que deseas integrar | | name | string (opcional) | El nombre del firmante que se usará por defecto para firmar | | lockName | boolean (opcional) | Si el campo de nombre debe estar bloqueado no permitiendo modificaciones | | email | string (opcional) | El email del firmante que se usará por defecto para firmar | | lockEmail | boolean (opcional) | Si el campo de email debe estar bloqueado no permitiendo modificaciones | | onDocumentReady | function (opcional) | Una función de callback que se llamará cuando el documento esté cargado y listo para firmar | | onDocumentCompleted | function (opcional) | Una función de callback que se llamará cuando el documento haya sido completado | | onDocumentError | function (opcional) | Una función de callback que se llamará cuando ocurra un error con el documento | | onFieldSigned | function (opcional) | Una función de callback que se llamará cuando un campo sea firmado | | onFieldUnsigned | function (opcional) | Una función de callback que se llamará cuando un campo sea desfirmado | ### Token de Firma [Sección titulada «Token de Firma»](#token-de-firma) Si tienes un token de firma, puedes proporcionarlo al tag `legaldoc-embed-sign-document`. ```html ``` #### Atributos [Sección titulada «Atributos»](#atributos-1) | Atributo | Tipo | Descripción | | ------------------- | ------------------- | ------------------------------------------------------------------------------------------- | | token | string | El token para el documento que deseas integrar | | name | string (opcional) | El nombre del firmante que se usará por defecto para firmar | | lockName | boolean (opcional) | Si el campo de nombre debe estar bloqueado no permitiendo modificaciones | | onDocumentReady | function (opcional) | Una función de callback que se llamará cuando el documento esté cargado y listo para firmar | | onDocumentCompleted | function (opcional) | Una función de callback que se llamará cuando el documento haya sido completado | | onDocumentError | function (opcional) | Una función de callback que se llamará cuando ocurra un error con el documento | ### Creación vía JavaScript [Sección titulada «Creación vía JavaScript»](#creación-vía-javascript) También puedes crear el elemento tag usando JavaScript, para la generación dinámica de cualquiera de los modos. Por ejemplo, esto agregaría el embed de firma de documento al DOM. ```javascript document.getElementById('mi-contenedor-aqui').innerHTML = ''; const tag = document.createElement('legaldoc-embed-sign-document'); tag.setAttribute('token', data.token); tag.style.width = '100%'; tag.style.height = '100%'; document.getElementById('mi-contenedor-aqui').appendChild(tag); ``` # Webhooks > Cómo recibir notificaciones en tiempo real de los eventos de tus documentos, y cómo verificar que vienen de Legaldoc. Los webhooks son notificaciones HTTP que Legaldoc.io envía a una URL de tu elección cada vez que ocurre un evento sobre un documento o plantilla — sin que tengas que consultar la API periódicamente para saber si algo cambió. Casos de uso típicos: sincronizar el estado de un documento con tu base de datos, disparar un flujo automatizado cuando se completa una firma, o integrar Legaldoc con tu CRM u otros sistemas de terceros. ## Cómo funcionan [Sección titulada «Cómo funcionan»](#cómo-funcionan) 1. Configuras una URL de webhook en Legaldoc. 2. Cuando ocurre un evento, Legaldoc envía un `POST` a esa URL con el tipo de evento y los datos del documento. 3. Tu servidor procesa el evento y responde `200 OK`. Alternativa síncrona Los webhooks son la forma recomendada de enterarte de cambios de estado, pero no la única: en cualquier momento en que necesites confirmar el estado al instante —por ejemplo, en la pantalla de retorno tras la redirección de firma— puedes consultar `GET /envelope/{envelopeId}` directamente. Ver [Guía de Integración](/guides/integration-guide/#5-saber-cu%C3%A1ndo-termin%C3%B3). ## Eventos disponibles [Sección titulada «Eventos disponibles»](#eventos-disponibles) ### Eventos de documento [Sección titulada «Eventos de documento»](#eventos-de-documento) | Evento | Se dispara cuando… | | ------------------------------ | ------------------------------------------------------------------------------------ | | `DOCUMENT_CREATED` | Se crea un nuevo documento. | | `DOCUMENT_SENT` | El documento se envía a los destinatarios. | | `DOCUMENT_OPENED` | Un destinatario abre el documento por primera vez. | | `DOCUMENT_SIGNED` | Un destinatario firma. Se dispara por cada firma individual, no solo al completarse. | | `DOCUMENT_RECIPIENT_COMPLETED` | Un destinatario completa su acción requerida (firmar, aprobar o visar). | | `DOCUMENT_COMPLETED` | Todos los destinatarios completaron su acción. | | `DOCUMENT_REJECTED` | Un destinatario rechaza el documento. | | `DOCUMENT_CANCELLED` | El dueño del documento lo cancela o elimina. | | `DOCUMENT_REMINDER_SENT` | Se envía un recordatorio a un destinatario pendiente. | ### Eventos de plantilla [Sección titulada «Eventos de plantilla»](#eventos-de-plantilla) | Evento | Se dispara cuando… | | ------------------ | ----------------------------------------------------------------------------------------- | | `TEMPLATE_CREATED` | Se crea una nueva plantilla. | | `TEMPLATE_UPDATED` | Se modifica una plantilla (configuración, destinatarios o campos). | | `TEMPLATE_DELETED` | Se elimina una plantilla. | | `TEMPLATE_USED` | Se crea un documento a partir de una plantilla — se dispara junto con `DOCUMENT_CREATED`. | Para el flujo de firma estándar, los eventos que casi siempre te interesan son `DOCUMENT_COMPLETED` y `DOCUMENT_REJECTED` — ver [Guía de Integración](/guides/integration-guide/#5-saber-cu%C3%A1ndo-termin%C3%B3). El resto sirve para trazabilidad más fina, por ejemplo notificar a un usuario interno cuando un destinatario específico firma dentro de un flujo secuencial con varios firmantes. Puedes suscribirte a todos los eventos o solo a los que necesites. ## Estructura del payload [Sección titulada «Estructura del payload»](#estructura-del-payload) Toda notificación comparte esta forma: ```json { "event": "DOCUMENT_COMPLETED", "payload": { /* documento o plantilla, con sus destinatarios */ }, "createdAt": "2024-04-22T11:52:18.277Z", "webhookEndpoint": "https://tu-servidor.com/webhooks/legaldoc" } ``` | Campo | Descripción | | ----------------- | ------------------------------------------------------------------------------------------------------- | | `event` | Identificador del tipo de evento — uno de los listados arriba. | | `payload` | El documento o plantilla afectado, incluyendo su lista de destinatarios y el estado actual de cada uno. | | `createdAt` | Fecha y hora en que se generó la notificación. | | `webhookEndpoint` | La URL a la que se está entregando esta notificación. | Dentro de `payload`, cada destinatario trae su propio estado: `signingStatus` (`NOT_SIGNED`, `SIGNED`, `REJECTED`), `readStatus` (`NOT_OPENED`, `OPENED`) y, si rechazó, `rejectionReason`. El detalle completo de cada recurso está en la [Referencia de la API](/api/). Payloads de ejemplo, no un esquema cerrado La API v2 todavía no publica un esquema OpenAPI específico para los payloads de webhook — los campos disponibles pueden variar según el tipo de documento. Diseña tu integración para ignorar campos que no reconozcas, en vez de asumir una lista fija. ## Configurar un webhook [Sección titulada «Configurar un webhook»](#configurar-un-webhook) Desde la configuración de tu cuenta o equipo, en la sección de Webhooks: 1. Indica la **URL** que va a recibir las notificaciones (debe ser HTTPS). 2. Elige a qué **eventos** te quieres suscribir. 3. Define, opcionalmente, un **secreto** — lo vas a necesitar para verificar la autenticidad de cada notificación (ver más abajo). Tu endpoint debe cumplir estos requisitos: | Requisito | Detalle | | -------------- | ------------------------------------- | | Protocolo | HTTPS | | Método | Acepta `POST` | | Content-Type | `application/json` | | Respuesta | `2xx` dentro de 30 segundos | | Disponibilidad | Accesible públicamente desde internet | Para desarrollo local, expón tu servidor con un túnel (por ejemplo [ngrok](https://ngrok.com)) para poder recibir notificaciones reales mientras pruebas. ## Verificar la autenticidad [Sección titulada «Verificar la autenticidad»](#verificar-la-autenticidad) Si configuraste un secreto, cada notificación incluye el header `X-Legaldoc-Secret` con ese valor: ```http POST /webhooks/legaldoc HTTP/1.1 Content-Type: application/json X-Legaldoc-Secret: tu_secreto_configurado {"event": "DOCUMENT_COMPLETED", "payload": { /* ... */ }} ``` Antes de procesar cualquier notificación, compara ese header contra tu secreto guardado usando una comparación de tiempo constante (no `===` ni `==`), para no filtrar información del secreto a través de variaciones en el tiempo de respuesta: ```javascript const crypto = require('crypto'); function esValida(secretoRecibido, secretoEsperado) { if (!secretoEsperado) return true; // sin secreto configurado if (!secretoRecibido) return false; try { return crypto.timingSafeEqual( Buffer.from(secretoRecibido), Buffer.from(secretoEsperado), ); } catch { return false; // largos distintos } } app.post('/webhooks/legaldoc', (req, res) => { const secreto = req.headers['x-legaldoc-secret']; if (!esValida(secreto, process.env.LEGALDOC_WEBHOOK_SECRET)) { return res.status(401).json({ error: 'Firma inválida' }); } const { event, payload } = req.body; // procesar el evento... res.status(200).json({ received: true }); }); ``` Si la verificación falla, responde `401` sin detallar el motivo y registra el intento para monitoreo — nunca proceses el payload de una notificación que no verificó. ## Reintentos [Sección titulada «Reintentos»](#reintentos) Si tu endpoint no responde `2xx` a tiempo, Legaldoc reintenta la entrega con backoff exponencial: | Intento | Espera | | ------- | ---------- | | 1 | Inmediato | | 2 | 1 minuto | | 3 | 5 minutos | | 4 | 30 minutos | | 5 | 2 horas | Después del quinto intento fallido, la notificación queda marcada como fallida y no se reintenta automáticamente. Por eso conviene diseñar tu handler así: * **Responde rápido**: confirma con `200 OK` de inmediato y procesa el evento de forma asíncrona, en vez de hacer todo el trabajo dentro del mismo request. * **Procesa de forma idempotente**: una misma notificación puede llegar más de una vez (por reintentos, o por un reenvío manual) — que procesarla dos veces no debe causar efectos duplicados en tu sistema. ## Disponibilidad [Sección titulada «Disponibilidad»](#disponibilidad) Los webhooks están disponibles para usuarios individuales y equipos. # Errores comunes > Matriz de referencia para diagnosticar errores de la API y de webhooks. Referencia rápida de los códigos de error que puede devolver la API, con la causa habitual y la acción recomendada para resolverlos. ## Errores generales [Sección titulada «Errores generales»](#errores-generales) | Código | Descripción | Qué hacer | | ------------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | | `ALREADY_EXISTS` | El recurso que intentas crear ya existe. | Verifica si el recurso ya fue creado antes. Usa una operación de actualización en vez de crear uno nuevo. | | `EXPIRED_CODE` | El código de acceso o token proporcionado expiró. | Genera un nuevo código o solicita un nuevo enlace antes de reintentar. | | `INVALID_BODY` | El cuerpo de la petición está mal formado. | Revisa la estructura de tu JSON: que cumpla el esquema esperado y no falte ningún campo requerido. | | `INVALID_REQUEST` | La petición en general es inválida. | Revisa la URL, los parámetros de consulta y los headers. | | `RECIPIENT_EXPIRED` | El enlace de firma del destinatario expiró. | Genera y reenvía una nueva invitación a ese destinatario. | | `LIMIT_EXCEEDED` | Se superó el límite de uso de tu plan. | Revisa los [límites de tu plan](/resources/rate-limits/) o espera al siguiente ciclo de facturación. | | `NOT_FOUND` | El recurso solicitado no existe (404). | Verifica el ID del recurso (envelope, documento) en la URL, y que no haya sido eliminado. | | `NOT_IMPLEMENTED` | La función solicitada no está disponible actualmente. | Consulta la documentación para ver los métodos disponibles. | | `NOT_SETUP` | Falta configuración previa para esta acción. | Completa la configuración necesaria en tu cuenta antes de reintentar. | | `INVALID_CAPTCHA` | Falló la validación del captcha. | Verifica que el token de captcha se genere y envíe correctamente. | | `UNAUTHORIZED` | Falta autenticación o es inválida (401). | Verifica que tu API Key sea correcta y esté en el header `Authorization` — ver [Autenticación](/guides/authentication/). | | `FORBIDDEN` | Acceso denegado al recurso (403). | Verifica que tu API Key tenga los permisos necesarios para esa acción. | | `UNKNOWN_ERROR` | Error interno inesperado (500). | Reintenta más tarde. Si persiste, contacta a soporte con el payload y la hora del incidente. | | `RETRY_EXCEPTION` | La operación falló temporalmente pero se puede reintentar. | Implementa reintentos automáticos, idealmente con backoff exponencial. | | `SCHEMA_FAILED` | Falló la validación estricta del esquema. | Verifica que los tipos de datos enviados coincidan exactamente con la especificación OpenAPI. | | `TOO_MANY_REQUESTS` | Se excedió el límite de frecuencia (429). | Reduce la frecuencia de tus llamadas — ver [Límites de uso](/resources/rate-limits/). | | `TWO_FACTOR_AUTH_FAILED` | Falló la verificación de 2FA. | Verifica que el código 2FA sea correcto y no haya expirado. | | `WEBHOOK_INVALID_REQUEST` | La petición relacionada a un webhook es inválida. | Revisa la configuración de tu endpoint receptor — ver [Webhooks](/guides/webhooks/). | ## Errores de estado del envelope [Sección titulada «Errores de estado del envelope»](#errores-de-estado-del-envelope) Ocurren al intentar una acción incompatible con el estado actual del envelope: | Código | Descripción | Qué hacer | | -------------------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | | `ENVELOPE_DRAFT` | La acción no se puede realizar porque el envelope sigue en `DRAFT`. | Distribúyelo primero — ver [Distribuir y la experiencia de firma](/guides/integration-guide/#4-distribuir-y-la-experiencia-de-firma). | | `ENVELOPE_COMPLETED` | La acción no se puede realizar porque el envelope ya está `COMPLETED`. | No se pueden modificar destinatarios ni campos una vez terminado el proceso de firma. | | `ENVELOPE_REJECTED` | La acción no se puede realizar porque un destinatario rechazó el envelope. | El flujo de firma queda detenido permanentemente. Crea un nuevo envelope si necesitas reenviar el documento. | | `ENVELOPE_LEGACY` | El envelope usa un formato obsoleto. | Recréalo con la versión actual de la API para poder interactuar con él. | *** ## Ver también [Sección titulada «Ver también»](#ver-también) * [Límites de uso](/resources/rate-limits/) — límites de frecuencia y de plan * [Webhooks](/guides/webhooks/) — notificaciones de cambios de estado * [Guía de Integración](/guides/integration-guide/) — flujo general y sus errores frecuentes # Modo desarrollador > Herramientas para depurar IDs de campos, destinatarios y coordenadas en el editor. El modo desarrollador agrega, sobre el editor de documentos de Legaldoc, la información técnica que normalmente no ves como usuario final — útil al construir tu integración, para confirmar que los IDs y coordenadas que usas en la API coinciden con lo que ves en pantalla. ## Qué muestra [Sección titulada «Qué muestra»](#qué-muestra) Con el modo desarrollador activo, cada campo del editor muestra: * **ID del campo** — el identificador único del campo. * **ID del destinatario** — a quién está asignado ese campo. * **Posición (X / Y)** — la posición del campo en la página. * **Ancho / Alto** — las dimensiones del campo. ## Cómo activarlo [Sección titulada «Cómo activarlo»](#cómo-activarlo) Agrega el parámetro `?devmode=true` a la URL del editor de un documento en tu cuenta de Legaldoc. Es útil, por ejemplo, para verificar visualmente dónde quedó ubicado un campo que creaste por coordenadas (ver [Guía de Integración](/guides/integration-guide/#2-agregar-campos-de-firma)) antes de reproducir esa posición en otros documentos. *** ## Ver también [Sección titulada «Ver también»](#ver-también) * [Campos](/resources/fields/) — crear y posicionar campos vía API * [Guía de Integración](/guides/integration-guide/) — placeholder vs. coordenadas # Envelopes > Operaciones adicionales sobre envelopes más allá de crear, distribuir y consultar. La [Guía de Integración](/guides/integration-guide/) cubre el flujo mínimo: crear, distribuir, consultar estado y descargar. Este recurso documenta el resto de las operaciones sobre un envelope — listar con filtros, actualizar, cancelar, duplicar, reenviar y consultar varios a la vez. ## Listar envelopes [Sección titulada «Listar envelopes»](#listar-envelopes) ```http GET /envelope ``` Devuelve una lista paginada. Acepta estos parámetros de consulta: | Parámetro | Valores | Descripción | | ------------------------------------ | -------------------------------------------------------- | ------------------------------------------------------------------ | | `query` | texto | Búsqueda libre por título u otros campos indexados. | | `page` / `perPage` | número | Paginación (`perPage` por defecto 10). | | `type` | `DOCUMENT`, `TEMPLATE` | Filtra por tipo de envelope. | | `templateId` | número | Solo envelopes creados a partir de esa plantilla. | | `source` | `DOCUMENT`, `TEMPLATE`, `TEMPLATE_DIRECT_LINK` | Filtra por origen de creación. | | `status` | `DRAFT`, `PENDING`, `COMPLETED`, `REJECTED`, `CANCELLED` | Filtra por estado. | | `hasExpiredRecipients` | `true`, `false` | Solo envelopes con algún destinatario cuyo enlace de firma expiró. | | `folderId` | string | Filtra por carpeta. | | `orderByColumn` / `orderByDirection` | `createdAt` / `asc`, `desc` | Orden del listado. | Ver la especificación completa en la [Referencia de la API](/api/operations/envelope-find/). ## Consultar varios envelopes [Sección titulada «Consultar varios envelopes»](#consultar-varios-envelopes) ```http POST /envelope/get-many ``` Recibe un arreglo `ids` y devuelve el detalle de cada uno en una sola llamada — útil para evitar N llamadas a `GET /envelope/{envelopeId}` cuando ya tienes los IDs (por ejemplo, después de crear varios documentos desde una plantilla). Ver la especificación completa en la [Referencia de la API](/api/operations/envelope-getmany/). ## Actualizar un envelope [Sección titulada «Actualizar un envelope»](#actualizar-un-envelope) ```http POST /envelope/update ``` Modifica `data` (propiedades del envelope, como `title` o `externalId`) y/o `meta` (asunto, mensaje, `redirectUrl`, etc.) de un envelope existente. Solo tiene efecto mientras el envelope está en `DRAFT` — una vez distribuido, estas propiedades quedan fijas. Ver la especificación completa en la [Referencia de la API](/api/operations/envelope-update/). ## Cancelar un envelope [Sección titulada «Cancelar un envelope»](#cancelar-un-envelope) ```http POST /envelope/cancel ``` Cancela un envelope en curso (`DRAFT` o `PENDING`), con un `reason` opcional. A diferencia de un rechazo (que lo origina un destinatario), la cancelación la origina el dueño del envelope. Ver la especificación completa en la [Referencia de la API](/api/operations/envelope-cancel/). ## Eliminar un envelope [Sección titulada «Eliminar un envelope»](#eliminar-un-envelope) ```http POST /envelope/delete ``` Elimina un envelope. Un envelope `COMPLETED` no se puede eliminar — cancélalo antes si todavía no llegó a ese estado. Ver la especificación completa en la [Referencia de la API](/api/operations/envelope-delete/). ## Duplicar un envelope [Sección titulada «Duplicar un envelope»](#duplicar-un-envelope) ```http POST /envelope/duplicate ``` Crea una copia de un envelope existente. Los flags `includeRecipients` e `includeFields` controlan si la copia trae también los destinatarios y campos originales, o queda en blanco para configurarla desde cero. Ver la especificación completa en la [Referencia de la API](/api/operations/envelope-duplicate/). ## Reenviar a destinatarios pendientes [Sección titulada «Reenviar a destinatarios pendientes»](#reenviar-a-destinatarios-pendientes) ```http POST /envelope/redistribute ``` Vuelve a notificar a destinatarios específicos de un envelope ya distribuido — por ejemplo, si no vieron el correo original o su enlace expiró. Recibe `recipients`, la lista de destinatarios a renotificar. Ver la especificación completa en la [Referencia de la API](/api/operations/envelope-redistribute/). *** ## Ver también [Sección titulada «Ver también»](#ver-también) * [Destinatarios](/resources/recipients/) — agregar y gestionar quién firma * [Campos](/resources/fields/) — tipos de campo y sus opciones * [Plantillas](/resources/templates/) — crear envelopes reutilizables # Campos > Tipos de campo, sus opciones de configuración, y cómo posicionarlos. Un campo (`field`) es un elemento interactivo sobre el PDF — una firma, un texto, una fecha — asignado a un destinatario específico. La [Guía de Integración](/guides/integration-guide/#2-agregar-campos-de-firma) explica cómo crearlos y cómo posicionarlos (placeholder o coordenadas). Este recurso detalla los tipos de campo disponibles y sus opciones de configuración (`fieldMeta`). ## Tipos de campo [Sección titulada «Tipos de campo»](#tipos-de-campo) | Tipo | Descripción | Autocompletado | | ------------- | ------------------------------------------------------------------------------------------------------------------- | -------------- | | `SIGNATURE` | Firma dibujada, tipeada o subida como imagen. | No | | `INITIALS` | Iniciales del destinatario. | No | | `NAME` | Nombre completo del destinatario. | Sí | | `EMAIL` | Correo del destinatario. | Sí | | `DATE` | Fecha en que se completó el campo. | Sí | | `TEXT` | Texto libre. | No | | `NUMBER` | Numérico, con validación opcional de rango. | No | | `RADIO` | Selección única entre opciones. | No | | `CHECKBOX` | Selección múltiple entre opciones. | No | | `DROPDOWN` | Selección única desde un menú desplegable. | No | | `RUT` | Incorpora un RUT dentro del documento, validado contra la estructura del RUT chileno (dígito verificador incluido). | No | | `RUT_FIRMADO` | El RUT asociado a una firma, validado a través de ClaveÚnica. | Sí | ## Opciones comunes (`fieldMeta`) [Sección titulada «Opciones comunes (fieldMeta)»](#opciones-comunes-fieldmeta) Todos los tipos aceptan estas opciones base dentro de `fieldMeta`: | Opción | Tipo | Descripción | | ------------- | ------- | ------------------------------------------------------------------------------------------------------ | | `label` | string | Texto visible junto al campo. | | `placeholder` | string | Texto de ayuda cuando el campo está vacío. | | `required` | boolean | Si el campo debe completarse antes de firmar. | | `readOnly` | boolean | Bloquea el campo con un valor precargado. | | `fontSize` | number | Tamaño de texto en píxeles (8–96, por defecto 12). | | `overflow` | string | `auto`, `horizontal`, `vertical` o `crop` — cómo se comporta el texto que excede el espacio del campo. | ### Opciones adicionales por tipo [Sección titulada «Opciones adicionales por tipo»](#opciones-adicionales-por-tipo) | Tipo | Opciones extra | | ----------------------------------- | ------------------------------------------------------------------------------------------------------------ | | `INITIALS`, `NAME`, `EMAIL`, `DATE` | `textAlign` (`left`, `center`, `right`) | | `TEXT` | `text` (valor por defecto), `characterLimit`, `textAlign`, `lineHeight`, `letterSpacing`, `verticalAlign` | | `NUMBER` | `value`, `minValue`, `maxValue`, `numberFormat`, `textAlign`, `lineHeight`, `letterSpacing`, `verticalAlign` | | `RADIO` | `values` (opciones), `direction` (`vertical`, `horizontal`) | | `CHECKBOX` | `values`, `validationRule`, `validationLength`, `direction` | | `DROPDOWN` | `values`, `defaultValue` | | `RUT` | `text` (valor por defecto), `characterLimit`, `textAlign` | | `RUT_FIRMADO` | `textAlign` | ```jsonc // Ejemplo: campo de texto con validación { "type": "TEXT", "recipientId": 456, "page": 1, "positionX": 10, "positionY": 70, "width": 40, "height": 4, "fieldMeta": { "type": "text", "label": "Cargo", "placeholder": "Ingresa tu cargo", "characterLimit": 100, "required": true } } ``` RUT vs. RUT\_FIRMADO Ambos validan la estructura del RUT chileno (dígito verificador incluido), pero no son lo mismo: `RUT` es un campo para incorporar un RUT dentro del documento, sin estar ligado a una firma. `RUT_FIRMADO` es el RUT asociado a una firma, validado a través de ClaveÚnica. Sello de firma (Firma Electrónica Avanzada) Para campos `SIGNATURE` en un envelope con FEA, `fieldMeta` acepta además un objeto `stamp` que agrega al PDF la información verificada del firmante junto a su firma: ```jsonc { "type": "SIGNATURE", "fieldMeta": { "type": "signature", "stamp": { "enabled": true, "label": "", "layout": "sideBySide", // o "textOnly" "showName": true, "showRut": true, "showDate": true, "border": true } } } ``` `stamp.enabled` está apagado por defecto. Ver [Firma Electrónica Avanzada](/guides/fea-orquestacion/) para el resto de las diferencias que introduce FEA. ## Crear campos [Sección titulada «Crear campos»](#crear-campos) ```http POST /envelope/field/create-many ``` Ver la especificación completa en la [Referencia de la API](/api/operations/envelope-field-createmany/). ## Actualizar campos [Sección titulada «Actualizar campos»](#actualizar-campos) ```http POST /envelope/field/update-many ``` Solo funciona mientras el envelope está en `DRAFT` — una vez distribuido, los campos quedan fijos. Ver la especificación completa en la [Referencia de la API](/api/operations/envelope-field-updatemany/). ## Eliminar un campo [Sección titulada «Eliminar un campo»](#eliminar-un-campo) ```http POST /envelope/field/delete ``` Ver la especificación completa en la [Referencia de la API](/api/operations/envelope-field-delete/). ## Consultar un campo [Sección titulada «Consultar un campo»](#consultar-un-campo) ```http GET /envelope/field/{fieldId} ``` Ver la especificación completa en la [Referencia de la API](/api/operations/envelope-field-get/). *** ## Ver también [Sección titulada «Ver también»](#ver-también) * [Guía de Integración](/guides/integration-guide/#2-agregar-campos-de-firma) — cómo posicionar campos con placeholder o coordenadas * [Destinatarios](/resources/recipients/) — a quién se asigna cada campo * [Firma Electrónica Avanzada](/guides/fea-orquestacion/) — reglas adicionales de FEA sobre los campos # Límites de uso > Límites de frecuencia de la API y de uso por plan. ## Límite de frecuencia (rate limit) [Sección titulada «Límite de frecuencia (rate limit)»](#límite-de-frecuencia-rate-limit) **Límite:** 100 solicitudes por minuto por dirección IP. **Respuesta al excederlo:** `429 Too Many Requests`. ```json { "error": "Too many requests, please try again later." } ``` Actualmente no se exponen headers de rate limit en la respuesta. Si recibes un `429`, espera al menos 60 segundos antes de reintentar. Al exceder un límite de plan, la API responde: ```json { "error": "You have reached your document limit for this month. Please upgrade your plan.", "code": "LIMIT_EXCEEDED", "statusCode": 400 } ``` ## Códigos de error asociados [Sección titulada «Códigos de error asociados»](#códigos-de-error-asociados) | Código | Estado HTTP | Descripción | | ------------------- | ----------- | ------------------------------------- | | `TOO_MANY_REQUESTS` | 429 | Se excedió el límite de frecuencia. | | `LIMIT_EXCEEDED` | 400 | Se excedió un límite de uso del plan. | *** ## Ver también [Sección titulada «Ver también»](#ver-también) * [Autenticación](/guides/authentication/) — cómo se identifican tus peticiones * [Errores comunes](/resources/common-errors/) — referencia completa de códigos de error # Destinatarios > Objeto destinatario, roles, autenticación y orden de firma. Un destinatario (`recipient`) representa a una persona involucrada en un envelope — quien firma, aprueba, o solo recibe una copia. La [Guía de Integración](/guides/integration-guide/#1-crear-el-envelope-y-los-firmantes) muestra cómo crear destinatarios al crear el envelope; este recurso profundiza en el objeto y las operaciones sobre destinatarios ya creados. ## Roles [Sección titulada «Roles»](#roles) | Rol | Comportamiento | | ----------- | ------------------------------------------------------------------------------ | | `SIGNER` | Debe firmar. Sus campos requeridos deben completarse. | | `APPROVER` | Debe aprobar antes de que los `SIGNER` con `signingOrder` mayor puedan firmar. | | `VIEWER` | Puede ver el documento, no realiza ninguna acción. | | `CC` | Recibe una copia del documento completado, no participa del proceso. | | `ASSISTANT` | Puede completar campos en nombre de otro destinatario. | ## Crear destinatarios [Sección titulada «Crear destinatarios»](#crear-destinatarios) ```http POST /envelope/recipient/create-many ``` Cada destinatario acepta `email`, `name`, `role`, y opcionalmente `signingOrder`, `accessAuth` y `actionAuth`. Ver la especificación completa en la [Referencia de la API](/api/operations/envelope-recipient-createmany/). ### Orden de firma [Sección titulada «Orden de firma»](#orden-de-firma) Con firma secuencial, los destinatarios con `signingOrder` más bajo firman primero; los que comparten el mismo valor pueden firmar en simultáneo. Para habilitar firma secuencial, define `signingOrder: "SEQUENTIAL"` en el `meta` del envelope — ver [Crear el envelope](/guides/integration-guide/#1-crear-el-envelope-y-los-firmantes). ### Autenticación de destinatarios [Sección titulada «Autenticación de destinatarios»](#autenticación-de-destinatarios) Para reforzar la seguridad de un destinatario en particular, más allá de la autenticación general de la API: | `accessAuth` (para ver el documento) | Descripción | | ------------------------------------ | ------------------------------------------- | | `ACCOUNT` | El destinatario debe tener sesión iniciada. | | `TWO_FACTOR_AUTH` | El destinatario debe verificar con 2FA. | | `actionAuth` (para firmar) | Descripción | | -------------------------- | ----------------------------------------------------------------------------------------------------------------- | | `ACCOUNT` | El destinatario debe tener sesión iniciada. | | `PASSKEY` | Requiere autenticación con passkey. | | `TWO_FACTOR_AUTH` | Requiere código 2FA. | | `PASSWORD` | Requiere verificación con contraseña. | | `EXPLICIT_NONE` | Desactiva explícitamente la autenticación de acción. | | `CLAVE_UNICA` | Requiere verificar la identidad del destinatario con ClaveÚnica. | | `FAO_HASH` | Verificación asociada a Firma Electrónica Avanzada — ver [Firma Electrónica Avanzada](/guides/fea-orquestacion/). | RUT y Firma Electrónica Avanzada Para destinatarios en un envelope con FEA, se agrega la propiedad `rut` (identidad a verificar contra la entidad certificadora) y el orden de firma pasa a ser obligatoriamente secuencial. Ver el capítulo de [Firma Electrónica Avanzada](/guides/fea-orquestacion/#2-crear-el-envelope-y-los-firmantes). ## Actualizar destinatarios [Sección titulada «Actualizar destinatarios»](#actualizar-destinatarios) ```http POST /envelope/recipient/update-many ``` Solo disponible mientras el envelope no está `COMPLETED`. Acepta los mismos campos que la creación, todos opcionales salvo el `id` del destinatario a modificar. Ver la especificación completa en la [Referencia de la API](/api/operations/envelope-recipient-updatemany/). ## Eliminar un destinatario [Sección titulada «Eliminar un destinatario»](#eliminar-un-destinatario) ```http POST /envelope/recipient/delete ``` Ver la especificación completa en la [Referencia de la API](/api/operations/envelope-recipient-delete/). ## Rechazar en nombre de un destinatario [Sección titulada «Rechazar en nombre de un destinatario»](#rechazar-en-nombre-de-un-destinatario) ```http POST /envelope/recipient/{recipientId}/reject ``` Marca a un destinatario como `REJECTED` sin pasar por el flujo normal de firma — por ejemplo, si te informan el rechazo por un canal fuera de Legaldoc y necesitas reflejarlo en el envelope. Ver la especificación completa en la [Referencia de la API](/api/operations/envelope-recipient-rejectonbehalfof/). ## Consultar un destinatario [Sección titulada «Consultar un destinatario»](#consultar-un-destinatario) ```http GET /envelope/recipient/{recipientId} ``` Devuelve el objeto completo, incluyendo `signingStatus` (`NOT_SIGNED`, `SIGNED`, `REJECTED`), `readStatus` (`NOT_OPENED`, `OPENED`) y `sendStatus` (`NOT_SENT`, `SENT`). Ver la especificación completa en la [Referencia de la API](/api/operations/envelope-recipient-get/). *** ## Ver también [Sección titulada «Ver también»](#ver-también) * [Campos](/resources/fields/) — asignar campos a cada destinatario * [Guía de Integración](/guides/integration-guide/) — flujo completo de creación y firma * [Firma Electrónica Avanzada](/guides/fea-orquestacion/) — RUT, orden secuencial y validación de identidad # Plantillas > Crear envelopes reutilizables y generar documentos a partir de ellos. En Legaldoc, una plantilla no es un recurso aparte: es un envelope con `type: "TEMPLATE"`. Se crea, se le agregan destinatarios y campos, y se distribuye igual que cualquier envelope (ver [Guía de Integración](/guides/integration-guide/)) — la diferencia es que un envelope tipo `TEMPLATE` no se firma directamente, sino que se usa como base para generar documentos reales. ## Crear un documento a partir de una plantilla [Sección titulada «Crear un documento a partir de una plantilla»](#crear-un-documento-a-partir-de-una-plantilla) ```http POST /envelope/use ``` Toma el `envelopeId` de una plantilla y crea un nuevo envelope tipo `DOCUMENT` a partir de ella. | Campo | Tipo | Requerido | Descripción | | -------------------- | ------- | --------- | -------------------------------------------------------------------------------------- | | `envelopeId` | string | Sí | ID de la plantilla a usar. | | `recipients` | array | Sí | Destinatarios reales, mapeados por `id` a los destinatarios definidos en la plantilla. | | `distributeDocument` | boolean | No | Si es `true`, distribuye el documento de inmediato tras crearlo. | | `externalId` | string | No | Identificador propio para el documento generado. | | `folderId` | string | No | Carpeta donde crear el documento. | | `customDocumentData` | array | No | Reemplaza uno o más PDF de la plantilla por archivos nuevos (ver abajo). | | `prefillFields` | array | No | Precarga valores en campos de la plantilla (ver abajo). | Cada destinatario en `recipients` requiere `id` (el ID del destinatario en la plantilla) y `email`; `name` y `signingOrder` son opcionales y sobrescriben lo definido en la plantilla. Ver la especificación completa en la [Referencia de la API](/api/operations/envelope-use/). ### Reemplazar el PDF de la plantilla [Sección titulada «Reemplazar el PDF de la plantilla»](#reemplazar-el-pdf-de-la-plantilla) Si necesitas generar el PDF dinámicamente pero reutilizar los destinatarios y campos ya configurados en la plantilla, usa `customDocumentData` para mapear cada archivo de la plantilla (por su `envelopeItemId`) a un archivo nuevo: ```jsonc { "envelopeId": "envelope_plantilla_123", "recipients": [{ "id": 1, "email": "firmante@ejemplo.cl", "name": "Nombre Real" }], "customDocumentData": [ { "identifier": "envelope_item_xyz", "envelopeItemId": "envelope_item_xyz" } ] } ``` El PDF de reemplazo debe mantener el mismo diseño de página que el original — los campos de la plantilla se posicionan según las coordenadas originales, y un cambio de layout los desalinea. ### Precargar valores en los campos [Sección titulada «Precargar valores en los campos»](#precargar-valores-en-los-campos) `prefillFields` permite completar campos de la plantilla antes de distribuir — útil cuando ya conoces el dato (por ejemplo, tomándolo de tu propio sistema) y no quieres pedírselo al firmante: ```jsonc { "envelopeId": "envelope_plantilla_123", "recipients": [{ "id": 1, "email": "firmante@ejemplo.cl" }], "prefillFields": [ { "id": 101, "type": "text", "value": "Ingeniero de Software Senior" }, { "id": 102, "type": "number", "value": "1200000" }, { "id": 103, "type": "checkbox", "value": ["salud", "seguro-vida"] } ] } ``` El `type` de cada `prefillField` debe coincidir con el tipo real del campo en la plantilla. *** ## Ver también [Sección titulada «Ver también»](#ver-también) * [Envelopes](/resources/envelopes/) — listar plantillas filtrando por `type=TEMPLATE` * [Campos](/resources/fields/) — tipos de campo y sus opciones * [Guía de Integración](/guides/integration-guide/) — flujo general de creación y firma