# Introducción (/docs) ## ¿Qué es Sifende? [#qué-es-sifende] Sifende es una plataforma que permite a empresas o personas emitir, gestionar y consultar documentos electrónicos (DEs) a través del sistema SIFEN de la DNIT, sin tener que implementar la infraestructura de integración por su cuenta. Con Sifende podés: * Emitir Facturas Electrónicas (FE), Notas de Crédito (NCE) y Notas de Débito (NDE) * Consultar el estado de procesamiento en tiempo real * Descargar KuDE en PDF y XML firmado * Enviar comprobates a tus clientes por email * Gestionar cancelaciones e inutilizaciones de numeración ## ¿Por dónde empezar? [#por-dónde-empezar] ## Para LLMs y agentes [#para-llms-y-agentes] Esta documentación está disponible en formatos optimizados para contexto: | Recurso | URL | Contenido | | ------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Índice (`llms.txt`) | [`/llms.txt`](/llms.txt) | Lista de todas las páginas con título y descripción; sirve como punto de entrada | | Contenido completo | [`/llms-full.txt`](/llms-full.txt) | Todas las páginas concatenadas en texto plano para contexto total | | Página individual | `/llms.mdx/docs/[slug]` | Cualquier página como Markdown limpio, p.ej. [`/llms.mdx/docs/referencia/documentos-electronicos/emitir`](/llms.mdx/docs/referencia/documentos-electronicos/emitir) | # Ambientes (/docs/conceptos/ambientes) Sifende tiene dos ambientes separados: **pruebas** (QA / sandbox) y **producción**. El contrato de la API es el mismo en los dos. Lo que cambia es a qué endpoint de SIFEN se conectan, qué credenciales usan, y si los documentos emitidos tienen efecto fiscal o no. ## Comparación rápida [#comparación-rápida] | Aspecto | Pruebas | Producción | | ------------------- | --------------------------------------- | --------------------------------------- | | Endpoint Sifende | `https://api.sifende.com.py/api/v1/...` | `https://api.sifende.com.py/api/v1/...` | | Endpoint SIFEN | e-kuatia test (DNIT) | e-kuatia prod (DNIT) | | Prefijo API key | `sk_test_...` | `sk_live_...` | | Certificado | De pruebas (provisto por SET) | Real, emitido por una AC autorizada | | Timbrado | De pruebas | Asignado por la SET para producción | | Validez legal | ❌ No tiene efecto fiscal | ✅ Documentos legalmente válidos | | Visible en e-kuatia | Solo en e-kuatia test | Sí (consulta pública con CDC) | El contrato de la API es idéntico. Cuando estés listo para ir a producción no hace falta tocar el código: solo cambian las credenciales (API key y timbrado). ## Ambiente de pruebas (QA) [#ambiente-de-pruebas-qa] Es donde validás tu integración antes de emitir documentos reales. ### Para qué se usa [#para-qué-se-usa] * Validar tu integración: emitir FE, NCE, NDE de prueba sin consecuencias fiscales. * Probar casos límite como receptores B2B, B2C, innominados, con descuentos o multi-IVA. * Reproducir errores. Los rechazos de SIFEN test usan los mismos códigos que producción. * Hacer demos a clientes mostrando el flujo sin emitir documentos reales. ### Credenciales de pruebas [#credenciales-de-pruebas] * Certificado de pruebas: la SET provee un P12 genérico para el ambiente de testing. * Timbrado de pruebas: la SET también asigna un timbrado específico para QA. * API key: en el panel de Sifende, generá una clave con prefijo `sk_test_` desde el modo "Pruebas". ### Limitaciones del ambiente de pruebas [#limitaciones-del-ambiente-de-pruebas] * El endpoint de SIFEN test puede tener mayor latencia y menor disponibilidad que producción. * Los documentos emitidos en QA no aparecen en e-kuatia productivo y no tienen validez legal. * Los CDC generados en QA no sirven en producción, porque los timbrados son distintos. ## Ambiente de producción [#ambiente-de-producción] El ambiente real, con efecto fiscal. ### Para qué se usa [#para-qué-se-usa-1] * Emitir documentos electrónicos legalmente válidos. * Operar tu negocio facturando ante DNIT. * Cancelar o inutilizar documentos productivos. ### Requisitos [#requisitos] * Certificado digital P12 real, emitido por una autoridad certificadora autorizada en Paraguay (DOCUMENTA, eFirma, e-Forma, ID-Token, etc.). * Timbrado electrónico vigente asignado por la SET al RUC del contribuyente. * Habilitación como facturador electrónico (CSC asignado por e-kuatia). * API key con prefijo `sk_live_`. ## Cómo cambiar de ambiente [#cómo-cambiar-de-ambiente] El cambio de QA a producción es de configuración, no de código: Configurá el contribuyente en producción desde el panel: subí el certificado real, cargá el timbrado productivo y registrá el CSC asignado por e-kuatia. Generá una API key productiva (`sk_live_...`) en el panel. Reemplazá la variable de entorno en tu sistema: ```bash # Antes (pruebas) SIFENDE_API_KEY=sk_test_abc123... # Después (producción) SIFENDE_API_KEY=sk_live_xyz789... ``` Antes de migrar todo el flujo, probá con un documento de bajo monto para validar que toda la cadena funciona. La URL base es la misma en los dos ambientes (`api.sifende.com.py`). Sifende detecta el ambiente por el prefijo de la API key (`sk_test_` vs `sk_live_`) y rutea al endpoint correcto de SIFEN. ## Buenas prácticas de separación [#buenas-prácticas-de-separación] * Variables de entorno distintas por ambiente: `SIFENDE_API_KEY_TEST` y `SIFENDE_API_KEY_PROD`. * Bases de datos separadas para CDC de QA y producción. No mezcles los identificadores. * Banderas visuales en tu sistema: poné un banner amarillo "AMBIENTE DE PRUEBAS" si el API key empieza con `sk_test_`. * Nunca uses claves de producción en desarrollo. Si lo hacés, vas a emitir documentos reales con datos de prueba. ## Próximos pasos [#próximos-pasos] * [Inicio Rápido — Requisitos Previos](/docs/inicio-rapido/requisitos-previos): cómo conseguir las credenciales. * [Ir a Producción](/docs/guias/ir-a-produccion): checklist de go-live. * [Autenticación](/docs/conceptos/autenticacion): más sobre las API keys. # Autenticación (/docs/conceptos/autenticacion) Sifende soporta dos modos de autenticación según el caso de uso. Para integraciones backend (ERP, e-commerce, scripts) usás **API key**. Para construir un frontend propio sobre la plataforma, usás **Keycloak JWT**. ## API Key (integraciones máquina a máquina) [#api-key-integraciones-máquina-a-máquina] Es el modo principal para sistemas que emiten documentos automáticamente desde un backend. ### Formato [#formato] ``` Authorization: Bearer sk_live_abc123xyz... ``` El header es `Authorization: Bearer`, no `X-Api-Key`. El valor incluye el prefijo (`sk_live_` para producción o `sk_test_` para pruebas). ### Ejemplo [#ejemplo] ```bash curl -X POST https://api.sifende.com.py/api/v1/documento-electronico \ -H "Authorization: Bearer sk_live_abc123xyz..." \ -H "Content-Type: application/json" \ -d '{ "tipoDocumento": "FACTURA_ELECTRONICA", ... }' ``` ### Cuándo usar API key [#cuándo-usar-api-key] * Tu ERP emite facturas automáticamente desde el backend. * Un script periódico que sincroniza ventas y emite NCE. * Pruebas con `curl`, Postman o un cliente HTTP propio. * Integración mobile que delega la emisión a tu backend. ### Ciclo de vida del API key [#ciclo-de-vida-del-api-key] | Operación | Descripción | | --------- | ------------------------------------------------------------------------------------------------------------- | | Crear | Desde el panel: Configuración → API Keys → Nueva clave. La clave completa solo se muestra una vez. | | Rotar | Generás una clave nueva sin invalidar la anterior; cuando tu sistema esté usando la nueva, eliminás la vieja. | | Eliminar | Revoca la clave inmediatamente; cualquier request con esa clave pasa a recibir `401`. | Cada API key está vinculada a un solo contribuyente. Si tenés varios contribuyentes, generá una clave por cada uno. ### Buenas prácticas de seguridad [#buenas-prácticas-de-seguridad] * Nunca hardcodees la clave en el código fuente. Usá variables de entorno o un secret manager (AWS Secrets Manager, Vault, GCP Secret Manager). * Nunca la expongas en código frontend, repos públicos o logs. * Rotala cada 90 días o por ahí. No hace falta más, pero tampoco menos. * Si sospechás que se filtró, revocala en el momento. * Usá `sk_test_` en desarrollo y `sk_live_` solo en producción, para no emitir documentos reales por error. ## Keycloak JWT (frontend personalizado) [#keycloak-jwt-frontend-personalizado] Para aplicaciones que construyen una UI propia sobre Sifende usando OAuth 2.0 / OpenID Connect. Si tu integración es desde un backend, no necesitás esto. Usá API key. ### Flujo [#flujo] El usuario se autentica vía Keycloak (login con email + contraseña, o IDP social). Recibe un JWT `access_token` con duración limitada. El frontend lo envía en cada request: `Authorization: Bearer {jwt}`. El backend de Sifende valida la firma del JWT y extrae el ID del usuario. ### Endpoints de sesión [#endpoints-de-sesión] Los endpoints autenticados con JWT viven bajo `/api/v1/contribuyentes/:id/...` y verifican que el usuario autenticado sea propietario del contribuyente. ```bash curl https://api.sifende.com.py/api/v1/contribuyentes/123/lotes \ -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." ``` ## ¿Cuál usar? [#cuál-usar] | Caso de uso | Auth recomendada | | -------------------------------- | ---------------- | | Integración ERP | API key | | Script backend de sincronización | API key | | App mobile (vía backend propio) | API key | | Frontend web propio | Keycloak JWT | | Pruebas con `curl` o Postman | API key | ## Errores de autenticación [#errores-de-autenticación] | Status | Descripción | | ------------------ | ------------------------------------------------------------------------------------------------------------ | | `401 Unauthorized` | Clave ausente, inválida o revocada | | `403 Forbidden` | Clave válida pero sin acceso al recurso (por ejemplo, intentás operar sobre un contribuyente que no es tuyo) | Para más detalles, ver [Referencia: Autenticación](/docs/referencia/autenticacion) y la guía de [API Keys](/docs/referencia/api-keys). # CDC (Código de Control) (/docs/conceptos/cdc) El **CDC** (Código de Control) es el identificador único de un documento electrónico ante SIFEN. Tiene 44 caracteres numéricos y lleva toda la información esencial del documento codificada en su estructura. Es el dato más importante a guardar después de emitir. ## Qué es el CDC [#qué-es-el-cdc] ``` 01800123451001001000000122026042710000000006 ``` Cada documento electrónico tiene un único CDC, irrepetible. Lo genera Sifende al emitir y SIFEN lo valida. Es el código que aparece en el QR del KuDE y permite consultar el documento en e-kuatia. ## Estructura del CDC (44 dígitos) [#estructura-del-cdc-44-dígitos] El CDC se arma concatenando 11 campos en este orden: | Pos. | Largo | Campo | Descripción | | ----- | ----- | ---------------------------------- | --------------------------------------------------------- | | 1–2 | 2 | Tipo de documento (`iTiDE`) | `01` = FE, `04` = AFE, `05` = NCE, `06` = NDE, `07` = NRE | | 3–10 | 8 | RUC del emisor | Sin DV, padded con ceros a izquierda — ej. `80012345` | | 11 | 1 | DV del emisor | Dígito verificador del RUC del emisor | | 12–14 | 3 | Establecimiento | Número de sucursal (`001`–`999`) | | 15–17 | 3 | Punto de expedición | Caja/terminal dentro del establecimiento | | 18–24 | 7 | Número del documento | Auto-incremental por establecimiento+punto | | 25 | 1 | Tipo de contribuyente (`iTipCont`) | `1` = persona física, `2` = persona jurídica | | 26–33 | 8 | Fecha de emisión | `AAAAMMDD` — ej. `20260427` | | 34 | 1 | Tipo de emisión | `1` = normal, `2` = contingencia | | 35–43 | 9 | Código de seguridad | Aleatorio, 9 dígitos, generado por Sifende | | 44 | 1 | Dígito verificador del CDC | Calculado con módulo 11 sobre los 43 dígitos previos | Total: 2 + 8 + 1 + 3 + 3 + 7 + 1 + 8 + 1 + 9 + 1 = 44 caracteres. El campo `iTipCont` (posición 25, tipo de contribuyente del emisor) es parte del CDC y no hay que confundirlo con `numeroTimbrado`, que no aparece en el CDC. El timbrado se referencia indirectamente a través de la combinación `establecimiento + puntoExpedicion + numeroDocumento`. ## Ejemplo desglosado [#ejemplo-desglosado] Tomando el CDC `01800123451001001000000122026042710000000006`: ``` 01 80012345 1 001 001 0000001 2 20260427 1 000000000 6 │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └─ DV módulo 11 del CDC │ │ │ │ │ │ │ │ │ └─ Código de seguridad (9 dígitos) │ │ │ │ │ │ │ │ └─ Tipo emisión: 1 = normal │ │ │ │ │ │ │ └─ Fecha 2026-04-27 │ │ │ │ │ │ └─ Tipo contribuyente: 2 = persona jurídica │ │ │ │ │ └─ Documento N° 0000001 │ │ │ │ └─ Punto expedición 001 │ │ │ └─ Establecimiento 001 │ │ └─ DV del RUC emisor (1) │ └─ RUC del emisor (sin DV) — 80012345 └─ Tipo: 01 = Factura Electrónica ``` ## Representación visual en el KuDE [#representación-visual-en-el-kude] En la representación gráfica del documento, el CDC se muestra en grupos de 4 dígitos para que sea más fácil de leer: ``` 0180 0123 4510 0100 1000 0001 2202 6042 7100 0000 0006 ``` ## Cómo se genera [#cómo-se-genera] Sifende genera el CDC al recibir el request de emisión, en este orden: Determina el `iTiDE` según el `tipoDocumento` enviado. Recupera RUC y DV del emisor desde la configuración del contribuyente. Asigna el siguiente número disponible del timbrado para el establecimiento+punto solicitados. Determina el `iTipCont` según el tipo de contribuyente del emisor (persona física o jurídica). Genera un código de seguridad aleatorio de 9 dígitos. Calcula el dígito verificador con módulo 11 sobre los 43 dígitos previos. Concatena todo y lo asigna al documento como `id` y atributo de la firma XML. ## Cómo usar el CDC [#cómo-usar-el-cdc] El CDC es la clave primaria de tu documento ante SIFEN. Lo necesitás para: * Consultar estado: `GET /api/v1/documento-electronico/status/:cdc` * Descargar el KuDE: `GET /api/v1/documento-electronico/:cdc/kude` * Cancelar el documento: `POST /api/v1/documento-electronico/:cdc/cancelar` * Asociarlo en una NCE/NDE: el campo `documentoAsociado.cdc` referencia la FE original. * Consulta pública en e-kuatia: cualquier persona con el CDC puede verificar el documento en `https://ekuatia.set.gov.py/consultas/`. Guardá el CDC en tu base de datos apenas Sifende lo retorne. Es el único dato que te permite operar sobre el documento después. Sin CDC no podés consultar, cancelar ni descargar el KuDE. ## Inmutabilidad [#inmutabilidad] Una vez generado, el CDC no se puede modificar. Si SIFEN rechaza el documento y querés re-emitirlo: * Si los cambios no tocan ninguno de los 11 campos del CDC (RUC, número, fecha, etc.), SIFEN permite reutilizar el mismo CDC. * Si los cambios afectan algún campo del CDC (por ejemplo, cambia el número o la fecha), tenés que emitir un documento nuevo con CDC nuevo. En la práctica, Sifende siempre genera un CDC nuevo ante un POST nuevo. Los reintentos manuales del mismo payload terminan creando documentos diferentes con CDC diferentes. ## Próximos pasos [#próximos-pasos] * [Convenciones — CDC](/docs/referencia/convenciones): el formato exacto en la API. * [Timbrado y Numeración](/docs/conceptos/timbrado-numeracion): qué partes del CDC dependen del timbrado. * [Documentos Asociados](/docs/referencia/modelos/documento-asociado): cómo referenciar un CDC en NCE/NDE. # Ciclo de Vida de un Documento (/docs/conceptos/ciclo-de-vida) Cuando emitís un documento electrónico no se aprueba al instante. Pasa por varios estados mientras Sifende lo construye, lo firma, lo asocia a un lote, lo envía a SIFEN y consulta el resultado. Entender estos estados es clave para integrar bien. ## Estados del documento [#estados-del-documento] | Estado | Descripción | | ------------- | --------------------------------------------------------------------------------------------------------------------- | | `PENDIENTE` | Sifende recibió el request y armó el DE; aún no fue asociado a un lote | | `EN_LOTE` | El DE está dentro de un lote `PREPARADO` o `INTENTANDO` esperando ser transmitido a SIFEN | | `ENVIADO` | El lote ya se transmitió a SIFEN; Sifende está consultando el resultado por `siResultLoteDE` | | `APROBADO` | SIFEN validó el documento. Es legalmente válido y aparece en e-kuatia | | `RECHAZADO` | SIFEN rechazó el documento por error de validación (terminal, no se reintenta) | | `ERROR` | Falló el procesamiento interno (firma, transmisión, conectividad). Sifende reintenta solo | | `CANCELADO` | Documento previamente `APROBADO` que fue anulado por evento de cancelación posterior (terminal) | | `DESCONOCIDO` | Estado de borde: el lote agotó reintentos sin respuesta concluyente de SIFEN (terminal, requiere intervención manual) | ## Diagrama de transiciones [#diagrama-de-transiciones] ``` ┌─────────────┐ POST DE ────► │ PENDIENTE │ └──────┬──────┘ │ Worker asocia el DE a un lote ▼ ┌─────────────┐ │ EN_LOTE │ └──────┬──────┘ │ El lote se firma y se transmite │ a SIFEN (siRecepLoteDE) ▼ ┌─────────────┐ │ ENVIADO │ └──────┬──────┘ │ Scheduler consulta resultado │ del lote (siResultLoteDE, cada 60s) ▼ ┌──────────┼──────────┐ ▼ ▼ ▼ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ APROBADO │ │ RECHAZADO │ │ ERROR │ └─────┬─────┘ └───────────┘ └─────┬─────┘ │ (terminal) │ Reintento automático │ Evento de │ del lote (vuelve │ cancelación │ a EN_LOTE/ENVIADO); │ dentro del plazo │ si agota reintentos: ▼ ▼ ┌───────────┐ ┌──────────────┐ │ CANCELADO │ │ DESCONOCIDO │ └───────────┘ └──────────────┘ (terminal) (terminal) ``` ## Tiempos típicos [#tiempos-típicos] * PENDIENTE → EN\_LOTE: pocos segundos. El worker agrupa el DE en un lote casi al toque. * EN\_LOTE → ENVIADO: pocos segundos más, una vez que el lote se firma y SIFEN responde con el `idLote`. * ENVIADO → APROBADO/RECHAZADO: 1 a 5 minutos en condiciones normales. En horarios pico puede llegar a 1–24 horas (SIFEN no garantiza tiempos de respuesta). * ERROR: si la falla es transitoria (red, timeout SIFEN), el lote se reintenta con backoff exponencial. Eventualmente termina en `APROBADO`, `RECHAZADO` o `DESCONOCIDO`. Sifende retorna el CDC al instante cuando recibe el POST, incluso en estado `PENDIENTE`. No hace falta esperar la aprobación para guardarlo y mostrarlo al usuario. ## Cómo consultar el estado [#cómo-consultar-el-estado] Tres maneras: Polling activo: consultá `GET /api/v1/documento-electronico/status/:cdc` cada 30–60 segundos hasta obtener `APROBADO`, `RECHAZADO` o `ERROR`. Webhook (próximamente): Sifende notifica a tu URL cuando cambia el estado. Panel web: la lista de documentos muestra el estado actualizado en tiempo real. Ver [Guía: Consultar Estado](/docs/guias/consultar-estado) para la estrategia de polling recomendada. ## Qué hacer en cada estado [#qué-hacer-en-cada-estado] ### `PENDIENTE`, `EN_LOTE` o `ENVIADO` [#pendiente-en_lote-o-enviado] * Mostrale al usuario "Procesando…". * Guardá el CDC en tu base de datos. * Esperá la confirmación antes de imprimir o entregar el KuDE al cliente. ### `APROBADO` [#aprobado] * El documento es legalmente válido. * Podés [descargar el KuDE](/docs/referencia/documentos-electronicos/descargar-kude) (PDF para el cliente). * Aparece en e-kuatia y el receptor lo puede consultar. ### `RECHAZADO` [#rechazado] * El documento es inválido y SIFEN no lo aceptó. No se puede recuperar. * Sifende devuelve los códigos de error de SIFEN. Ver [Rechazos SIFEN](/docs/solucion-problemas/rechazos-sifen). * Corregí los datos y emití un documento nuevo (con número y CDC nuevos). ### `ERROR` [#error] * Distinto de `RECHAZADO`: indica un problema interno (firma, transmisión, conectividad), no una validación fallida de SIFEN. * Sifende reintenta solo los lotes que terminan en `ERROR`. No hace falta reemitir el documento. * Si después de varios reintentos el lote no avanza, contactá a soporte. ### `CANCELADO` [#cancelado] * El documento fue anulado por un evento posterior. * Estado terminal. No se puede revertir. * Para emitir uno en su reemplazo, generá un documento nuevo (con número nuevo). ### `DESCONOCIDO` [#desconocido] * Estado de borde: el lote agotó los reintentos sin respuesta concluyente de SIFEN (por ejemplo, SIFEN devolvió `0364 — consulta extemporánea` después de 48h). * Requiere intervención manual: hay que consultar el documento individualmente por CDC y, si aparece en SIFEN, reconciliar el estado. Un documento en `RECHAZADO` no puede pasar a `APROBADO`. Si SIFEN lo rechazó, hay que emitir uno nuevo con los datos corregidos. ## Cancelación posterior [#cancelación-posterior] Un documento `APROBADO` puede pasar a `CANCELADO` si emitís un evento de cancelación dentro del plazo permitido por SIFEN (típicamente 48 horas). Ver [Eventos SIFEN](/docs/conceptos/eventos-sifen). ## Estados del lote vs estados del documento [#estados-del-lote-vs-estados-del-documento] El documento vive dentro de un lote, que también tiene su propio ciclo. Mientras el lote está `PREPARADO` o `INTENTANDO`, los DE adentro están en `EN_LOTE`. Cuando el lote pasa a `ENVIADO`, los DE pasan a `ENVIADO`. Cuando el lote se procesa (`PROCESADO`), cada DE recibe individualmente su veredicto: `APROBADO` o `RECHAZADO`. Ver [Lotes](/docs/conceptos/lotes). ## Próximos pasos [#próximos-pasos] * [Lotes](/docs/conceptos/lotes): cómo se agrupan los documentos para SIFEN. * [Consultar Estado](/docs/guias/consultar-estado): estrategia de polling. * [Manejar Errores](/docs/guias/manejar-errores): qué hacer cuando algo falla. # Cómo Funciona Sifende (/docs/conceptos/como-funciona) Sifende actúa como una capa de integración entre tu sistema (ERP, e-commerce, software propio) y SIFEN, el Sistema Integrado de Facturación Electrónica Nacional administrado por la DNIT. Tu sistema solo habla JSON con Sifende. Toda la complejidad de SOAP, XML, XSD, firma digital XAdES y reintentos queda absorbida del lado nuestro. ## Arquitectura general [#arquitectura-general] ## Flujo de una factura electrónica [#flujo-de-una-factura-electrónica] Tu sistema envía un POST con JSON al endpoint de Sifende con los datos del documento (receptor, ítems, condición de pago). Sifende valida el payload contra las reglas de SIFEN: RUC del receptor, formato de campos, totales, IVA, timbrado vigente, numeración disponible. Sifende construye el XML según el Manual Técnico V150 y todas las Notas Técnicas vigentes. Calcula el CDC (Código de Control) de 44 caracteres y se lo asigna al documento. Sifende firma el XML con tu certificado P12 (que se guarda de forma segura desde la configuración inicial). La firma usa XAdES-BES, como exige SIFEN. Sifende empaqueta el documento en un lote y lo transmite a SIFEN vía SOAP (`siRecepLoteDE`). SIFEN responde al toque con un identificador de lote. Sifende retorna el CDC a tu sistema. La respuesta es síncrona: en cuestión de segundos recibís el código que identifica al documento. Sifende consulta los resultados (`siResultLoteDE`) cada 60 segundos hasta obtener el veredicto de SIFEN. El documento pasa a estado `APROBADO` o `RECHAZADO`. Tu sistema consulta el estado vía `GET /api/v1/documento-electronico/status/:cdc` o recibe la confirmación por webhook (próximamente). ## ¿Qué hace Sifende por vos? [#qué-hace-sifende-por-vos] | Responsabilidad | Sifende | Tu sistema | | ----------------------------------------- | ------- | ---------- | | Validar campos de SIFEN | ✅ | — | | Generar XML según Manual Técnico V150 | ✅ | — | | Calcular CDC y dígito verificador | ✅ | — | | Firmar con certificado digital P12 | ✅ | — | | Comunicación SOAP con e-kuatia | ✅ | — | | Reintentos y backoff ante caídas de SIFEN | ✅ | — | | Consulta periódica de resultados | ✅ | — | | Generar el KuDE (representación gráfica) | ✅ | — | | Enviar el JSON con los datos comerciales | — | ✅ | | Guardar el CDC retornado | — | ✅ | La respuesta inmediata de Sifende contiene el CDC. El estado final `APROBADO` o `RECHAZADO` llega minutos después, cuando SIFEN procesa el lote. Ver [Ciclo de Vida](/docs/conceptos/ciclo-de-vida). ## Componentes internos [#componentes-internos] * API REST: recibe los requests JSON de tu sistema (autenticada con [API key](/docs/conceptos/autenticacion)). * Motor de generación de DE: construye el XML respetando el formato exigido por SIFEN. * Worker asíncrono: envía lotes y firma documentos en background. * Scheduler de consulta: corre cada 60 segundos para obtener resultados de lotes pendientes. * Panel web: para gestionar contribuyentes, certificados, timbrados y monitorear documentos. ## Próximos pasos [#próximos-pasos] * [Autenticación](/docs/conceptos/autenticacion): cómo identificarte ante la API. * [Documentos Electrónicos](/docs/conceptos/documentos-electronicos): qué tipos podés emitir. * [Ciclo de Vida](/docs/conceptos/ciclo-de-vida): qué pasa después de emitir. # Contribuyente (/docs/conceptos/contribuyente) Un **contribuyente** es la persona física o jurídica registrada ante la SET (Secretaría de Estado de Tributación) y la DNIT como facturador electrónico. Es la entidad que emite los documentos: la "razón social" que aparece en cada factura. En Sifende, todo documento electrónico se emite a nombre de un contribuyente. Sin un contribuyente configurado no se pueden emitir documentos. ## Datos básicos [#datos-básicos] Cada contribuyente en Sifende guarda estos datos: | Campo | Descripción | | ------------------------------------ | --------------------------------------- | | `ruc` | Número de RUC sin DV — ej. `"80012345"` | | `digitoVerificador` | Dígito verificador del RUC — ej. `"1"` | | `razonSocial` | Nombre o razón social registrado en SET | | `nombreFantasia` | Nombre comercial (opcional) | | `actividadEconomica` | Código CIIU principal (ej. `47190`) | | `email` | Email de contacto | | `direccion` | Dirección fiscal del contribuyente | | `departamento`, `distrito`, `ciudad` | Geografía paraguaya | El RUC se valida contra el padrón de SIFEN al crear el contribuyente. Si no existe o el dígito verificador no coincide, la creación falla con `404 ruc-not-found`. ## Componentes de un contribuyente [#componentes-de-un-contribuyente] Más allá de los datos básicos, un contribuyente necesita tres elementos para emitir documentos: ### 1. Certificado digital P12 [#1-certificado-digital-p12] El certificado digital es la identidad criptográfica del contribuyente ante SIFEN. Es un archivo `.p12` (PKCS#12) emitido por una autoridad certificadora habilitada en Paraguay (DOCUMENTA, eFirma, e-Forma, ID-Token, etc.). Sifende guarda el certificado cifrado y lo usa automáticamente para firmar cada documento. Vos solo lo subís una vez desde el panel. ``` Contribuyente └── Certificado P12 (cifrado en reposo) └── Usado por Sifende para firmar cada DE ``` Cuando el certificado está por vencer, recibís una notificación. Ver [Certificado Digital](/docs/solucion-problemas/certificado-digital) para detalles operativos. ### 2. CSC (Código de Seguridad del Contribuyente) [#2-csc-código-de-seguridad-del-contribuyente] El CSC es un código que genera e-kuatia (la plataforma oficial de la DNIT) y se usa para construir la URL del QR del KuDE. Cada contribuyente tiene su CSC, asignado al habilitarse como facturador electrónico. El CSC no es secreto en el sentido criptográfico (viaja en el QR del KuDE), pero hace falta para que SIFEN valide los documentos. ### 3. Timbrado [#3-timbrado] El timbrado electrónico es la autorización numérica de la SET para emitir documentos. Cada timbrado tiene una validez (fechaInicio, fechaFin) y una numeración asignada por establecimiento. Ver [Timbrado y Numeración](/docs/conceptos/timbrado-numeracion). ## Una cuenta, varios contribuyentes [#una-cuenta-varios-contribuyentes] Una sola cuenta de Sifende puede gestionar varios contribuyentes. Sirve para casos como: * Estudios contables que facturan a nombre de varios clientes. * Holdings con varias razones sociales bajo el mismo grupo. * Desarrolladores que integran Sifende para varias empresas. Cada contribuyente tiene su propio certificado, CSC, timbrado y API key. Son ambientes lógicamente independientes dentro de la misma cuenta. ``` Cuenta Sifende (usuario Keycloak) ├── Contribuyente A (RUC 80012345-1) │ ├── Certificado A.p12 │ ├── Timbrado 12345678 │ └── API key sk_live_aaa... ├── Contribuyente B (RUC 80098765-3) │ ├── Certificado B.p12 │ ├── Timbrado 87654321 │ └── API key sk_live_bbb... └── Contribuyente C ... ``` ## Crear un contribuyente [#crear-un-contribuyente] Desde el panel: Andá a Contribuyentes → Crear. Cargá los datos básicos (RUC, razón social, dirección, actividad económica). Subí el certificado P12 y su contraseña. Configurá el timbrado (número, fechas, establecimientos, puntos de expedición). Generá una API key para empezar a integrar. Una vez completados estos pasos, el contribuyente queda listo para emitir documentos electrónicos. ## Próximos pasos [#próximos-pasos] * [Timbrado y Numeración](/docs/conceptos/timbrado-numeracion): cómo se numeran los documentos. * [Autenticación](/docs/conceptos/autenticacion): cómo se vincula la API key al contribuyente. * [Inicio Rápido](/docs/inicio-rapido): emitir tu primer documento. # Documentos Electrónicos (/docs/conceptos/documentos-electronicos) Un **documento electrónico (DE)** es la versión digital, firmada y registrada en SIFEN de un comprobante tributario tradicional. Reemplaza al equivalente físico (factura preimpresa, nota de crédito en papel) y tiene el mismo valor legal, con la ventaja de que la DNIT lo puede auditar en tiempo real. Cada DE en Sifende se identifica por su **CDC** (Código de Control), un identificador único de 44 caracteres que SIFEN reconoce a nivel nacional. ## Tipos de documento [#tipos-de-documento] | Tipo | Código SIFEN | Estado | | ---------------------------------- | ------------ | ---------------- | | Factura Electrónica (FE) | 1 | ✅ Disponible | | Nota de Crédito Electrónica (NCE) | 5 | ✅ Disponible | | Nota de Débito Electrónica (NDE) | 6 | ✅ Disponible | | Autofactura Electrónica (AFE) | 4 | 🚧 En desarrollo | | Nota de Remisión Electrónica (NRE) | 7 | 🚧 En desarrollo | Todos los tipos disponibles se emiten por el mismo endpoint polimórfico: `POST /api/v1/documento-electronico`. El campo `tipoDocumento` define el tipo. Ver [Emitir documento](/docs/referencia/documentos-electronicos/emitir). ## Tipos disponibles [#tipos-disponibles] ### Factura Electrónica (FE) [#factura-electrónica-fe] La FE es el documento más común. Equivale a la factura tradicional. Se emite cuando vendés un producto o servicio, y declara la operación gravada con IVA ante la DNIT. * Cuándo usarla: todas las ventas a clientes (B2B con RUC, B2C nominado o innominado). * Detalles: soporta condición de pago `CONTADO` o `CRÉDITO`, ítems con IVA al 10%, 5% o exento, descuentos por línea y globales. * Receptor: puede ser otro contribuyente con RUC, una persona física con cédula, o "Sin Nombre" (innominado, solo permitido en FE). Ver [Modelo: Factura Electrónica](/docs/referencia/modelos/factura-electronica). ### Nota de Crédito Electrónica (NCE) [#nota-de-crédito-electrónica-nce] La NCE se emite para anular o reducir una operación previamente facturada: devoluciones, descuentos posteriores o corrección de un error en la FE original. * Cuándo usarla: devoluciones de mercadería, bonificaciones aplicadas después de la venta, ajustes por errores en una FE ya aprobada. * Detalles: requiere obligatoriamente un documento asociado (la FE que está corrigiendo). El receptor no puede ser innominado: tiene que tener RUC o cédula identificada. * Motivo (`iMotEmi`): devolución, descuento, bonificación, etc. Ver [Modelo: Nota de Crédito](/docs/referencia/modelos/nota-credito). ### Nota de Débito Electrónica (NDE) [#nota-de-débito-electrónica-nde] La NDE se emite para incrementar el monto de una operación ya facturada: intereses por mora, cargos adicionales, ajustes que aumentan el importe original. * Cuándo usarla: intereses por pago tardío, recargos pactados después de la venta, ajustes al alza. * Detalles: igual que la NCE, requiere documento asociado y receptor identificado. La diferencia con la NCE es semántica: la NCE reduce el monto, la NDE lo aumenta. Ver [Modelo: Nota de Débito](/docs/referencia/modelos/nota-debito). ## Tipos en desarrollo [#tipos-en-desarrollo] ### Autofactura Electrónica (AFE) 🚧 [#autofactura-electrónica-afe-] Se emite cuando una empresa se compra a sí misma un servicio (típicamente a un proveedor que no es contribuyente del IVA, como un agricultor o servicio independiente). Pendiente de implementación en Sifende. ### Nota de Remisión Electrónica (NRE) 🚧 [#nota-de-remisión-electrónica-nre-] Documenta el traslado físico de mercadería sin transferencia de propiedad. Por ejemplo, un envío entre sucursales de la misma empresa. Pendiente de implementación en Sifende. ## Características comunes [#características-comunes] Todos los DE en Sifende comparten: * CDC único de 44 caracteres, asignado al emitir. * Firma digital XAdES-BES con el certificado del contribuyente. * Validación contra SIFEN dentro de los minutos siguientes a la emisión. * KuDE descargable (representación gráfica en PDF) una vez aprobado. * Cancelación mediante evento, dentro del plazo permitido por SIFEN (ver [Eventos SIFEN](/docs/conceptos/eventos-sifen)). ## Próximos pasos [#próximos-pasos] * [Ciclo de Vida](/docs/conceptos/ciclo-de-vida): qué pasa después de emitir. * [CDC](/docs/conceptos/cdc): el identificador único. * [Receptor](/docs/conceptos/receptor): B2B, B2C e innominado. # Eventos SIFEN (/docs/conceptos/eventos-sifen) Un **evento SIFEN** es una notificación oficial que tu sistema envía a SIFEN sobre un documento ya emitido, para informar un cambio de estado posterior. Los eventos son la única forma legal de "modificar" la realidad fiscal después de la aprobación: anular un documento ya aprobado o saltar un rango de numeración no usado. ## Qué es un evento [#qué-es-un-evento] A diferencia de un documento electrónico (que declara una operación comercial), un evento comunica un acontecimiento sobre un documento o numeración. Los eventos viajan por un web service distinto (`siRecepEvento`) y reciben su propio veredicto de SIFEN. Sifende soporta dos tipos de eventos: | Evento | Qué hace | Documento afectado | | ------------- | ----------------------------------------------- | -------------------------- | | Cancelación | Anula un DE ya aprobado | Un DE específico (por CDC) | | Inutilización | Marca un rango de numeración como no utilizable | Un rango (sin CDC) | ## Cancelación de documento [#cancelación-de-documento] Anula un documento que ya fue aprobado por SIFEN. Una vez cancelado, el documento queda registrado en SIFEN pero sin valor fiscal: como si nunca se hubiera emitido para fines tributarios. ### Cuándo usarlo [#cuándo-usarlo] * Emitiste una FE con datos incorrectos y ya está aprobada. * El cliente devolvió la mercadería antes de tomar posesión y querés anular en lugar de emitir una NCE. * Detectaste un duplicado emitido por error. ### Restricciones [#restricciones] * El documento tiene que estar `APROBADO`. No podés cancelar uno en `RECHAZADO` o `PENDIENTE`. * Plazo: 48 horas desde la fecha de emisión (según SIFEN). Pasado ese plazo hay que emitir una NCE en su lugar. * El timbrado tiene que estar vigente al momento de enviar la cancelación. * Es irreversible: un documento cancelado no se puede "descancelar". ### Cómo emitirla [#cómo-emitirla] ```bash curl -X POST https://api.sifende.com.py/api/v1/documento-electronico/:cdc/cancelar \ -H "Authorization: Bearer sk_live_..." \ -H "Content-Type: application/json" \ -d '{ "motivo": "Documento emitido con datos incorrectos del receptor" }' ``` El motivo es un texto libre de 5 a 500 caracteres que se registra ante SIFEN. Ver [Cancelar Documento](/docs/referencia/documentos-electronicos/cancelar) para detalles. ## Inutilización de numeración [#inutilización-de-numeración] Marca un rango de números no utilizados dentro de un timbrado como inutilizables. Sirve para documentar saltos de numeración que de otra forma quedarían como huecos en la secuencia ante SIFEN. ### Cuándo usarlo [#cuándo-usarlo-1] * Tu sistema reservó un número pero falló antes de emitir el documento. * Un cambio de software dejó números sin asignar. * Detectaste un hueco en la secuencia y necesitás cerrarlo formalmente. ### Restricciones [#restricciones-1] * Solo aplica a números nunca emitidos. No podés inutilizar un número que ya tiene un CDC en SIFEN. * El rango tiene que pertenecer a un mismo establecimiento + punto de expedición + tipo de documento. * El timbrado tiene que estar vigente al enviar la inutilización. * Es irreversible: los números inutilizados no se pueden reutilizar. ### Cómo emitirla [#cómo-emitirla-1] ```bash curl -X POST https://api.sifende.com.py/api/v1/documento-electronico/inutilizar \ -H "Authorization: Bearer sk_live_..." \ -H "Content-Type: application/json" \ -d '{ "tipoDocumento": "FACTURA_ELECTRONICA", "numeroEstablecimiento": 1, "puntoExpedicion": 1, "numeroInicio": 25, "numeroFin": 30, "motivo": "Pérdida de numeración por falla del sistema" }' ``` Ver [Inutilizar Numeración](/docs/referencia/documentos-electronicos/inutilizar) para detalles. ## Diferencias entre eventos y NCE/NDE [#diferencias-entre-eventos-y-ncende] Hay una superposición conceptual entre cancelar un DE y emitir una nota de crédito. La diferencia: | Aspecto | Cancelación (evento) | Nota de Crédito (NCE) | | ----------------- | ----------------------------------- | ---------------------------------------- | | Tipo de operación | Evento SIFEN | Documento electrónico | | Plazo | 48 horas desde emisión | Sin límite estricto | | Efecto contable | Documento "no existe" fiscalmente | Documento existe + reverso parcial/total | | Receptor | Cualquier tipo (incluso innominado) | Receptor identificado (no innominado) | | Cuándo elegirla | Errores tempranos, duplicados | Devoluciones, descuentos posteriores | Como regla práctica: * Si todavía estás en plazo y el cliente no recibió el comprobante, cancelá. * Si el cliente ya pagó o recibió el comprobante, emitís una NCE. ## Listado de eventos emitidos [#listado-de-eventos-emitidos] Podés consultar el historial de eventos enviados a SIFEN: ```bash curl https://api.sifende.com.py/api/v1/documento-electronico/eventos \ -H "Authorization: Bearer sk_live_..." ``` Ver [Listar Eventos](/docs/referencia/eventos/listar) para los parámetros de búsqueda. ## Próximos pasos [#próximos-pasos] * [Cancelar Documento](/docs/guias/cancelar-documento): guía paso a paso. * [Inutilizar Numeración](/docs/guias/inutilizar-numeracion): guía paso a paso. * [Nota de Crédito](/docs/guias/nota-credito): alternativa a la cancelación. # Conceptos (/docs/conceptos) Esta sección explica los conceptos clave de la plataforma Sifende y del sistema SIFEN. Cada página es independiente: podés leerlas en orden o ir directo al concepto que necesitás. ## Orden recomendado [#orden-recomendado] Para desarrolladores que están integrando por primera vez: 1. [Cómo Funciona](/docs/conceptos/como-funciona): arquitectura general. 2. [Autenticación](/docs/conceptos/autenticacion): cómo hablar con la API. 3. [Contribuyente](/docs/conceptos/contribuyente): la entidad emisora. 4. [Timbrado y Numeración](/docs/conceptos/timbrado-numeracion): la habilitación de la SET. 5. [Documentos Electrónicos](/docs/conceptos/documentos-electronicos): qué podés emitir. 6. [Receptor](/docs/conceptos/receptor): quién recibe el documento. 7. [Ítems e IVA](/docs/conceptos/items-iva): cómo se estructuran los productos. 8. [Ciclo de Vida](/docs/conceptos/ciclo-de-vida): qué pasa después de emitir. 9. [CDC](/docs/conceptos/cdc): el identificador único del documento. 10. [Lotes](/docs/conceptos/lotes): cómo se agrupan los DEs para SIFEN. 11. [Ambientes](/docs/conceptos/ambientes): test vs producción. 12. [Eventos SIFEN](/docs/conceptos/eventos-sifen): cancelación e inutilización. # Ítems e IVA (/docs/conceptos/items-iva) Cada documento electrónico tiene un arreglo de **ítems**: los productos o servicios que se están facturando. Sifende calcula los totales, el IVA por tasa y los subtotales a partir de los ítems que envíes. ## Anatomía de un ítem [#anatomía-de-un-ítem] ```json { "codigo": "PROD-001", "descripcion": "Café molido tostado 500g", "unidadMedida": "UNI", "cantidad": 2, "precioUnitario": 25000, "afectacionTributaria": "GRAVADO", "tasaIVA": 10 } ``` | Campo | Descripción | | ---------------------- | ------------------------------------------------------------------------------------------------- | | `codigo` | Código interno del producto (libre, para tu sistema) | | `descripcion` | Nombre legible del producto/servicio que aparece en el KuDE | | `unidadMedida` | Unidad — `UNI`, `KG`, `LITRO`, `HORA`, etc. (ver [Enumeraciones](/docs/referencia/enumeraciones)) | | `cantidad` | Cantidad numérica (acepta decimales) | | `precioUnitario` | Precio por unidad con IVA incluido (en PYG, entero) | | `afectacionTributaria` | Cómo aplica el IVA (ver abajo) | | `tasaIVA` | Tasa de IVA — `10`, `5` o `0` | ## Tasas de IVA en Paraguay [#tasas-de-iva-en-paraguay] El sistema tributario paraguayo tiene tres tasas de IVA: | Tasa | Aplica a | | ------------- | -------------------------------------------------------------------------------------------------------- | | 10% (general) | La mayoría de productos y servicios: ropa, electrónicos, software, servicios profesionales, combustibles | | 5% (reducida) | Alimentos de la canasta básica, productos farmacéuticos, productos agropecuarios, libros | | 0% (exento) | Exportaciones, servicios financieros, alquiler de inmuebles para vivienda, salud, educación | ## Afectación tributaria [#afectación-tributaria] El campo `afectacionTributaria` indica cómo se trata fiscalmente cada ítem. Combinalo con `tasaIVA` para indicar la tasa aplicable: | Valor | `tasaIVA` | Descripción | | ----------------- | ---------- | --------------------------------------------------------- | | `GRAVADO` | `10` o `5` | Operación gravada con IVA. La tasa se indica en `tasaIVA` | | `EXENTO` | `0` | Operación exenta (sin IVA) | | `EXONERADO` | `0` | Operación exonerada por ley específica | | `GRAVADO_PARCIAL` | `10` o `5` | Solo una porción de la operación está gravada | Un solo documento puede mezclar ítems con afectaciones distintas. Por ejemplo, una farmacia que vende medicamentos (`GRAVADO` con `tasaIVA: 5`) y cosméticos (`GRAVADO` con `tasaIVA: 10`) en la misma factura. ## Cálculo de IVA [#cálculo-de-iva] En Paraguay, el `precioUnitario` siempre incluye el IVA. No se agrega IVA encima del precio: el IVA ya está adentro. El cálculo del componente de IVA dentro de un precio total es: ``` IVA = precio × tasa / (100 + tasa) Base = precio × 100 / (100 + tasa) ``` ### Ejemplo — IVA al 10% [#ejemplo--iva-al-10] Producto a `25000` PYG con IVA al 10% (`afectacionTributaria: "GRAVADO"`, `tasaIVA: 10`): ``` IVA = 25000 × 10 / 110 = 2272.73 → 2273 PYG Base = 25000 × 100 / 110 = 22727.27 → 22727 PYG Verificación: 22727 + 2273 = 25000 ✓ ``` ### Ejemplo — IVA al 5% [#ejemplo--iva-al-5] Producto a `21000` PYG con IVA al 5% (`afectacionTributaria: "GRAVADO"`, `tasaIVA: 5`): ``` IVA = 21000 × 5 / 105 = 1000 PYG Base = 21000 × 100 / 105 = 20000 PYG Verificación: 20000 + 1000 = 21000 ✓ ``` No tenés que hacer estos cálculos a mano. Sifende los aplica solo a partir de `precioUnitario`, `cantidad`, `afectacionTributaria` y `tasaIVA`. La fórmula está acá para que entiendas el modelo. ## Reglas de redondeo en PYG [#reglas-de-redondeo-en-pyg] El guaraní no tiene decimales. Los totales se redondean a entero usando "half-up" (medio hacia arriba): | Valor calculado | Valor en factura | | --------------- | ---------------- | | `2272.73` | `2273` | | `2272.49` | `2272` | | `2272.50` | `2273` | Si tu sistema interno trabaja con decimales (por ejemplo, tu ERP guarda `precio = 22727.27`), redondeá antes de enviar a Sifende. Mandar `precioUnitario: 22727.27` con `monedaOperacion: "PYG"` te va a dar error de validación. ## Cálculo de totales [#cálculo-de-totales] Sifende deriva estos totales del arreglo de ítems: ``` subtotal_item = cantidad × precioUnitario total_documento = Σ subtotal_item (todos los ítems) total_iva_10 = Σ iva calculado de ítems con afectacionTributaria=GRAVADO y tasaIVA=10 total_iva_5 = Σ iva calculado de ítems con afectacionTributaria=GRAVADO y tasaIVA=5 total_iva = total_iva_10 + total_iva_5 total_gravado_10 = Σ base de ítems con afectacionTributaria=GRAVADO y tasaIVA=10 total_gravado_5 = Σ base de ítems con afectacionTributaria=GRAVADO y tasaIVA=5 total_exento = Σ subtotales de ítems con afectacionTributaria=EXENTO o EXONERADO ``` Estos totales aparecen en el bloque F (Totales) del DE y se imprimen en el KuDE. ## Descuentos [#descuentos] Cada ítem puede tener un descuento por línea, y el documento puede tener un descuento global aplicado al total. Ver [Modelo: Item](/docs/referencia/modelos/item) para el detalle de los campos. ## Próximos pasos [#próximos-pasos] * [Modelo: Item](/docs/referencia/modelos/item): schema completo. * [Enumeraciones](/docs/referencia/enumeraciones): unidades de medida y afectaciones. * [Convenciones — Moneda PYG](/docs/referencia/convenciones): reglas de redondeo. # Lotes (/docs/conceptos/lotes) SIFEN no acepta documentos individuales: los recibe en **lotes** vía el web service `siRecepLoteDE`. Sifende toma cada documento que emitís, lo empaqueta en un lote y lo transmite a SIFEN. El lote es la unidad de comunicación con SIFEN. ## Qué es un lote [#qué-es-un-lote] Un lote es un contenedor XML que agrupa uno o más documentos electrónicos firmados, listos para ser transmitidos a SIFEN. ``` Lote (siRecepLoteDE) ├── DE 1 (firmado XAdES) ├── DE 2 (firmado XAdES) ├── ... └── DE N (hasta 50 por lote, según especificación SIFEN) ``` Hoy Sifende envía un documento por lote. Esto simplifica el manejo de errores: si SIFEN rechaza un DE, el rechazo no afecta a otros documentos del mismo batch. Más adelante podríamos agrupar para mejor throughput, pero el contrato de la API no cambia. ## Estados del lote [#estados-del-lote] | Estado | Descripción | | ------------ | ---------------------------------------------------------------------------------------- | | `PREPARADO` | Sifende creó el lote y firmó los DEs adentro; aún no lo transmitió | | `INTENTANDO` | Worker en proceso de transmitir el lote a SIFEN (`siRecepLoteDE`) | | `ENVIADO` | El lote fue aceptado por SIFEN; esperando resultado del procesamiento | | `PROCESADO` | SIFEN procesó el lote; cada DE adentro tiene su veredicto (`APROBADO` o `RECHAZADO`) | | `ERROR` | Falló la transmisión o SIFEN devolvió un error transitorio. Se reintenta automáticamente | | `FALLIDO` | Reintentos agotados sin éxito. Estado terminal, requiere intervención manual | ## Diagrama del ciclo de vida del lote [#diagrama-del-ciclo-de-vida-del-lote] ``` ┌─────────────┐ POST DE ───► │ PREPARADO │ └──────┬──────┘ │ Worker toma el lote y comienza la transmisión ▼ ┌─────────────┐ │ INTENTANDO │ ◄────┐ └──────┬──────┘ │ │ │ Reintento con backoff ▼ │ exponencial ┌────────┴────────┐ │ ▼ ▼ │ ┌───────────┐ ┌───────────┴┐ │ ENVIADO │ │ ERROR │ └─────┬─────┘ └─────┬──────┘ │ siResultLoteDE │ Reintentos │ devuelve 0362 │ agotados ▼ ▼ ┌───────────┐ ┌───────────┐ │ PROCESADO │ │ FALLIDO │ └───────────┘ └───────────┘ (terminal) (terminal) ``` ## Códigos de respuesta de SIFEN [#códigos-de-respuesta-de-sifen] Cuando Sifende consulta el resultado de un lote vía `siResultLoteDE`, SIFEN devuelve uno de estos códigos: | Código | Significado | Acción | | ------ | --------------------------------------------------------- | -------------------------------------------------------- | | `0360` | Lote inexistente — SIFEN no lo encuentra | Reintentar consulta; si persiste, marcar `FALLIDO` | | `0361` | Aún en procesamiento — no concluido | Reintentar. Puede tomar 1 a 24 horas en horario pico | | `0362` | Procesamiento concluido — resultados individuales adentro | Procesar veredicto de cada DE → `APROBADO` o `RECHAZADO` | | `0363` | Tipos mixtos de DE en el lote | Lote rechazado completo; corregir y re-emitir | | `0364` | Consulta extemporánea (después de 48h del envío) | Caer a consulta por CDC individual (`siConsDE`) | Ventana de 48h: pasadas 48 horas desde el envío del lote, `siResultLoteDE` deja de responder y devuelve `0364`. Si en ese tiempo Sifende no logró confirmar el resultado del lote, el sistema cambia a consulta individual por CDC (`siConsDE`) para reconciliar el estado. ## Cómo Sifende procesa los lotes [#cómo-sifende-procesa-los-lotes] Recepción del POST: tu sistema envía un documento. Sifende valida el payload, genera el XML, lo firma y lo asocia a un nuevo lote en estado `PREPARADO`. Transmisión a SIFEN: un worker toma el lote y lo marca `INTENTANDO`. Envía el lote vía `siRecepLoteDE`. Si SIFEN responde con un `idLote` válido, el lote pasa a `ENVIADO`. Si la transmisión falla por error transitorio, el lote pasa a `ERROR` y se reintenta. Polling de resultados: un scheduler corre cada 60 segundos consultando `siResultLoteDE` con cada `idLote` de los lotes en `ENVIADO`. Procesamiento del veredicto: interpretación del código de respuesta de SIFEN según la tabla de arriba. En `0362`, cada DE recibe individualmente `APROBADO` o `RECHAZADO`. Estado final: el lote pasa a `PROCESADO` (éxito), `FALLIDO` (reintentos agotados) o queda en `ENVIADO` esperando que SIFEN concluya. ## Estado del lote vs. estado del documento [#estado-del-lote-vs-estado-del-documento] | Lote | Documentos adentro | | -------------------------- | --------------------------------------------------------------------- | | `PREPARADO` / `INTENTANDO` | Todos en `EN_LOTE` | | `ENVIADO` | Todos en `ENVIADO` | | `PROCESADO` | Cada DE en `APROBADO` o `RECHAZADO` individualmente | | `ERROR` | Documentos quedan en `EN_LOTE` o `ENVIADO` hasta que se reintenta | | `FALLIDO` | Documentos quedan en `ERROR` o `DESCONOCIDO` para intervención manual | Un lote puede estar `PROCESADO` con algunos documentos `APROBADO` y otros `RECHAZADO`. SIFEN evalúa cada DE individualmente: un rechazo aislado no invalida los otros documentos del mismo lote. ## Reintentos automáticos [#reintentos-automáticos] Si SIFEN no responde o devuelve un error transitorio (no de validación), Sifende reintenta solo: * Backoff exponencial: 1m, 5m, 15m, 1h, 6h. * Sin pérdida de datos: el documento queda en `EN_LOTE` o `ENVIADO` hasta que se confirma. * Sin acción de tu parte: tu sistema no necesita hacer nada. Sifende absorbe los retrasos de SIFEN. ## Visualización en el panel [#visualización-en-el-panel] Desde el panel web, andá a Documentos electrónicos → Lotes para ver: * Lista de todos los lotes con su estado actual. * Cantidad de DEs por lote y resultado individual de cada uno. * Códigos de respuesta de SIFEN para diagnóstico. * Detalle del payload XML enviado y recibido. ## Próximos pasos [#próximos-pasos] * [Ciclo de Vida](/docs/conceptos/ciclo-de-vida): los estados del documento individual. * [Polling de Resultados](/docs/guias/polling-resultados): la estrategia recomendada. * [Rechazos SIFEN](/docs/solucion-problemas/rechazos-sifen): códigos de error y sus causas. # Receptor (/docs/conceptos/receptor) El **receptor** es el destinatario del documento electrónico: la persona o empresa a la que le emitís la factura. Cómo se llena el bloque de receptor depende de si el destinatario es otro contribuyente (B2B) o un consumidor final (B2C). ## Campos del receptor [#campos-del-receptor] El bloque `receptor` usa siempre los mismos nombres de campo, sin importar el tipo de operación: | Campo | Descripción | | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `tipoOperacion` | Tipo de operación — `B2B`, `B2C`, `B2G`, `B2F` | | `tipoContribuyente` | `CONTRIBUYENTE`, `NO_CONTRIBUYENTE`, `EXTRANJERO`, `NOMINADO`, `INNOMINADO` | | `tipoDocumento` | Tipo de identificación — `CEDULA_PARAGUAYA`, `PASAPORTE`, `CEDULA_EXTRANJERA`, `CARNET_DE_RESIDENCIA`, `INNOMINADO`, `TARJETA_DIPLOMATICA`, `OTRO` (no aplica para B2B) | | `numeroDocumento` | El RUC (parte numérica), cédula o número de pasaporte | | `digitoVerificador` | DV del RUC. Solo para receptores B2B/B2G | | `nombreRazonSocial` | Nombre completo o razón social del receptor | | `direccion` | Dirección del receptor (opcional según operación) | ## Ejemplos [#ejemplos] Receptor innominado para venta mostrador sin identificar al cliente. El caso más simple: ```json { "receptor": { "tipoOperacion": "B2C", "tipoContribuyente": "INNOMINADO", "nombreRazonSocial": "Sin Nombre" } } ``` Receptor contribuyente con RUC, venta a otra empresa: ```json { "receptor": { "tipoOperacion": "B2B", "tipoContribuyente": "CONTRIBUYENTE", "numeroDocumento": "80098765", "digitoVerificador": "3", "nombreRazonSocial": "Comercial Guaraní S.A.", "direccion": "Av. España 1234" } } ``` ## B2B — Receptor contribuyente [#b2b--receptor-contribuyente] Cuando el destinatario es otra empresa o persona con RUC, la operación es B2B y `tipoContribuyente = CONTRIBUYENTE`. El receptor se identifica por `numeroDocumento` (parte numérica del RUC) y `digitoVerificador`. No se envía `tipoDocumento` para B2B. SIFEN valida el RUC contra su padrón. Si el RUC no existe o el DV no coincide, el documento se rechaza con error 1302–1306. ## B2C — Receptor consumidor final [#b2c--receptor-consumidor-final] Cuando el destinatario es una persona física que no es contribuyente del IVA, la operación es B2C. Tenés dos modalidades: ### Receptor identificado (NOMINADO) [#receptor-identificado-nominado] ```json { "receptor": { "tipoOperacion": "B2C", "tipoContribuyente": "NOMINADO", "tipoDocumento": "CEDULA_PARAGUAYA", "numeroDocumento": "1234567", "nombreRazonSocial": "María González" } } ``` Para extranjeros, usá `tipoDocumento: "PASAPORTE"` o `"CARNET_DE_RESIDENCIA"` según corresponda, y `tipoContribuyente: "EXTRANJERO"` cuando el receptor reside fuera del país. ### Receptor innominado [#receptor-innominado] Para ventas de bajo monto en mostrador donde no se identifica al cliente, usá `tipoContribuyente: "INNOMINADO"` con `nombreRazonSocial: "Sin Nombre"`. El receptor innominado solo es válido en Factura Electrónica (FE). SIFEN rechaza notas de crédito y notas de débito con receptor innominado (NT-010 / NT-023). Para emitir una NCE o NDE, el receptor tiene que estar identificado con RUC, cédula o pasaporte. ## Resumen — cuándo usar cada modo [#resumen--cuándo-usar-cada-modo] | Caso | `tipoOperacion` | `tipoContribuyente` | `tipoDocumento` | | ------------------------------------ | --------------------- | ------------------- | ----------------------------------------------------- | | Vendés a otra empresa con RUC | `B2B` | `CONTRIBUYENTE` | — (RUC va en `numeroDocumento` + `digitoVerificador`) | | Vendés a un cliente final con cédula | `B2C` | `NOMINADO` | `CEDULA_PARAGUAYA` | | Vendés a un extranjero residente | `B2C` | `EXTRANJERO` | `PASAPORTE` o `CARNET_DE_RESIDENCIA` | | Venta de bajo monto sin identificar | `B2C` | `INNOMINADO` | — | | Venta a institución pública | `B2G` | `CONTRIBUYENTE` | — (RUC va en `numeroDocumento` + `digitoVerificador`) | | Exportación / receptor del exterior | `B2F` | `EXTRANJERO` | `PASAPORTE` u `OTRO` | | Emitís una NCE o NDE | igual al doc original | No `INNOMINADO` | RUC o cédula | ## Próximos pasos [#próximos-pasos] * [Modelo: Receptor](/docs/referencia/modelos/receptor): schema completo. * [Guía: Receptor B2B vs B2C](/docs/guias/receptor-b2b-b2c): paso a paso con ejemplos. * [Documentos Electrónicos](/docs/conceptos/documentos-electronicos): qué tipos podés emitir. # Timbrado y Numeración (/docs/conceptos/timbrado-numeracion) El **timbrado** es la autorización numérica que la SET le otorga al contribuyente para emitir documentos. Cada timbrado es un permiso con un rango de numeración, una validez temporal y una asignación a uno o más establecimientos. Sin un timbrado vigente no se puede emitir ningún documento electrónico. ## Qué es un timbrado [#qué-es-un-timbrado] ``` Timbrado 12345678 ├── Vigencia: 2026-01-01 a 2027-12-31 ├── Tipo: Electrónico ├── Asignado a: Contribuyente (RUC 80012345-1) └── Habilita: numeración por establecimiento + punto de expedición ``` Un timbrado electrónico tiene estos datos: | Campo | Descripción | | ------------- | ----------------------------------------------------------------------- | | `numero` | Número de timbrado de 8 dígitos (asignado por la SET) — ej. `12345678` | | `fechaInicio` | Fecha desde la cual se pueden emitir documentos con este timbrado | | `fechaFin` | Fecha hasta la cual el timbrado es válido (D'F011 / `dFeFinT` en SIFEN) | | `tipo` | Electrónico (único soportado en Sifende) | Si intentás emitir un documento con un timbrado vencido, SIFEN lo rechaza con el código de error 1108. Verificá que `fechaFin` no haya pasado. ## Establecimiento y punto de expedición [#establecimiento-y-punto-de-expedición] La numeración de un documento electrónico no es global del contribuyente. Está segmentada por **establecimiento** (sucursal) y **punto de expedición** (caja, terminal o software dentro del establecimiento). ``` Contribuyente ├── Establecimiento 001 (Casa Central — Asunción) │ ├── Punto de expedición 001 (Caja 1) │ ├── Punto de expedición 002 (Caja 2) │ └── Punto de expedición 003 (Software web) └── Establecimiento 002 (Sucursal Encarnación) └── Punto de expedición 001 (Caja única) ``` Cada combinación `establecimiento` + `puntoExpedicion` mantiene su propia numeración secuencial. Esto permite que varias cajas del mismo establecimiento facturen en paralelo sin chocarse. ## Formato del número de documento [#formato-del-número-de-documento] El número de un documento electrónico tiene tres componentes: ``` {establecimiento}-{puntoExpedicion}-{numero} 001 - 001 - 0000001 ``` | Componente | Dígitos | Ejemplo | Descripción | | ------------------- | ------- | --------- | ------------------------------------------------------- | | Establecimiento | 3 | `001` | Identifica la sucursal | | Punto de expedición | 3 | `001` | Identifica el punto de venta dentro del establecimiento | | Número | 7 | `0000001` | Auto-incremental por establecimiento+punto | El número se incrementa secuencialmente y sin saltos dentro de cada combinación establecimiento+punto. Si se rompe la secuencia (por ejemplo, por una emisión fallida), hay que [inutilizar el rango faltante](/docs/conceptos/eventos-sifen) ante SIFEN. ## Asignación automática en Sifende [#asignación-automática-en-sifende] Sifende asigna solo el siguiente número disponible al emitir un documento. Tu sistema no tiene que llevar la cuenta: solo tenés que indicar el `numeroEstablecimiento` y `puntoExpedicion` (por defecto `1` y `1` si no hay multi-establecimiento). ```json { "tipoDocumento": "FACTURA_ELECTRONICA", "numeroEstablecimiento": 1, "puntoExpedicion": 1, ... } ``` Sifende toma el próximo número del timbrado vigente y se lo asigna al documento. ## Relación con el CDC [#relación-con-el-cdc] El número de documento, junto con el RUC, fecha y timbrado, forma parte del [CDC](/docs/conceptos/cdc): ``` CDC = tiDE(2) + RUC(8) + DV(1) + establecimiento(3) + puntoExpedicion(3) + numero(7) + tipoEmision(1) + fechaEmision(8) + codigoSeguridad(9) + DV(1) ``` Por eso, una vez emitido un documento, su número no se puede modificar: está embebido en el CDC. ## Vencimiento del timbrado [#vencimiento-del-timbrado] Cuando tu timbrado se acerca al `fechaFin`: Solicitá un timbrado nuevo en el portal de la SET. Cargá el nuevo timbrado en el panel de Sifende antes de que venza el actual. Sifende empieza a usar el nuevo timbrado automáticamente cuando el anterior expira. Tener dos timbrados solapados (uno vigente y uno futuro) no es un problema. Sifende usa el que esté vigente en la `fechaEmision` del documento. ## Próximos pasos [#próximos-pasos] * [CDC](/docs/conceptos/cdc): la estructura del identificador único. * [Inutilizar Numeración](/docs/guias/inutilizar-numeracion): qué hacer si se rompe la secuencia. * [Múltiples Establecimientos](/docs/guias/multiples-establecimientos): cómo gestionar varias sucursales. # Cancelar un Documento (/docs/guias/cancelar-documento) Si emitiste un DE con datos incorrectos y querés invalidarlo legalmente, tenés que cancelarlo enviando un evento a SIFEN. ## Cuándo cancelar [#cuándo-cancelar] Cancelá un documento cuando: * El documento está en estado `APROBADO` * Detectaste un error en datos del receptor, items o montos * El receptor **aún no lo declaró** ante la SET para deducir IVA * Estás dentro del plazo legal (48 horas según normativa SIFEN) La cancelación es **irreversible**. Una vez que SIFEN procesa el evento, el documento queda en estado `CANCELADO` permanentemente y no se puede revertir. ## Cuándo NO podés cancelar [#cuándo-no-podés-cancelar] | Estado del DE | Acción correcta | | ---------------------- | --------------------------------------------------------------- | | `RECHAZADO` | No requiere cancelación, ya es legalmente nulo. Emití uno nuevo | | `CANCELADO` | Ya está cancelado | | `PENDIENTE` | Esperá a que SIFEN procese el lote antes de decidir | | Receptor ya lo declaró | Solo podés emitir una **Nota de Crédito** para anularlo | ## Cómo cancelar [#cómo-cancelar] **Identificá el CDC** del documento a cancelar (44 dígitos). **Enviá el evento de cancelación** con un motivo descriptivo (5-500 caracteres): ```bash curl -X POST "https://api.sifende.com.py/api/v1/documento-electronico/01800123451001001000000122026042710000000006/cancelar" \ -H "Authorization: Bearer sk_live_..." \ -H "Content-Type: application/json" \ -d '{ "motivo": "Error en datos del receptor" }' ``` | Campo | Tipo | Req. | Restricción | | -------- | -------- | ---- | ---------------- | | `motivo` | `string` | Sí | 5-500 caracteres | **Sifende reenvía el evento a SIFEN.** El documento pasa a estado `CANCELADO`. **Verificá el estado** consultando `GET /api/v1/documento-electronico/status/:cdc`. ## Ejemplo en TypeScript [#ejemplo-en-typescript] ```typescript async function cancelarDocumento(cdc: string, motivo: string) { const response = await fetch( `https://api.sifende.com.py/api/v1/documento-electronico/${cdc}/cancelar`, { method: "POST", headers: { "Authorization": `Bearer ${process.env.SIFENDE_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ motivo }), } ); if (!response.ok) { const error = await response.json(); throw new Error(`Cancelación fallida: ${error.detail}`); } console.log("Documento cancelado correctamente"); } await cancelarDocumento( "01800123451001001000000122026042710000000006", "Error en RUC del receptor" ); ``` ## Después de cancelar [#después-de-cancelar] * El número de documento **no se libera**: queda registrado como cancelado en SIFEN * Si necesitás facturar nuevamente, emití un **nuevo DE** con el siguiente número de la secuencia * El KuDE original queda invalidado y no debe entregarse al cliente La cancelación NO consume un nuevo número de timbrado. Solo afecta al documento existente. ## Errores comunes [#errores-comunes] | Error | Causa | Solución | | ------------------------------------- | ------------------------------- | ----------------------------------------- | | `409 evento-cancelacion-error` | Documento no está en `APROBADO` | Verificá el estado con `GET /status/:cdc` | | `404 documento-electronico-not-found` | CDC no existe | Verificá los 44 dígitos del CDC | | `400 evento-cancelacion-error` | Plazo de cancelación vencido | Emití una Nota de Crédito en su lugar | ## Próximos pasos [#próximos-pasos] * [Inutilizar Numeración](/docs/guias/inutilizar-numeracion): para números no emitidos * [Nota de Crédito](/docs/guias/nota-credito): alternativa cuando ya pasó el plazo * [Referencia: Cancelar](/docs/referencia/documentos-electronicos/cancelar) # Consultar Estado de un Documento (/docs/guias/consultar-estado) Cuando emitís un documento electrónico, Sifende lo recibe al instante y te devuelve el CDC, pero el procesamiento real en SIFEN es **asíncrono**. Esta guía cubre cómo consultar el estado y cuándo dejar de hacer polling. ## El endpoint [#el-endpoint] ``` GET /api/v1/documento-electronico/status/:cdc ``` ```bash curl https://api.sifende.com.py/api/v1/documento-electronico/status/$CDC \ -H "Authorization: Bearer $SIFENDE_API_KEY" ``` Respuesta (`DocumentoElectronicoStatusDTO`): ```json { "cdc": "01800123451001001000000122026042710000000006", "estado": "APROBADO", "iTiDe": 1, "numeroDocumento": 1, "fechaCreacion": "2026-04-27T10:30:00", "protocoloAutorizacion": "20260427103018987654", "mensajeRechazo": null } ``` | Campo | Tipo | Descripción | | ----------------------- | ---------------- | ------------------------------------------------------------ | | `cdc` | `string` | CDC del documento (44 caracteres) | | `estado` | `enum` | Estado actual (ver tabla abajo) | | `iTiDe` | `int` | Código numérico del tipo de documento SIFEN | | `numeroDocumento` | `Long` | Número correlativo dentro del rango de timbrado (entero) | | `fechaCreacion` | `string` | Fecha y hora en que Sifende registró el documento (ISO 8601) | | `protocoloAutorizacion` | `string \| null` | Número de protocolo SIFEN. Solo si `estado: "APROBADO"` | | `mensajeRechazo` | `string \| null` | Mensaje detallado de SIFEN. Solo si `estado: "RECHAZADO"` | `numeroDocumento` es un **entero** (`Long`). Si necesitás el formato `"NNN-NNN-NNNNNNN"` para mostrar al usuario, usá el campo `numeroFormateado` que devuelve la respuesta de **emisión** (`POST /documento-electronico`). Son dos campos separados, no los confundas. ## Estados posibles [#estados-posibles] | Estado | Significado | ¿Seguir consultando? | | ------------- | -------------------------------------------------------------- | :-----------------------------: | | `PENDIENTE` | Sifende lo recibió pero todavía no se asignó a un lote | Sí | | `EN_LOTE` | Está agrupado en un lote, listo para enviar a SIFEN | Sí | | `ENVIADO` | El lote ya fue enviado a SIFEN, esperando respuesta | Sí | | `APROBADO` | SIFEN lo aprobó. El documento es válido y firmado | **No**, terminado | | `RECHAZADO` | SIFEN lo rechazó. Leer `mensajeRechazo` | **No**, terminado | | `ERROR` | Falla técnica en el envío o procesamiento | **No**, revisar logs y reemitir | | `CANCELADO` | Fue aprobado y luego cancelado vía evento | **No**, terminado | | `DESCONOCIDO` | Estado no determinable (caso raro de inconsistencia con SIFEN) | Consultar nuevamente | ## Estrategia de polling recomendada [#estrategia-de-polling-recomendada] * **Intervalo:** entre 3 y 5 segundos. Más frecuente solo agrega carga sin reducir la latencia real. * **Timeout:** 5 minutos como máximo. Si no resuelve antes, hay un problema de procesamiento; revisá el panel de Sifende. * **Detenete** apenas el estado pase a `APROBADO`, `RECHAZADO` o `CANCELADO`. ### Implementación en TypeScript [#implementación-en-typescript] ```typescript type EstadoDE = | 'PENDIENTE' | 'EN_LOTE' | 'ENVIADO' | 'APROBADO' | 'RECHAZADO' | 'ERROR' | 'CANCELADO' | 'DESCONOCIDO'; interface EstadoResponse { cdc: string; estado: EstadoDE; iTiDe: number; numeroDocumento: number; // Long en backend fechaCreacion: string; protocoloAutorizacion: string | null; mensajeRechazo: string | null; } async function esperarResultadoSIFEN( cdc: string, { intervaloMs = 5000, timeoutMs = 300_000 } = {} ): Promise { const inicio = Date.now(); while (Date.now() - inicio < timeoutMs) { const res = await fetch( `https://api.sifende.com.py/api/v1/documento-electronico/status/${cdc}`, { headers: { Authorization: `Bearer ${process.env.SIFENDE_API_KEY}` } } ); if (!res.ok) { throw new Error(`Error consultando estado: ${res.status}`); } const data: EstadoResponse = await res.json(); if ( data.estado === 'APROBADO' || data.estado === 'RECHAZADO' || data.estado === 'ERROR' || data.estado === 'CANCELADO' ) { return data; } await new Promise(r => setTimeout(r, intervaloMs)); } throw new Error(`Timeout esperando resultado SIFEN para CDC ${cdc}`); } ``` ### Uso típico [#uso-típico] ```typescript const resultado = await esperarResultadoSIFEN(cdc); switch (resultado.estado) { case 'APROBADO': // Descargá el KuDE, marcá la venta como facturada, mandá email al cliente break; case 'RECHAZADO': // Logueá mensajeRechazo, alertá al equipo, corregí los datos console.error('SIFEN rechazó el DE:', resultado.mensajeRechazo); break; case 'CANCELADO': // El documento fue cancelado; usualmente esto no aparece en flujo de emisión break; } ``` ## ¿Qué hacer en cada estado final? [#qué-hacer-en-cada-estado-final] ### APROBADO [#aprobado] * Tu documento ya es legalmente válido en SIFEN. * Generá el KuDE y entregalo al cliente. Ver [Descargar KuDE](/docs/guias/descargar-kude). * Si necesitás revertirlo o ajustarlo, emití una [Nota de Crédito](/docs/guias/nota-credito) o [Nota de Débito](/docs/guias/nota-debito). ### RECHAZADO [#rechazado] * El campo `mensajeRechazo` contiene el código y descripción de SIFEN (ej: `1108 - Timbrado vencido`). * Corregí los datos del documento o la configuración subyacente (timbrado, certificado, etc.). El documento rechazado no se reintenta tal cual; emitís uno nuevo. * Detalle paso a paso en [Manejar Errores](/docs/guias/manejar-errores) y [Reintentar Rechazados](/docs/guias/reintentar-rechazados). ### CANCELADO [#cancelado] * El documento fue aprobado y posteriormente cancelado mediante evento. * Este estado normalmente lo vas a ver al consultar un documento ya procesado, no como resultado del flujo de emisión. ## Alternativa: webhooks (próximamente) [#alternativa-webhooks-próximamente] El polling es sencillo pero puede ser ineficiente a alto volumen. Sifende está implementando webhooks para notificar cambios de estado de manera push; seguí los avances en el [changelog](/docs/referencia/changelog). ## Próximos pasos [#próximos-pasos] * Si recibís `RECHAZADO`, andá a [Manejar Errores](/docs/guias/manejar-errores). * Para volúmenes altos y polling de muchos documentos, ver [Polling de Resultados](/docs/guias/polling-resultados). * Para entender los códigos SIFEN que aparecen en `mensajeRechazo`, ver [Rechazos SIFEN](/docs/solucion-problemas/rechazos-sifen). # Descargar KuDE (/docs/guias/descargar-kude) El **KuDE** (*Kuatia'i Documento Electrónico*) es la representación gráfica del documento electrónico: el PDF imprimible o adjuntable por email que entregás al cliente. No es el documento legal en sí mismo, pero es la versión legible para humanos. **El documento legal es el XML firmado**, no el KuDE. El cliente puede validar su factura escaneando el QR del KuDE, que apunta al portal SIFEN. No estás obligado a imprimirlo o enviarlo, pero sí es la forma estándar de entregarlo. ## Requisitos [#requisitos] * El documento debe estar en estado `APROBADO`. KuDE no se genera para `PENDIENTE`, `EN_LOTE` ni `RECHAZADO`. * Necesitás el **CDC** del documento. * Aplica a los tipos soportados: FE, NCE, NDE. ## El endpoint [#el-endpoint] ``` GET /api/v1/documento-electronico/:cdc/kude ``` Respuesta: binario PDF (`Content-Type: application/pdf`). ## Descargar y guardar a disco (cURL) [#descargar-y-guardar-a-disco-curl] ```bash curl -o factura.pdf \ https://api.sifende.com.py/api/v1/documento-electronico/$CDC/kude \ -H "Authorization: Bearer $SIFENDE_API_KEY" ``` ## Descargar desde Node.js / TypeScript [#descargar-desde-nodejs--typescript] Guardar el PDF en disco usando `fs/promises`: ```typescript import { writeFile } from 'node:fs/promises'; async function descargarKuDE(cdc: string, destino: string): Promise { const res = await fetch( `https://api.sifende.com.py/api/v1/documento-electronico/${cdc}/kude`, { headers: { Authorization: `Bearer ${process.env.SIFENDE_API_KEY}` } } ); if (!res.ok) { const problem = await res.json(); throw new Error(`No se pudo descargar KuDE: ${problem.title}`); } const buffer = Buffer.from(await res.arrayBuffer()); await writeFile(destino, buffer); } await descargarKuDE( '01800123451001001000000122026042710000000006', './kude/factura-001.pdf' ); ``` ## Stream a una respuesta HTTP (Express) [#stream-a-una-respuesta-http-express] Si tu backend está sirviendo el KuDE al frontend o al cliente directamente, podés streamearlo sin guardarlo en disco: ```typescript import express from 'express'; const app = express(); app.get('/facturas/:cdc/kude', async (req, res) => { const upstream = await fetch( `https://api.sifende.com.py/api/v1/documento-electronico/${req.params.cdc}/kude`, { headers: { Authorization: `Bearer ${process.env.SIFENDE_API_KEY}` } } ); if (!upstream.ok) { return res.status(upstream.status).json(await upstream.json()); } res.setHeader('Content-Type', 'application/pdf'); res.setHeader( 'Content-Disposition', `inline; filename="factura-${req.params.cdc}.pdf"` ); // Stream del body upstream a la respuesta del cliente const reader = upstream.body!.getReader(); while (true) { const { value, done } = await reader.read(); if (done) break; res.write(value); } res.end(); }); ``` Usá `Content-Disposition: inline` para que se muestre embebido en el navegador, o `attachment; filename="..."` para forzar descarga. ## Adjuntar el KuDE a un email al cliente [#adjuntar-el-kude-a-un-email-al-cliente] Patrón típico: una vez que el DE pasa a `APROBADO`, descargás el KuDE y lo enviás al receptor. ```typescript import { Resend } from 'resend'; const resend = new Resend(process.env.RESEND_API_KEY); async function enviarFacturaPorEmail(cdc: string, emailCliente: string) { const res = await fetch( `https://api.sifende.com.py/api/v1/documento-electronico/${cdc}/kude`, { headers: { Authorization: `Bearer ${process.env.SIFENDE_API_KEY}` } } ); const pdf = Buffer.from(await res.arrayBuffer()); await resend.emails.send({ from: 'facturas@miempresa.com.py', to: emailCliente, subject: `Tu factura electrónica`, html: '

Adjuntamos tu factura electrónica. Podés validarla escaneando el QR.

', attachments: [ { filename: `factura-${cdc}.pdf`, content: pdf }, ], }); } ``` ## Errores frecuentes [#errores-frecuentes] | Status | Tipo | Causa | Solución | | ------ | --------------------------------- | --------------------------------------------------- | ------------------------------------------------------- | | 404 | `documento-electronico-not-found` | CDC inexistente | Verificá el CDC | | 422 | `kude-generation-error` | El DE no está aprobado, o falta data para el render | Confirmá `estado: "APROBADO"` antes de pedir KuDE | | 501 | `kude-not-supported` | Tipo de documento sin soporte de KuDE (ej: AFE 🚧) | Esperá a que se implemente. Solo FE/NCE/NDE actualmente | ## Buenas prácticas [#buenas-prácticas] * **No descargues KuDE en cada request del cliente.** Guardalo en blob storage (S3, GCS) la primera vez y serví desde ahí. * **No mostrés KuDE de documentos no aprobados.** Pueden cambiar, y al cliente le confunde. * **Cacheá** el KuDE: una vez que el DE está `APROBADO`, el PDF no cambia. Tiene sentido cachear indefinidamente. * Si el QR del KuDE no resuelve en SIFEN, verificá que estés en el ambiente correcto (test vs producción). ## Próximos pasos [#próximos-pasos] * ¿Todavía no aprobaron tu DE? → [Consultar Estado](/docs/guias/consultar-estado). * Si tu cliente reporta problemas con el QR del KuDE, ver [FAQ](/docs/solucion-problemas/faq). * Detalles de la API → [Referencia: Descargar KuDE](/docs/referencia/documentos-electronicos/descargar-kude). # Factura Electrónica (/docs/guias/factura-electronica) Esta guía cubre la emisión de una Factura Electrónica (FE) paso a paso, desde el armado del request hasta el seguimiento del estado en SIFEN. ## Antes de empezar [#antes-de-empezar] Verificá que tenés los tres elementos imprescindibles configurados: * **Timbrado activo** y vigente, cargado en el panel de Sifende para tu contribuyente. * **Certificado digital** subido: `.p12` válido y en vigencia. * **API key** generada desde el panel y disponible como variable de entorno. Si te falta alguno, volvé a [Inicio Rápido: Requisitos previos](/docs/inicio-rapido/requisitos-previos). ## Paso 1: Armá el request [#paso-1-armá-el-request] La FE usa el endpoint polimórfico `POST /api/v1/documento-electronico` con `tipoDocumento: "FACTURA_ELECTRONICA"`. Los datos del **emisor** los completa Sifende automáticamente desde el contribuyente y el timbrado configurados. Vos solo pasás los datos del receptor, los ítems y la condición de pago. Ejemplo de FE B2C **innominada** (consumo final hasta Gs. 5.000.000) con un solo ítem gravado al 10%: ```json { "tipoDocumento": "FACTURA_ELECTRONICA", "fechaEmision": "2026-04-27T10:30:00", "tipoEmision": "NORMAL", "numeroEstablecimiento": 1, "puntoExpedicion": 1, "tipoTransaccion": "VENTA_MERCADERIA", "monedaOperacion": "PYG", "receptor": { "tipoContribuyente": "INNOMINADO", "tipoOperacion": "B2C" }, "condicionOperacion": "CONTADO", "condicionPago": { "tipo": "CONTADO", "tipoPago": "EFECTIVO", "monedaPago": "PYG", "montoPago": 150000 }, "items": [ { "codigo": "PROD-A4-75", "descripcion": "Resma de papel A4 75g", "cantidad": 10, "unidadMedida": "UNI", "precioUnitario": 15000, "afectacionTributaria": "GRAVADO", "tasaIVA": 10 } ] } ``` Los montos en guaraníes son **enteros sin decimales**. `15000` representa Gs. 15.000. Ver [Convenciones](/docs/referencia/convenciones). ## Paso 2: Enviá la solicitud [#paso-2-enviá-la-solicitud] ```bash curl -X POST https://api.sifende.com.py/api/v1/documento-electronico \ -H "Authorization: Bearer $SIFENDE_API_KEY" \ -H "Content-Type: application/json" \ -d @factura.json ``` ```typescript const response = await fetch( 'https://api.sifende.com.py/api/v1/documento-electronico', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.SIFENDE_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify(facturaPayload), } ); if (!response.ok) { const problem = await response.json(); throw new Error(`${problem.title}: ${problem.detail}`); } const { id, cdc, estado, statusUrl, kudeUrl } = await response.json(); console.log('CDC emitido:', cdc, ', estado inicial:', estado); ``` ## Paso 3: Guardá la respuesta [#paso-3-guardá-la-respuesta] La respuesta exitosa es **`202 Accepted`** con un body que incluye los identificadores del DE creado: ```json { "id": "550e8400-e29b-41d4-a716-446655440000", "cdc": "01800123451001001000000122026042710000000006", "estado": "PENDIENTE", "tipoDocumento": "FACTURA_ELECTRONICA", "iTiDe": 1, "numeroDocumento": 1, "numeroFormateado": "001-001-0000001", "fechaCreacion": "2026-04-27T10:30:00", "qrUrl": "https://ekuatia.set.gov.py/consultas-test/qr?...", "statusUrl": "https://api.sifende.com.py/api/v1/documento-electronico/status/01800123451001001000000122026042710000000006", "kudeUrl": "https://api.sifende.com.py/api/v1/documento-electronico/01800123451001001000000122026042710000000006/kude" } ``` **`estado: "PENDIENTE"` es lo esperado.** SIFEN procesa el documento de forma asíncrona: el CDC ya es válido, pero la aprobación llega en segundos a minutos. Pasá al Paso 4 para verificar. Guardá `id`, `cdc` y `numeroFormateado` asociados a tu venta. Vas a necesitarlos para: * Consultar el estado de procesamiento en SIFEN (usá `statusUrl` o el `cdc`). * Descargar el KuDE (PDF) cuando esté aprobado (usá `kudeUrl`). * Cancelar el documento si fuera necesario. ## Paso 4: Esperá el resultado de SIFEN [#paso-4-esperá-el-resultado-de-sifen] SIFEN procesa los documentos de forma **asíncrona**. Apenas recibís el CDC, el estado interno es `PENDIENTE` o `EN_LOTE`. El procesamiento tarda habitualmente entre 15 y 60 segundos, pero puede superar los 2 minutos en el ambiente de QA. Implementá polling con timeout de al menos 5 minutos. Los detalles de la estrategia de polling están en [Consultar Estado de un Documento](/docs/guias/consultar-estado). ## Errores frecuentes en este flujo [#errores-frecuentes-en-este-flujo] | Status | Tipo | Causa más común | Cómo resolverlo | | ------ | -------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | | 400 | `validation-error` | Falta un campo obligatorio o un valor está mal formateado | Revisá `errores` en la respuesta | | 400 | `invalid-enum-value` | Valor de enum no reconocido (ej: `"INVOICE"` en `tipoDocumento`) | Revisá `valoresAceptados` en la respuesta | | 404 | `timbrado-not-found` | No hay timbrado configurado para este contribuyente | Cargá el timbrado en el panel | | 422 | SIFEN 1108 | Fecha fin de vigencia del timbrado incorrecta (timbrado vencido o mal configurado) | Renová o corregí la `fechaFin` del timbrado en SET y actualizalo en Sifende | | 401 | (texto plano) | API key inválida o revocada | Rotá la credencial desde el panel, en Configuración → API Keys | Para el listado completo, ver [Manejar Errores](/docs/guias/manejar-errores) y [Rechazos SIFEN](/docs/solucion-problemas/rechazos-sifen). ## Próximos pasos [#próximos-pasos] * ¿Estás facturando a una empresa con RUC? → [Receptor B2B y B2C](/docs/guias/receptor-b2b-b2c). * ¿Necesitás anular o ajustar una FE aprobada? → [Nota de Crédito](/docs/guias/nota-credito). * ¿Querés entregar el comprobante al cliente? → [Descargar KuDE](/docs/guias/descargar-kude). # Guías (/docs/guias) Cada guía está orientada a una tarea concreta. Seguí el orden si estás integrando por primera vez, o saltá a la que necesités. Muchas de estas operaciones también están disponibles vía la [Sifende CLI](/docs/herramientas/sifende-cli), útil para pruebas rápidas. ## Flujo principal [#flujo-principal] 1. [Factura Electrónica](/docs/guias/factura-electronica): emitir una FE completa 2. [Receptor B2B / B2C](/docs/guias/receptor-b2b-b2c): facturar a otro contribuyente 3. [Consultar Estado](/docs/guias/consultar-estado): verificar aprobación SIFEN 4. [Manejar Errores](/docs/guias/manejar-errores): qué hacer cuando algo falla ## Documentos adicionales [#documentos-adicionales] * [Nota de Crédito](/docs/guias/nota-credito) * [Nota de Débito](/docs/guias/nota-debito) ## Operaciones avanzadas [#operaciones-avanzadas] * [Descargar KuDE](/docs/guias/descargar-kude) * [Cancelar Documento](/docs/guias/cancelar-documento) * [Inutilizar Numeración](/docs/guias/inutilizar-numeracion) * [Polling de Resultados](/docs/guias/polling-resultados) * [Moneda Extranjera](/docs/guias/moneda-extranjera) * [Múltiples Establecimientos](/docs/guias/multiples-establecimientos) * [Reintentar Rechazados](/docs/guias/reintentar-rechazados) * [Ir a Producción](/docs/guias/ir-a-produccion) # Inutilizar Numeración (/docs/guias/inutilizar-numeracion) SIFEN exige que la numeración de documentos sea **secuencial y sin huecos**. Cuando se generan brechas (por errores de sistema, pruebas, fallos de emisión), debés inutilizar formalmente esos números para mantener la integridad del timbrado. ## Cuándo inutilizar [#cuándo-inutilizar] Inutilizá una numeración cuando: * Tenés un hueco en la secuencia de un timbrado activo * Un proceso falló al generar el XML y consumió el número * Emitiste documentos en un ambiente de pruebas que reservaron números reales * Cambiaste de timbrado y querés cerrar el rango anterior La inutilización es **irreversible**. Los números marcados como inutilizados quedan registrados en SIFEN para siempre y no podrán reutilizarse. ## Cómo inutilizar un rango [#cómo-inutilizar-un-rango] **Identificá el rango** que querés inutilizar: desde qué número hasta qué número, dentro de un mismo establecimiento y punto de expedición. **Enviá el evento de inutilización** con el rango y motivo: ```bash curl -X POST "https://api.sifende.com.py/api/v1/documento-electronico/inutilizar" \ -H "Authorization: Bearer sk_live_..." \ -H "Content-Type: application/json" \ -d '{ "numeroTimbrado": 12345678, "establecimiento": 1, "puntoExpedicion": 1, "numeroInicio": "0000105", "numeroFin": "0000107", "tipoDocumento": 1, "motivo": "Documentos no emitidos por falla del sistema" }' ``` | Campo | Tipo | Req. | Restricción | | ----------------- | --------- | ---- | ----------------------------------------- | | `numeroTimbrado` | `integer` | Sí | Número de timbrado SET | | `establecimiento` | `integer` | Sí | Número del establecimiento (ej: `1`) | | `puntoExpedicion` | `integer` | Sí | Número del punto de expedición (ej: `1`) | | `numeroInicio` | `string` | Sí | 1-7 caracteres | | `numeroFin` | `string` | Sí | 1-7 caracteres | | `tipoDocumento` | `short` | Sí | Código SIFEN `iTiDe` (1=FE, 5=NCE, 6=NDE) | | `motivo` | `string` | Sí | 5-500 caracteres | | `serie` | `string` | No | Máximo 2 caracteres | **SIFEN procesa el evento** y registra los números como inutilizados. **Continuá facturando normalmente.** El siguiente DE usará el próximo número disponible (108 en el ejemplo). ## Ejemplo en TypeScript [#ejemplo-en-typescript] ```typescript async function inutilizarNumeracion(rango: { numeroTimbrado: number; establecimiento: number; puntoExpedicion: number; numeroInicio: string; numeroFin: string; motivo: string; }) { const response = await fetch( "https://api.sifende.com.py/api/v1/documento-electronico/inutilizar", { method: "POST", headers: { "Authorization": `Bearer ${process.env.SIFENDE_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ tipoDocumento: 1, // 1 = FACTURA_ELECTRONICA (iTiDe) ...rango, }), } ); if (!response.ok) { const error = await response.json(); throw new Error(`Inutilización fallida: ${error.detail}`); } } await inutilizarNumeracion({ numeroTimbrado: 12345678, establecimiento: 1, puntoExpedicion: 1, numeroInicio: "0000105", numeroFin: "0000107", motivo: "Documentos no emitidos por falla del sistema", }); ``` ## Buenas prácticas [#buenas-prácticas] * **Inutilizá pronto.** No dejes huecos abiertos por más de unos días, dificulta auditorías. * **Mantené los rangos chicos.** Inutilizar 3 números es manejable; inutilizar 500 levanta sospechas. * **Documentá el motivo internamente.** El campo `motivo` queda en SIFEN, pero también guardá un registro propio. * **Verificá antes de inutilizar.** Confirmá que los números efectivamente no se emitieron. Si ya hay un DE con ese número, la inutilización fallará. ## Restricciones [#restricciones] | Restricción | Detalle | | ----------------------------- | -------------------------------------------------------------------------- | | Solo números no emitidos | No podés inutilizar un número ya usado en un DE existente | | Mismo establecimiento + punto | El rango debe estar dentro del mismo `establecimiento` y `puntoExpedicion` | | Timbrado activo | El timbrado al que pertenecen los números debe seguir vigente | ## Errores comunes [#errores-comunes] | Error | Causa | Solución | | -------------------------------- | ---------------------------------- | --------------------------------------------- | | `409 evento-inutilizacion-error` | Algún número del rango ya tiene DE | Ajustá el rango excluyendo los números usados | | `400 evento-inutilizacion-error` | `desde` mayor que `hasta` | Verificá el orden del rango | | `400 validation-error` | Tipo de documento inválido | Usá uno de los enums permitidos | ## Próximos pasos [#próximos-pasos] * [Cancelar Documento](/docs/guias/cancelar-documento): para documentos ya emitidos * [Reintentar Rechazados](/docs/guias/reintentar-rechazados): flujo después de un rechazo * [Referencia: Inutilizar](/docs/referencia/documentos-electronicos/inutilizar) # Ir a Producción (/docs/guias/ir-a-produccion) Pasar del entorno de pruebas a producción significa que tus DE empiezan a tener **validez fiscal real**. Errores en producción afectan tu IVA, tus declaraciones y la relación con tus clientes. Acá tenés el checklist antes del go-live. **Una vez en producción, los documentos son legales.** No podés emitir "de prueba" en producción: todo lo que enviás se envía a SIFEN como real. Hacé todas las pruebas en el entorno de pruebas primero (subiendo un certificado con `ambiente=DEV`). **No hay una URL separada para pruebas.** Sifende usa la misma URL base (`https://api.sifende.com.py`) para ambos entornos. El destino real (SIFEN test vs SIFEN producción) lo determina el **certificado** que subas: `ambiente=DEV` enruta a `sifen-test.set.gov.py`, `ambiente=PROD` enruta al SIFEN productivo. Tampoco hay prefijo `sk_test_`: todas las API keys usan el prefijo `sk_live_`. ## Pre-requisitos [#pre-requisitos] Antes de empezar, asegurate de tener: * [ ] Tu integración funcionando 100% en el entorno de pruebas (todos los flujos críticos probados con un certificado `ambiente=DEV`) * [ ] Acceso al portal **MARANGATÚ** de la SET (para timbrado y certificado) * [ ] Acceso al portal **e-Kuatia** (para CSC de producción) * [ ] Certificado digital de **producción** vigente (PKCS12 con clave) * [ ] Timbrado de **producción** otorgado por la SET ## Checklist de migración [#checklist-de-migración] **Obtené el CSC de producción.** Ingresá al portal e-Kuatia con tu certificado, andá a "Códigos de Seguridad", y generá un CSC de producción. Anotá el ID y el código: los necesitás en Sifende. **Subí el certificado de producción** en Sifende: 1. Configuración → Certificado Digital 2. Subí el archivo `.p12` o `.pfx` de producción 3. Ingresá la contraseña del certificado 4. Verificá que la fecha de vencimiento sea correcta ⚠️ El certificado de producción (`ambiente=PROD`) **es distinto** al de pruebas (`ambiente=DEV`); no reuses credenciales. El campo `ambiente` del certificado es lo que determina si Sifende enruta a SIFEN test o a SIFEN producción. **Registrá el timbrado de producción** en Sifende: 1. Configuración → Timbrados 2. Cargá el número de timbrado obtenido en MARANGATÚ 3. Ingresá `establecimiento`, `puntoExpedicion`, rango (`desde`/`hasta`), `fechaInicio` y `fechaFin` 4. Activalo **Creá una API key de producción** en Sifende: 1. Configuración → API Keys 2. Generá una nueva key 3. **Copiala inmediatamente.** Solo se muestra una vez 4. Guardala como secreto (variables de entorno, vault, etc.) ⚠️ Aunque todas las API keys usan el prefijo `sk_live_`, **separá las keys del entorno de pruebas y las de producción**. Manejalas como secretos distintos en tu vault / variables de entorno y nunca las cruces. **Actualizá tu integración:** ```bash # Variables de entorno SIFENDE_BASE_URL=https://api.sifende.com.py SIFENDE_API_KEY=sk_live_... ``` Cambiá la URL base y la API key. Recompilá / redeployá tu aplicación. **Emití tu primer DE de producción** con un cliente conocido (ej: tu propia empresa o un cliente de confianza). Verificá que: * El estado pasa a `APROBADO` * El KuDE se genera correctamente * El DE aparece en el portal e-Kuatia **Monitoreá las primeras 24-48 horas** activamente. Cualquier rechazo masivo indica un problema que hay que detener antes de que escale. ## URL base [#url-base] Sifende usa **una sola URL base** para los dos entornos: ``` https://api.sifende.com.py ``` El destino real lo determina el certificado: | `ambiente` del certificado | SIFEN al que se enruta | Para qué sirve | | -------------------------- | ----------------------- | --------------------------- | | `DEV` | `sifen-test.set.gov.py` | Pruebas, sin validez fiscal | | `PROD` | SIFEN producción | Documentos legales reales | > No hay una URL `qa.sifende.com.py` ni un prefijo `sk_test_`. Si tu integración apuntaba a una URL distinta a la oficial, actualizala antes del go-live. ## Checklist crítico antes del go-live [#checklist-crítico-antes-del-go-live] ### Credenciales [#credenciales] * [ ] API key de producción guardada en variables de entorno (no en código) * [ ] Certificado de producción subido y vigente * [ ] Timbrado de producción registrado y activo * [ ] CSC de producción configurado ### Manejo de errores [#manejo-de-errores] * [ ] Reintentos con backoff exponencial para errores de red (timeouts, 5xx) * [ ] Manejo de `429 Too Many Requests` * [ ] Polling de estado implementado (ver [Polling de Resultados](/docs/guias/polling-resultados)) * [ ] Persistís el `id` y `cdc` de cada DE antes de pollear * [ ] Loggeás todos los rechazos con código y motivo ### Calidad de datos [#calidad-de-datos] * [ ] Validación de RUC en el frontend (formato `{número}-{dv}`) * [ ] Montos en PYG son enteros sin decimales * [ ] Fechas en formato ISO 8601 sin timezone (`2026-04-15T10:30:00`) * [ ] Campos B2B completos cuando corresponde (razón social, dirección, tipo contribuyente) * [ ] Solo usás `condicionPago: CONTADO` por ahora; el soporte para `CREDITO` está en desarrollo **Solo `CONTADO` está completamente soportado** en producción. La condición de pago `CREDITO` (cuotas, plazos) está en el roadmap pero no es estable hoy. Si tu negocio requiere ventas a crédito, esperá la próxima versión o contactá a soporte. ### Flujos probados [#flujos-probados] * [ ] Factura Electrónica B2C (con receptor INNOMINADO) * [ ] Factura Electrónica B2B * [ ] Nota de Crédito sobre una FE aprobada * [ ] Nota de Débito sobre una FE aprobada * [ ] Cancelación de un documento aprobado * [ ] Inutilización de rango de numeración * [ ] Reemisión después de un rechazo * [ ] Descarga de KuDE PDF ### Notificaciones [#notificaciones] * [ ] Si activaste email al receptor, probaste que llega correctamente * [ ] El template de email tiene tu branding y datos de contacto ### Seguridad [#seguridad] * [ ] La API key NO está en el código fuente ni en repositorios git * [ ] Tenés un plan de rotación de API keys * [ ] El certificado digital está almacenado de forma segura * [ ] Configuraste alertas para errores 5xx y 401 ### Operaciones [#operaciones] * [ ] Tenés un dashboard para ver tasa de aprobación / rechazo * [ ] Tenés un plan para días de baja de SIFEN (rara pero pasa) * [ ] Documentaste internamente el procedimiento para incidentes ## Pruebas finales recomendadas [#pruebas-finales-recomendadas] Antes del go-live oficial, ejecutá estos casos en producción con datos reales pero a baja escala: 1. **DE B2C de monto bajo.** Verificá que llega `APROBADO` y el KuDE es válido. 2. **DE B2B con cliente conocido.** Confirmá que el cliente recibe el documento por email (si aplica) y puede consultarlo en e-Kuatia. 3. **Cancelación.** Cancelá el primer DE de prueba y emití uno nuevo. 4. **Verificación cruzada.** Entrá al portal e-Kuatia y confirmá que tus DE aparecen ahí. ## Si algo sale mal [#si-algo-sale-mal] * **Rechazos masivos:** detené la emisión, revisá logs, contactá soporte * **Timeout:** ver [Polling de Resultados](/docs/guias/polling-resultados) * **Error de certificado:** ver [Certificado Digital](/docs/solucion-problemas/certificado-digital) * **Otros:** ver [Solución de Problemas](/docs/solucion-problemas) ## Soporte [#soporte] ¿Necesitás ayuda con el go-live? * Email: [soporte@sifende.com.py](mailto:soporte@sifende.com.py) * [Página de soporte](/docs/solucion-problemas/soporte) * [FAQ](/docs/solucion-problemas/faq) ## Próximos pasos [#próximos-pasos] * [Polling de Resultados](/docs/guias/polling-resultados): patrón asíncrono recomendado * [Manejar Errores](/docs/guias/manejar-errores): error handling de producción * [Múltiples Establecimientos](/docs/guias/multiples-establecimientos): si tenés varias sucursales # Manejar Errores (/docs/guias/manejar-errores) Esta guía explica cómo distinguir los distintos tipos de error que recibís de Sifende, qué hacer con cada uno y cuándo (no) reintentar. ## Dos fuentes de error muy distintas [#dos-fuentes-de-error-muy-distintas] Mezclarlas en un mismo `catch` es una de las primeras causas de bugs en integraciones SIFEN. | Fuente | Cuándo aparece | Cómo se ve | | ----------------------------- | ------------------------------------------------------ | --------------------------------------------------------- | | **API Sifende** | Inmediato, en el HTTP response | Status 400/401/403/404/422 + Problem Details JSON | | **SIFEN (rechazo asíncrono)** | Después del polling, una vez que SIFEN procesa el lote | `estado: "RECHAZADO"` + `mensajeRechazo` con código SIFEN | ## Errores de la API Sifende (síncronos) [#errores-de-la-api-sifende-síncronos] Sigan el formato **RFC 9457 Problem Details** (excepto autenticación): ```json { "type": "https://sifende.com.py/probs/validation-error", "title": "Error de validación", "status": 400, "detail": "La solicitud contiene 2 error(es) de validación", "errores": { "receptor.numeroDocumento": "Número de documento es obligatorio", "items[0].precioUnitario": "El precio no puede ser negativo" } } ``` ### Cómo manejarlos en código [#cómo-manejarlos-en-código] ```typescript const res = await fetch('https://api.sifende.com.py/api/v1/documento-electronico', { method: 'POST', headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }); if (!res.ok) { const problem = await res.json(); if (problem.type?.includes('validation-error')) { for (const [campo, mensaje] of Object.entries(problem.errores ?? {})) { console.error(`Campo inválido: ${campo} → ${mensaje}`); } throw new ValidacionError(problem); } if (problem.type?.includes('invalid-enum-value')) { console.error(`Valor inválido en ${problem.campo}. Aceptados:`, problem.valoresAceptados); throw new EnumError(problem); } throw new ApiError(problem); } const cdc = await res.text(); ``` ### Tabla rápida de tipos [#tabla-rápida-de-tipos] | Status | Tipo | Acción | | ------ | ---------------------------------------- | ------------------------------------------------------------ | | 400 | `validation-error` | Revisar `errores` por campo, corregir y reenviar | | 400 | `invalid-enum-value` | Usar uno de `valoresAceptados` | | 400 | `invalid-format` | Corregir formato (fecha, RUC, CDC) | | 401 | (texto plano) | API key inválida/expirada. Rotá la credencial desde el panel | | 403 | `access-denied` | El usuario/key no tiene acceso a ese contribuyente | | 404 | `*-not-found` | Recurso inexistente. Verificar IDs | | 409 | `duplicate-*` | Ya existe. Usar el existente o cambiar identificador | | 422 | `documento-electronico-generation-error` | Error de compliance SIFEN al generar el XML | Catálogo completo en [Errores](/docs/referencia/errores). ## Rechazos SIFEN (asíncronos) [#rechazos-sifen-asíncronos] Después de emitir, el documento queda en `PENDIENTE` o `EN_LOTE`. Una vez procesado, SIFEN devuelve un código con la respuesta. Los más frecuentes en producción: | Código SIFEN | Significado | Cómo corregirlo | | ------------ | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | | 1108 | Fecha fin de vigencia del timbrado incorrecta (timbrado vencido o mal configurado) | Renová o corregí la `fechaFin` del timbrado en SET y actualizalo en Sifende | | 1302 | Falta `tipoContribuyente` del receptor para B2B | Completar `tipoContribuyente: "CONTRIBUYENTE"` | | 1303 | Tipo de contribuyente receptor inválido (informado cuando es `NO_CONTRIBUYENTE`) | No incluyas `tipoContribuyente` en B2C no contribuyente | | 1304 | Falta `numeroDocumento` (RUC) para receptor contribuyente | Completar el RUC del receptor | | 1305 | RUC del receptor no requerido (informado para `NO_CONTRIBUYENTE`) | Eliminar `numeroDocumento` en B2C no contribuyente | | 1306 | RUC del receptor inexistente en Marangatu (RUC no registrado en SET) | Confirmar el RUC con tu cliente | | 1309 | DV del RUC del receptor incorrecto | Verificar `digitoVerificador` | | 2026 | CDC asociado no existe o no está aprobado | Solo emitir NCE/NDE sobre FE en estado `APROBADO` | Tabla completa: [Rechazos SIFEN](/docs/solucion-problemas/rechazos-sifen). ### Cómo leer el rechazo [#cómo-leer-el-rechazo] El campo `mensajeRechazo` del status response contiene el código y la descripción: ```typescript const resultado = await esperarResultadoSIFEN(cdc); if (resultado.estado === 'RECHAZADO') { // Ej: "1108 - Timbrado vencido" const codigo = resultado.mensajeRechazo?.split(' ')[0]; log.warn({ cdc, codigo, motivo: resultado.mensajeRechazo }, 'DE rechazado por SIFEN'); } ``` ## Cuándo reintentar (y cuándo no) [#cuándo-reintentar-y-cuándo-no] | Situación | ¿Reintentar? | Cómo | | ------------------------------------- | :----------: | ----------------------------------------------------------------------------------------- | | 5xx temporal de la API Sifende | ✅ | Backoff exponencial (1s, 2s, 4s…), máximo 3 intentos | | 401 / 403 | ❌ | Arreglá credenciales, no reintentes con la misma | | 400 `validation-error` | ❌ | Corregí los datos antes de reenviar | | 404 `documento-electronico-not-found` | ❌ | El CDC no existe; no aparecerá reintentando | | Rechazo SIFEN | ⚠️ | **Emitir un DE nuevo** con los datos corregidos. El original queda rechazado para siempre | **Nunca reintentes el mismo request rechazado por SIFEN.** Cada DE consume un número del rango de timbrado: el rechazado ya gastó ese número y no se recupera. Para evitar gastar números, [Inutilizar Numeración](/docs/guias/inutilizar-numeracion). ## Recomendaciones de logging [#recomendaciones-de-logging] En tu integración, logueá siempre: * `cdc`, para correlacionar con tu sistema. * `estado` y `mensajeRechazo` cuando consultes status. * `type`, `title`, `status` y `detail` del Problem Details ante errores. * El **request body completo** en errores 4xx (excepto la API key); facilita reproducir el problema. ```typescript log.error({ cdc, estado: resultado.estado, mensajeRechazo: resultado.mensajeRechazo, }, 'DE rechazado'); ``` ## Próximos pasos [#próximos-pasos] * Para reintentar después de un rechazo, ver [Reintentar Rechazados](/docs/guias/reintentar-rechazados). * Para ver el catálogo de códigos SIFEN, ver [Rechazos SIFEN](/docs/solucion-problemas/rechazos-sifen). * Para problemas con certificados y firmado, ver [Certificado Digital](/docs/solucion-problemas/certificado-digital). # Facturar en Moneda Extranjera (/docs/guias/moneda-extranjera) SIFEN permite emitir documentos en moneda extranjera (USD, EUR, BRL, ARS, etc.) siempre que se incluya el **tipo de cambio** del día y el sistema reporte los equivalentes en guaraníes. Esta guía explica cómo configurarla correctamente. ## Conceptos clave [#conceptos-clave] | Campo API | Descripción | Ejemplo | | ----------------- | -------------------------------------------------------------------------- | ------------------------- | | `monedaOperacion` | Código ISO de la moneda de operación | `USD`, `EUR` | | `tipoCambio` | Tipo de cambio: cantidad de PYG por **una unidad** de la moneda extranjera | `7300` (1 USD = 7300 PYG) | El tipo de cambio debe ser el **publicado por el Banco Central del Paraguay (BCP)** para la fecha de emisión del documento. Usar un tipo de cambio incorrecto puede causar rechazos o problemas fiscales. ## Cómo obtener el tipo de cambio [#cómo-obtener-el-tipo-de-cambio] * **BCP (oficial):** [https://www.bcp.gov.py](https://www.bcp.gov.py) publica diariamente las cotizaciones * Usá la cotización **comprador** o **vendedor** según tu política contable (lo más común: vendedor para ventas) * Guardá el tipo de cambio usado junto con el DE para auditoría ## Ejemplo: factura en USD [#ejemplo-factura-en-usd] ```json { "tipoDocumento": "FACTURA_ELECTRONICA", "fechaEmision": "2026-04-15T10:30:00", "tipoEmision": "NORMAL", "numeroEstablecimiento": 1, "puntoExpedicion": 1, "monedaOperacion": "USD", "tipoCambio": 7300, "tipoTransaccion": "PRESTACION_SERVICIOS", "condicionOperacion": "CONTADO", "receptor": { "tipoContribuyente": "CONTRIBUYENTE", "tipoOperacion": "B2B", "numeroDocumento": "80012345", "digitoVerificador": "1", "nombreRazonSocial": "Comercial Guaraní S.A." }, "condicionPago": { "tipo": "CONTADO", "tipoPago": "TRANSFERENCIA", "monedaPago": "USD", "montoPago": 1200 }, "items": [ { "codigo": "SRV-001", "descripcion": "Licencia de software anual", "cantidad": 1, "unidadMedida": "UNI", "precioUnitario": 1200, "afectacionTributaria": "GRAVADO", "tasaIVA": 10 } ] } ``` En este caso: * El precio del ítem (`1200`) está en **USD** * Sifende calcula automáticamente el equivalente en PYG: `1200 × 7300 = 8.760.000 PYG` * El KuDE muestra ambos valores: USD (operación) y PYG (referencia) ## Cálculo de IVA [#cálculo-de-iva] El IVA siempre se calcula sobre el monto en moneda de operación (USD en este caso), pero también se reporta su equivalente en PYG: | Concepto | USD | PYG | | --------- | ------------ | ------------- | | Subtotal | 1.090,91 | 7.963.636 | | IVA 10% | 109,09 | 796.364 | | **Total** | **1.200,00** | **8.760.000** | Sifende hace estos cálculos automáticamente; solo enviá los precios en moneda extranjera. ## Modos de tipo de cambio [#modos-de-tipo-de-cambio] ### GLOBAL (recomendado) [#global-recomendado] Un solo tipo de cambio para toda la factura. Más simple y común. Enviá `monedaOperacion` + `tipoCambio` a nivel de documento: ```json { "monedaOperacion": "USD", "tipoCambio": 7300 } ``` ### POR\_ITEM [#por_item] Cada ítem tiene su propio tipo de cambio. Usado en operaciones complejas con commodities. El `tipoCambio` se envía en cada ítem: ```json { "monedaOperacion": "USD", "items": [ { "descripcion": "Producto importado", "tipoCambio": 7300, "precioUnitario": 100 } ] } ``` ## Restricciones SIFEN [#restricciones-sifen] | Restricción | Detalle | | --------------------------------------- | ------------------------------------------------------ | | Tipo de cambio entero positivo | No puede ser 0 ni negativo | | Coherencia con la fecha | Usá el TC del día de emisión, no de hoy | | Operaciones B2C en USD | Permitidas pero infrecuentes; confirmá con tu contador | | Moneda extranjera + receptor INNOMINADO | Permitido para FE, no para Notas de Crédito | ## Ejemplo en TypeScript [#ejemplo-en-typescript] ```typescript async function emitirFacturaUSD(montoUSD: number, ruc: string, razonSocial: string) { const tipoCambio = await obtenerCotizacionBCP(); // tu integración con BCP const response = await fetch( "https://api.sifende.com.py/api/v1/documento-electronico", { method: "POST", headers: { "Authorization": `Bearer ${process.env.SIFENDE_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ tipoDocumento: "FACTURA_ELECTRONICA", fechaEmision: new Date().toISOString().slice(0, 19), tipoEmision: "NORMAL", numeroEstablecimiento: 1, puntoExpedicion: 1, monedaOperacion: "USD", tipoCambio, tipoTransaccion: "PRESTACION_SERVICIOS", condicionOperacion: "CONTADO", receptor: { tipoContribuyente: "CONTRIBUYENTE", tipoOperacion: "B2B", numeroDocumento: ruc.split("-")[0], digitoVerificador: ruc.split("-")[1], nombreRazonSocial: razonSocial, }, condicionPago: { tipo: "CONTADO", tipoPago: "TRANSFERENCIA", monedaPago: "USD", montoPago: montoUSD }, items: [{ codigo: "SRV-001", descripcion: "Servicio", cantidad: 1, unidadMedida: "UNI", precioUnitario: montoUSD, afectacionTributaria: "GRAVADO", tasaIVA: 10, }], }), } ); return response.json(); } ``` ## Próximos pasos [#próximos-pasos] * [Factura Electrónica](/docs/guias/factura-electronica): guía base de FE * [Modelo: Factura Electrónica](/docs/referencia/modelos/factura-electronica): schema completo * [Items y IVA](/docs/conceptos/items-iva): cómo se calcula el IVA # Múltiples Establecimientos (/docs/guias/multiples-establecimientos) Si tu negocio tiene varias sucursales o múltiples cajas/terminales por sucursal, SIFEN exige distinguir cada una mediante **número de establecimiento** y **punto de expedición**. Esta guía explica cómo configurarlos y usarlos correctamente. ## Conceptos [#conceptos] | Concepto | Descripción | Formato API | Formato visual (CDC/KuDE) | | ----------------------- | ------------------------------------------------------------- | ------------------------------------------------- | ------------------------------------------ | | **Establecimiento** | Ubicación física (sucursal, depósito, local) | `numeroEstablecimiento`: integer (`1`, `2`, `10`) | 3 dígitos con padding: `001`, `002`, `010` | | **Punto de expedición** | Caja, terminal o canal dentro del establecimiento | `puntoExpedicion`: integer (`1`, `2`) | 3 dígitos con padding: `001`, `002` | | **Timbrado** | Autorización SET para emitir DE en una combinación específica | Único por (establecimiento + punto) | n/a | La API recibe `numeroEstablecimiento` y `puntoExpedicion` como **enteros** (`1`, no `"001"`). Sifende se encarga del padding a 3 dígitos para el CDC, KuDE y formato visual. Cada combinación `establecimiento + punto` necesita su **propio rango de timbrado** otorgado por la SET. ## Ejemplo de estructura [#ejemplo-de-estructura] | Sucursal | `numeroEstablecimiento` | `puntoExpedicion` | Timbrado | | ------------------------ | ----------------------- | -------------------------- | -------- | | Casa Central (Asunción) | `1` | `1` (Caja 1), `2` (Caja 2) | 12345678 | | Sucursal Encarnación | `2` | `1` (Caja única) | 12345679 | | Sucursal Ciudad del Este | `3` | `1` (Caja 1), `2` (Online) | 12345680 | ## Configurar en Sifende [#configurar-en-sifende] **Andá a Configuración → Timbrados** en el panel de Sifende. **Creá un timbrado por combinación.** Ingresá: * Número de timbrado (otorgado por MARANGATÚ) * Establecimiento (entero, ej. `1`) * Punto de expedición (entero, ej. `1`) * Rango de números (desde / hasta) * Fecha inicio y fecha fin * Tipo de documento (FE, NCE, NDE, etc.) **Activalo.** Sifende valida que el timbrado esté vigente antes de permitir emisión. **Repetí** para cada sucursal y punto. ## Emitir DE desde un establecimiento específico [#emitir-de-desde-un-establecimiento-específico] En el body del request, pasá `numeroEstablecimiento` y `puntoExpedicion`: ```json { "tipoDocumento": "FACTURA_ELECTRONICA", "numeroEstablecimiento": 2, "puntoExpedicion": 1, "fechaEmision": "2026-04-15T10:30:00", "receptor": { "tipoContribuyente": "CONTRIBUYENTE", "tipoOperacion": "B2B", "numeroDocumento": "80012345", "digitoVerificador": "1", "nombreRazonSocial": "Comercial Guaraní S.A." }, "condicionOperacion": "CONTADO", "condicionPago": { "tipo": "CONTADO", "tipoPago": "EFECTIVO", "monedaPago": "PYG", "montoPago": 150000 }, "items": [ { "codigo": "PROD-X", "descripcion": "Producto X", "cantidad": 1, "unidadMedida": "UNI", "precioUnitario": 150000, "afectacionTributaria": "GRAVADO", "tasaIVA": 10 } ] } ``` Si no enviás `numeroEstablecimiento` ni `puntoExpedicion`, Sifende usa los defaults: `1` y `1`. ## Cómo aparecen en el CDC [#cómo-aparecen-en-el-cdc] El CDC (Código de Control de 44 dígitos) incluye el establecimiento y punto: ``` 01 80012345 6 110 010 001 0000000012 026041510 000000001 | | | | | | | | | | | └── Punto: 001 | | | | └── Establecimiento: 010 | | | └── Tipo emisión | | └── Tipo documento | └── DV del RUC └── RUC del emisor ``` ## Casos de uso comunes [#casos-de-uso-comunes] ### Sucursal nueva [#sucursal-nueva] Cuando abrís una sucursal nueva: 1. Solicitá un nuevo timbrado en MARANGATÚ con un establecimiento nuevo (ej. `2`) 2. Cargalo en Sifende 3. Empezá a emitir con `numeroEstablecimiento: 2` ### Caja online + caja física [#caja-online--caja-física] Tu sucursal tiene una caja física (POS) y un canal online: * `numeroEstablecimiento: 1`, `puntoExpedicion: 1` → POS físico * `numeroEstablecimiento: 1`, `puntoExpedicion: 2` → ventas online Cada uno tiene su propio rango de números, evitando conflictos de numeración. ### Empleado móvil [#empleado-móvil] Vendedores externos que emiten desde tablets: * Asignales un establecimiento + punto único * Sus DE se identifican fácilmente para auditoría ## Buenas prácticas [#buenas-prácticas] * **Reservá `1/1` (establecimiento/punto) para casa central.** Es el default y simplifica integraciones simples. * **Usá puntos diferentes para canales diferentes.** Online, POS, móvil: cada uno con su punto. * **Mantené timbrados activos.** Si un timbrado vence, la sucursal queda inoperativa. * **Loggeá `numeroEstablecimiento` y `puntoExpedicion` en cada DE.** Facilita reportes por sucursal. ## Errores comunes [#errores-comunes] | Error | Causa | Solución | | ----------------------- | ------------------------------------------------- | -------------------------------------------- | | `1108 timbrado vencido` | El timbrado de ese establecimiento + punto venció | Renová en MARANGATÚ y actualizalo en Sifende | | `timbrado-not-found` | No hay timbrado configurado para esa combinación | Cargá el timbrado en el panel | | Numeración duplicada | Reusaste un número ya emitido | Verificá el rango activo del timbrado | ## Próximos pasos [#próximos-pasos] * [Timbrado y Numeración](/docs/conceptos/timbrado-numeracion): concepto base * [CDC](/docs/conceptos/cdc): estructura del CDC * [Ir a Producción](/docs/guias/ir-a-produccion): checklist de configuración # Nota de Crédito Electrónica (/docs/guias/nota-credito) La **Nota de Crédito Electrónica (NCE)** se emite para revertir, descontar o anular una Factura Electrónica que ya fue aprobada por SIFEN. No reemplaza a la FE original; la complementa. ## ¿Cuándo emitir una NCE? [#cuándo-emitir-una-nce] Emití una NCE para: * Devoluciones totales o parciales de mercadería ya facturada. * Descuentos aplicados después de la emisión de la FE. * Anular una FE aprobada por error en datos no críticos (la FE original sigue existiendo en SIFEN). La NCE **solo aplica a documentos en estado `APROBADO`**. Si tu FE fue rechazada por SIFEN, no necesitás (ni podés) emitir una NCE: corregí los datos y emití una nueva FE. Ver [Reintentar Rechazados](/docs/guias/reintentar-rechazados). ## Diferencias clave con una FE [#diferencias-clave-con-una-fe] | Aspecto | FE | NCE | | ------------------- | --------------------- | --------------------------------------------- | | `tipoDocumento` | `FACTURA_ELECTRONICA` | `NOTA_DE_CREDITO_ELECTRONICA` | | `documentoAsociado` | Opcional | **Obligatorio** (referencia a la FE original) | | `motivoEmision` | No aplica | **Obligatorio** (string enum) | | `tipoTransaccion` | Obligatorio | No se envía (lo hereda de la FE original) | | Receptor innominado | Permitido | **No permitido** (siempre identificado) | | `condicionPago` | Obligatoria | No aplica | ## Restricción importante: receptor identificado [#restricción-importante-receptor-identificado] La NCE **no acepta receptor innominado** (NT-010 / NT-023). Si la FE original fue innominada (consumo final), igual debés identificar al receptor en la NCE con cédula o RUC. ## Paso 1: Identificá la FE a referenciar [#paso-1-identificá-la-fe-a-referenciar] Necesitás el **CDC de la FE original** y conocer el motivo de emisión. Los motivos válidos para NCE son: | Valor | Motivo | | -------------- | ----------------------------------------- | | `DEVOLUCION` | Devolución total o parcial de mercadería | | `DESCUENTO` | Descuento aplicado posterior a la emisión | | `BONIFICACION` | Bonificación comercial | | `PRECIO` | Ajuste de precio | | `otros` | Otros motivos no contemplados | `motivoEmision` es un **string enum**, no un código numérico. La API valida contra estos valores exactos. ## Paso 2: Armá el request [#paso-2-armá-el-request] ```json { "tipoDocumento": "NOTA_DE_CREDITO_ELECTRONICA", "fechaEmision": "2026-04-27T14:00:00", "tipoEmision": "NORMAL", "numeroEstablecimiento": 1, "puntoExpedicion": 1, "monedaOperacion": "PYG", "motivoEmision": "DEVOLUCION", "receptor": { "tipoContribuyente": "NO_CONTRIBUYENTE", "tipoOperacion": "B2C", "tipoDocumento": "CEDULA_PARAGUAYA", "numeroDocumento": "1234567", "nombreRazonSocial": "Juan Pérez" }, "documentoAsociado": { "tipo": "ELECTRONICO", "cdc": "01800123451001001000000122026042710000000006" }, "items": [ { "codigo": "PROD-A4-75", "descripcion": "Resma de papel A4 75g - devolución", "cantidad": 2, "unidadMedida": "UNI", "precioUnitario": 15000, "afectacionTributaria": "GRAVADO", "tasaIVA": 10 } ] } ``` ## Paso 3: Enviá la NCE [#paso-3-enviá-la-nce] ```bash curl -X POST https://api.sifende.com.py/api/v1/documento-electronico \ -H "Authorization: Bearer $SIFENDE_API_KEY" \ -H "Content-Type: application/json" \ -d @nota-credito.json ``` Respuesta exitosa: **`202 Accepted`** con un body `DocumentoElectronicoEmisionResponseDTO` que incluye el `id`, el `cdc`, `estado: "PENDIENTE"`, `numeroFormateado`, `qrUrl`, `statusUrl` y `kudeUrl`. Guardalos igual que con la FE. ## Paso 4: Confirmá la aprobación [#paso-4-confirmá-la-aprobación] Igual que con la FE, esperá el procesamiento asíncrono de SIFEN. Ver [Consultar Estado](/docs/guias/consultar-estado). ## Errores frecuentes [#errores-frecuentes] | Status | Tipo | Causa | Solución | | ------ | ------------------------- | ------------------------------------------------------ | ------------------------------------------------------------------ | | 400 | `validation-error` | Falta `motivoEmision` o `documentoAsociado` | Completar ambos campos | | 400 | `invalid-enum-value` | `motivoEmision` no es uno de los enums válidos | Usar `DEVOLUCION`, `DESCUENTO`, `BONIFICACION`, `PRECIO` u `otros` | | 422 | SIFEN 2026 | CDC referenciado no existe en SIFEN o no está aprobado | Verificar que la FE original esté `APROBADO` | | 422 | SIFEN (receptor inválido) | Se intentó usar receptor innominado | Identificar al receptor con cédula o RUC | ## Próximos pasos [#próximos-pasos] * ¿Necesitás cobrar más por la operación original? → [Nota de Débito](/docs/guias/nota-debito). * ¿La FE estaba dirigida a una empresa? → [Receptor B2B y B2C](/docs/guias/receptor-b2b-b2c) para estructurar el receptor. * Si SIFEN rechaza la NCE, consultá [Manejar Errores](/docs/guias/manejar-errores). # Nota de Débito Electrónica (/docs/guias/nota-debito) La **Nota de Débito Electrónica (NDE)** se emite para agregar cargos a una Factura Electrónica ya aprobada: intereses, ajustes por diferencias de cambio, fletes facturados después, recargos por mora, etc. ## ¿Cuándo emitir una NDE? [#cuándo-emitir-una-nde] Emití una NDE cuando necesitás **incrementar** el monto cobrado al receptor sobre una FE ya aprobada. Algunos casos típicos: * Intereses por mora. * Ajustes positivos por diferencia de cambio en facturas en moneda extranjera. * Fletes o servicios adicionales no incluidos en la FE original. * Recargos pactados después de la emisión. La NDE **solo aplica a documentos en estado `APROBADO`**. Si vas a corregir una FE rechazada, emití una nueva FE en lugar de una NDE. ## Estructura [#estructura] La NDE usa el mismo patrón que la NCE: requiere `documentoAsociado` con el CDC de la FE original y un `motivoEmision`. La diferencia es que el efecto es **aumentar** el monto, no descontarlo. | Aspecto | FE | NDE | | ------------------- | --------------------- | ----------------------------------------- | | `tipoDocumento` | `FACTURA_ELECTRONICA` | `NOTA_DE_DEBITO_ELECTRONICA` | | `documentoAsociado` | Opcional | **Obligatorio** (CDC de la FE original) | | `motivoEmision` | No aplica | **Obligatorio** (string enum) | | `tipoTransaccion` | Obligatorio | No se envía (lo hereda de la FE original) | | Receptor innominado | Permitido | **No permitido** | ## Restricción de receptor [#restricción-de-receptor] Igual que la NCE, la NDE **no admite receptor innominado**. Si la FE original era innominada, identificá al receptor con cédula o RUC en la NDE. ## Motivos válidos de emisión [#motivos-válidos-de-emisión] | Valor | Motivo | | ------------------ | -------------------------------------------------- | | `INTERES` | Intereses por mora o financiamiento | | `GASTO_TRANSPORTE` | Gastos de transporte / fletes facturados después | | `AUMENTO_PRECIO` | Aumento de precio sobre lo facturado originalmente | | `otros` | Otros motivos no contemplados | `motivoEmision` es un **string enum**, no un código numérico. La API valida contra estos valores exactos. ## Paso 1: Armá el request [#paso-1-armá-el-request] Ejemplo de NDE por intereses sobre una FE B2B aprobada: ```json { "tipoDocumento": "NOTA_DE_DEBITO_ELECTRONICA", "fechaEmision": "2026-04-27T16:15:00", "tipoEmision": "NORMAL", "numeroEstablecimiento": 1, "puntoExpedicion": 1, "monedaOperacion": "PYG", "motivoEmision": "INTERES", "receptor": { "tipoContribuyente": "CONTRIBUYENTE", "tipoOperacion": "B2B", "numeroDocumento": "80012345", "digitoVerificador": "1", "nombreRazonSocial": "Comercial Guaraní S.A." }, "documentoAsociado": { "tipo": "ELECTRONICO", "cdc": "01800123451001001000000122026042710000000006" }, "items": [ { "codigo": "INT-MORA", "descripcion": "Intereses por mora - Factura 001-001-0000123", "cantidad": 1, "unidadMedida": "UNI", "precioUnitario": 75000, "afectacionTributaria": "GRAVADO", "tasaIVA": 10 } ] } ``` ## Paso 2: Enviá la NDE [#paso-2-enviá-la-nde] ```bash curl -X POST https://api.sifende.com.py/api/v1/documento-electronico \ -H "Authorization: Bearer $SIFENDE_API_KEY" \ -H "Content-Type: application/json" \ -d @nota-debito.json ``` Respuesta exitosa: **`202 Accepted`** con un body `DocumentoElectronicoEmisionResponseDTO` (`id`, `cdc`, `estado: "PENDIENTE"`, `numeroFormateado`, `qrUrl`, `statusUrl`, `kudeUrl`). Guardá los identificadores como en la FE. ## Paso 3: Verificá el estado [#paso-3-verificá-el-estado] El procesamiento es asíncrono. Aplicá la misma estrategia de polling que para FE y NCE; ver [Consultar Estado](/docs/guias/consultar-estado). ## Errores frecuentes [#errores-frecuentes] | Status | Tipo | Causa | Solución | | ------ | -------------------- | ---------------------------------------------- | -------------------------------------------------------------- | | 400 | `validation-error` | Falta `documentoAsociado` o `motivoEmision` | Completar ambos campos | | 400 | `invalid-enum-value` | `motivoEmision` no es uno de los enums válidos | Usar `INTERES`, `GASTO_TRANSPORTE`, `AUMENTO_PRECIO` u `otros` | | 422 | SIFEN (CDC asociado) | CDC referenciado no existe o no está aprobado | Confirmar estado de la FE original | | 422 | SIFEN (receptor) | Se intentó usar receptor innominado | Identificar al receptor | ## Próximos pasos [#próximos-pasos] * ¿Necesitás revertir un cargo? → [Nota de Crédito](/docs/guias/nota-credito). * ¿Vas a entregar el comprobante al cliente? → [Descargar KuDE](/docs/guias/descargar-kude). * ¿Tenés errores de receptor? → [Receptor B2B y B2C](/docs/guias/receptor-b2b-b2c). # Polling de Resultados SIFEN (/docs/guias/polling-resultados) La emisión de documentos a SIFEN es **asíncrona**. Cuando hacés `POST /documento-electronico`, Sifende valida y firma el XML, lo envía a SIFEN en un lote, y la respuesta inicial es `PENDIENTE`. SIFEN procesa los lotes en su backend y devuelve el resultado segundos o minutos después. ## Por qué es asíncrono [#por-qué-es-asíncrono] SIFEN procesa documentos en lotes. Esto permite: * Throughput alto (miles de DE/minuto) * Validaciones complejas de cada documento * Resiliencia ante picos de carga El precio: **el resultado no es inmediato**. Tu integración debe consultar el estado periódicamente hasta obtener un veredicto final (`APROBADO` o `RECHAZADO`). ## Estados del DE [#estados-del-de] ``` PENDIENTE → EN_LOTE → ENVIADO → APROBADO ← estado terminal exitoso → RECHAZADO ← estado terminal con error SIFEN → ERROR ← falla técnica de envío/procesamiento APROBADO → CANCELADO ← después de un evento de cancelación DESCONOCIDO ← caso raro de inconsistencia ``` Los estados del DE (enum exacto del backend): `PENDIENTE`, `EN_LOTE`, `ENVIADO`, `APROBADO`, `RECHAZADO`, `ERROR`, `CANCELADO`, `DESCONOCIDO`. ## Estados del lote (interno) [#estados-del-lote-interno] Sifende agrupa los DE en lotes para enviarlos a SIFEN. El lote pasa por: ``` PREPARADO → INTENTANDO → ENVIADO → PROCESADO ← éxito → ERROR ← falló al procesar → FALLIDO ← falló al enviar ``` Estados del lote (enum exacto): `PREPARADO`, `INTENTANDO`, `ENVIADO`, `PROCESADO`, `ERROR`, `FALLIDO`. No necesitás pollear el lote: hacelo directamente sobre el DE por su CDC. ## Flujo completo [#flujo-completo] **Emitís el DE.** La respuesta incluye `id` y `cdc`, con estado `PENDIENTE`. ```json { "id": "550e8400-e29b-41d4-a716-446655440000", "cdc": "01800123451001001000000122026042710000000006", "estado": "PENDIENTE" } ``` **Persistí el `id` y el `cdc`** en tu base de datos antes de empezar a hacer polling. Si tu proceso crashea, vas a poder retomar el polling desde donde quedó. **Consultás el estado** con `GET /api/v1/documento-electronico/status/:cdc` repetidamente, con backoff. Respuesta: ```json { "cdc": "01800123451001001000000122026042710000000006", "estado": "APROBADO", "iTiDe": 1, "numeroDocumento": 1, "fechaCreacion": "2026-04-27T10:30:00", "protocoloAutorizacion": "01202604150000123456", "mensajeRechazo": null } ``` `numeroDocumento` es **`Long`** (entero). El formato `"NNN-NNN-NNNNNNN"` se devuelve como `numeroFormateado` en la respuesta de **emisión**: son campos separados, no los confundas. **Cortás el polling** cuando el estado sea `APROBADO`, `RECHAZADO`, `ERROR` o `CANCELADO` (estados terminales). ## Estrategia de backoff exponencial [#estrategia-de-backoff-exponencial] | Intento | Espera antes de consultar | | ------- | ------------------------- | | 1 | 2s | | 2 | 3s | | 3 | 5s | | 4 | 8s | | 5 | 13s | | 6+ | 30s (tope) | **Timeout total recomendado:** 10 minutos. Si después de 10 minutos seguís en `PENDIENTE`, contactá a soporte: algo está atascado en SIFEN. ## Implementación TypeScript [#implementación-typescript] ```typescript type EstadoDE = | "PENDIENTE" | "EN_LOTE" | "ENVIADO" | "APROBADO" | "RECHAZADO" | "ERROR" | "CANCELADO" | "DESCONOCIDO"; interface EstadoResponse { cdc: string; estado: EstadoDE; iTiDe: number; numeroDocumento: number; // Long en backend fechaCreacion: string; protocoloAutorizacion: string | null; mensajeRechazo: string | null; } async function consultarEstado(cdc: string): Promise { const response = await fetch( `https://api.sifende.com.py/api/v1/documento-electronico/status/${cdc}`, { headers: { "Authorization": `Bearer ${process.env.SIFENDE_API_KEY}` }, } ); if (!response.ok) throw new Error(`HTTP ${response.status}`); return response.json(); } async function esperarResultado( cdc: string, timeoutMs = 10 * 60 * 1000 ): Promise { const inicio = Date.now(); let intento = 0; while (Date.now() - inicio < timeoutMs) { const espera = Math.min(2000 * Math.pow(1.6, intento), 30000); await new Promise((r) => setTimeout(r, espera)); const resultado = await consultarEstado(cdc); if (resultado.estado === "APROBADO") return resultado; if (resultado.estado === "RECHAZADO") { throw new Error(`SIFEN rechazó el DE: ${resultado.mensajeRechazo}`); } if (resultado.estado === "ERROR") { throw new Error(`Falla técnica al procesar el DE: ${resultado.mensajeRechazo ?? 'sin detalle'}`); } if (resultado.estado === "CANCELADO") return resultado; intento++; } throw new Error(`Timeout esperando resultado para CDC ${cdc}`); } // Uso try { const resultado = await esperarResultado(cdc); console.log("DE aprobado:", resultado.cdc); } catch (err) { console.error("Error:", err.message); } ``` ## Persistencia para resiliencia [#persistencia-para-resiliencia] Si tu proceso puede crashear durante el polling, guardá el estado del DE en tu DB y reintentá desde ahí: ```typescript // Pseudocódigo async function emitirYPersistir(payload: any) { const { id, cdc } = await emitirDE(payload); await db.documentos.insert({ id, cdc, estado: "PENDIENTE" }); // Polling en background: incluso si el proceso muere, el DE queda persistido pollearYActualizar(cdc).catch(console.error); return { id, cdc }; } // Worker independiente para retomar PENDIENTES después de un crash async function reanudarPendientes() { const pendientes = await db.documentos.where({ estado: "PENDIENTE" }); for (const doc of pendientes) { pollearYActualizar(doc.cdc).catch(console.error); } } ``` **Webhooks: en el roadmap.** Estamos trabajando en notificaciones por webhook para eliminar la necesidad de polling. Mientras tanto, el polling con backoff es la estrategia recomendada. ## Tips de producción [#tips-de-producción] * **No uses intervalos fijos cortos** (cada 1s); es ineficiente y genera carga innecesaria * **Hacé el polling en background**, no bloquees la respuesta del usuario * **Loggeá el tiempo total** hasta `APROBADO`. Sirve para detectar degradaciones de SIFEN * **Manejá `429 Too Many Requests`** con backoff adicional si lo recibís * **Guardá `mensajeRechazo`** completo en tu DB cuando recibas `RECHAZADO` **Después de 48 horas, `siResultLoteDE` deja de devolver el lote.** SIFEN responde con código `0364` (no encontrado) para consultas a nivel de lote pasadas las 48 h desde el envío. Si tu DE quedó sin resolver en ese plazo, fallback a la consulta **por CDC individual** (`/documento-electronico/status/:cdc`), que sigue funcionando indefinidamente. Sifende ya implementa este fallback en su scheduler interno. ## Próximos pasos [#próximos-pasos] * [Reintentar Rechazados](/docs/guias/reintentar-rechazados): qué hacer si recibís `RECHAZADO` * [Manejar Errores](/docs/guias/manejar-errores): patrón general de error handling * [Referencia: Consultar Estado](/docs/referencia/documentos-electronicos/consultar-estado) # Receptor B2B y B2C (/docs/guias/receptor-b2b-b2c) El bloque `receptor` define a quién va dirigido el documento electrónico. SIFEN distingue tres situaciones principales, y elegir mal el tipo es la causa más común de rechazos en producción. ## Cuándo usar cada tipo [#cuándo-usar-cada-tipo] | Caso | `tipoOperacion` | `tipoContribuyente` | Datos requeridos | | ------------------------------------------------- | --------------- | ------------------------------- | ------------------------------ | | Venta a empresa con RUC (deduce IVA) | `B2B` | `CONTRIBUYENTE` | RUC + razón social + dirección | | Venta a persona física identificada | `B2C` | `NO_CONTRIBUYENTE` o `NOMINADO` | Cédula + nombre | | Consumo final hasta Gs. 5.000.000 sin identificar | `B2C` | `INNOMINADO` | Ninguno | Los valores aceptados de `tipoContribuyente` son: `CONTRIBUYENTE`, `NO_CONTRIBUYENTE`, `EXTRANJERO`, `NOMINADO`, `INNOMINADO`. Y los de `tipoOperacion`: `B2B`, `B2C`, `B2G` (gobierno) y `B2F` (extranjero). ## B2C innominado [#b2c-innominado] Es el caso más simple: ventas al consumidor final por hasta Gs. 5.000.000 donde no hace falta pedir documento. Cuando `tipoContribuyente` es `INNOMINADO`, no se cargan datos de identificación. ```json { "receptor": { "tipoContribuyente": "INNOMINADO", "tipoOperacion": "B2C" } } ``` **Restricción:** las **Notas de Crédito** y **Notas de Débito** electrónicas **no admiten receptor innominado** (NT-010 / NT-023). Si querés anular o ajustar una FE innominada, vas a tener que identificar al receptor en la NCE/NDE. ## B2C identificado [#b2c-identificado] Para ventas a personas físicas con cédula cuando el monto supera Gs. 5.000.000, o el cliente lo solicita explícitamente. ```json { "receptor": { "tipoContribuyente": "NO_CONTRIBUYENTE", "tipoOperacion": "B2C", "tipoDocumento": "CEDULA_PARAGUAYA", "numeroDocumento": "1234567", "nombreRazonSocial": "Juan Pérez", "direccion": "Av. España 1234, Asunción" } } ``` Otros tipos de documento aceptados para `B2C` cuando no hay cédula paraguaya: | `tipoDocumento` | Cuándo usarlo | | ---------------------- | ---------------------------------------- | | `CEDULA_PARAGUAYA` | Cédula de identidad paraguaya | | `PASAPORTE` | Extranjero sin cédula | | `CARNET_DE_RESIDENCIA` | Residente extranjero | | `CEDULA_EXTRANJERA` | Cédula de identidad extranjera | | `TARJETA_DIPLOMATICA` | Tarjeta diplomática | | `OTRO` | Otro identificador no contemplado arriba | ## B2B (factura a empresa) [#b2b-factura-a-empresa] Cuando el receptor es **otro contribuyente** que va a deducir IVA, el RUC es obligatorio. Sifende valida el RUC contra el padrón de la SET. ```json { "receptor": { "tipoContribuyente": "CONTRIBUYENTE", "tipoOperacion": "B2B", "numeroDocumento": "80012345", "digitoVerificador": "1", "nombreRazonSocial": "Comercial Guaraní S.A.", "direccion": "Av. Mariscal López 4567, Asunción" } } ``` Para B2B, **no enviés `tipoDocumento`**: el RUC se identifica por `tipoOperacion: "B2B"` + `tipoContribuyente: "CONTRIBUYENTE"`. Separás el número y el dígito verificador en dos campos: `numeroDocumento: "80012345"` y `digitoVerificador: "1"`. ### Cuándo es obligatorio facturar B2B [#cuándo-es-obligatorio-facturar-b2b] * Tu cliente es una empresa (PJ o EIRL). * Te pide factura crédito fiscal. * El monto supera Gs. 5.000.000 y el cliente tiene RUC. * Vas a registrar la operación contra cuenta corriente del receptor. ## Restricciones según tipo de documento [#restricciones-según-tipo-de-documento] | Tipo de documento | Innominado | B2C identificado | B2B con RUC | | ------------------------ | :--------: | :--------------: | :---------: | | Factura Electrónica (FE) | ✅ | ✅ | ✅ | | Nota de Crédito (NCE) | ❌ | ✅ | ✅ | | Nota de Débito (NDE) | ❌ | ✅ | ✅ | ## Errores frecuentes [#errores-frecuentes] | SIFEN | Causa | Solución | | ----- | -------------------------------------------------------------------------------------------- | ----------------------------------------------------------- | | 1302 | Falta `tipoContribuyente` para receptor B2B | Completar `tipoContribuyente: "CONTRIBUYENTE"` | | 1303 | Tipo de contribuyente receptor inválido (informado cuando el receptor es `NO_CONTRIBUYENTE`) | Eliminar `tipoContribuyente` cuando es B2C no contribuyente | | 1304 | Falta `numeroDocumento` (RUC) para receptor contribuyente | Completar el RUC del receptor | | 1305 | RUC del receptor no requerido (informado para `NO_CONTRIBUYENTE`) | Eliminar `numeroDocumento` para B2C no contribuyente | | 1306 | RUC del receptor inexistente en Marangatu (no registrado en SET) | Confirmar el RUC con tu cliente | | 1309 | DV del RUC del receptor incorrecto | Verificar `digitoVerificador` | Ver el catálogo completo en [Rechazos SIFEN](/docs/solucion-problemas/rechazos-sifen). ## Próximos pasos [#próximos-pasos] * Volvé a [Factura Electrónica](/docs/guias/factura-electronica) para el flujo completo de emisión. * Si vas a emitir notas de crédito/débito, repasá las restricciones en [Nota de Crédito](/docs/guias/nota-credito) y [Nota de Débito](/docs/guias/nota-debito). # Reintentar Documentos Rechazados (/docs/guias/reintentar-rechazados) Cuando SIFEN rechaza un DE, el documento queda **legalmente nulo** y no se puede "corregir". El flujo correcto es: identificar la causa, emitir un **nuevo DE** con los datos corregidos, e inutilizar el número original si es necesario. Un DE en estado `RECHAZADO` no tiene validez fiscal. **No lo entregues al cliente.** El número de documento que consumió tampoco se puede reutilizar: debe inutilizarse o quedará como hueco en la secuencia. ## Flujo completo [#flujo-completo] **Detectá el rechazo** consultando el estado del DE: ```bash curl "https://api.sifende.com.py/api/v1/documento-electronico/status/{cdc}" \ -H "Authorization: Bearer sk_live_..." ``` Respuesta: ```json { "cdc": "01800123451001001000000122026042710000000006", "estado": "RECHAZADO", "iTiDe": 1, "numeroDocumento": 1, "fechaCreacion": "2026-04-15T10:30:00", "protocoloAutorizacion": null, "mensajeRechazo": "[1306] RUC del receptor inexistente en Marangatu" } ``` `numeroDocumento` es un **`Long`** (entero). El formato `"NNN-NNN-NNNNNNN"` se devuelve como `numeroFormateado` en la respuesta de **emisión**: son campos separados. **Identificá la causa** a partir del código entre corchetes (`[1302]`) en `mensajeRechazo`. Consultá la tabla de [Rechazos SIFEN](/docs/solucion-problemas/rechazos-sifen) para ver el significado. **Corregí los datos** en tu sistema. Ej: corregir el formato del RUC, completar campos faltantes del receptor, ajustar fechas, etc. **Emití un NUEVO DE** con los datos corregidos. Recibirá un nuevo CDC. ```bash curl -X POST "https://api.sifende.com.py/api/v1/documento-electronico" \ -H "Authorization: Bearer sk_live_..." \ -H "Content-Type: application/json" \ -d '{ ...payload corregido... }' ``` **Inutilizá el número original** si el rechazo dejó un hueco que no se rellenará automáticamente. Ver [Inutilizar Numeración](/docs/guias/inutilizar-numeracion). ## Rechazos comunes y cómo corregirlos [#rechazos-comunes-y-cómo-corregirlos] ### `1108`: Timbrado vencido o sin fecha de fin [#1108-timbrado-vencido-o-sin-fecha-de-fin] **Causa:** El timbrado configurado en Sifende no tiene `fechaFin` o ya venció. **Solución:** 1. Renová el timbrado en MARANGATÚ (portal SET) 2. Andá a Configuración → Timbrados en Sifende 3. Actualizá `fechaFin` con la fecha del nuevo timbrado 4. Reemití el DE con el timbrado correcto ### `1302`: Falta `tipoContribuyente` para receptor B2B [#1302-falta-tipocontribuyente-para-receptor-b2b] **Causa:** Operación B2B sin el campo `tipoContribuyente` del receptor. **Solución:** Incluí `tipoContribuyente: "CONTRIBUYENTE"` (o `EXTRANJERO` según corresponda) en el bloque receptor. Reemití. ### `1303`: Tipo de contribuyente receptor inválido [#1303-tipo-de-contribuyente-receptor-inválido] **Causa:** Se envió `tipoContribuyente` cuando el receptor es `NO_CONTRIBUYENTE` (B2C identificado). **Solución:** Eliminá el campo `tipoContribuyente` cuando el receptor es B2C no contribuyente. Reemití. ### `1304`: Falta `numeroDocumento` (RUC) para receptor contribuyente [#1304-falta-numerodocumento-ruc-para-receptor-contribuyente] **Causa:** Operación B2B sin el RUC del receptor. **Solución:** Completá `numeroDocumento` (RUC sin DV) y `digitoVerificador` separadamente. Reemití. ### `1305`: RUC del receptor no requerido [#1305-ruc-del-receptor-no-requerido] **Causa:** Se envió `numeroDocumento` (RUC) cuando el receptor es `NO_CONTRIBUYENTE`. **Solución:** Eliminá `numeroDocumento` para receptor B2C no contribuyente. Reemití. ### `1306`: RUC del receptor inexistente en Marangatu [#1306-ruc-del-receptor-inexistente-en-marangatu] **Causa:** El RUC enviado no está registrado en el padrón de la SET. **Solución:** 1. Verificá el RUC en el [consultador SET](https://www.set.gov.py/portal/PARAGUAY-SET/Servicios/Consultas/RUC) 2. Confirmá con tu cliente que el RUC sea correcto 3. Reemití con el RUC corregido ### `1309`: Dígito verificador del RUC incorrecto [#1309-dígito-verificador-del-ruc-incorrecto] **Causa:** El DV enviado no coincide con el algoritmo SET (módulo 11). **Solución:** Verificá `digitoVerificador` por separado o pedile al receptor su RUC con DV. Reemití. ## Implementación TypeScript [#implementación-typescript] ```typescript async function reemitirSiRechazado(cdcOriginal: string, payloadOriginal: any) { const estado = await consultarEstado(cdcOriginal); if (estado.estado !== "RECHAZADO") { throw new Error(`No corresponde reemitir. Estado actual: ${estado.estado}`); } const codigoError = extraerCodigo(estado.mensajeRechazo); console.log(`Rechazo ${codigoError}: ${estado.mensajeRechazo}`); const payloadCorregido = await aplicarCorrecciones(payloadOriginal, codigoError); // Emitir nuevo DE: recibe un CDC nuevo const nuevo = await emitirDE(payloadCorregido); console.log(`Reemitido como CDC: ${nuevo.cdc}`); // Si querés que el número original quede formalmente cerrado await inutilizarNumero(cdcOriginal); return nuevo; } function extraerCodigo(motivo: string): string { const match = motivo.match(/\[(\d+)\]/); return match ? match[1] : "desconocido"; } ``` ## Buenas prácticas [#buenas-prácticas] * **Loggeá todos los rechazos** con el código y el payload original. Sirven para detectar bugs sistemáticos * **Validá antes de enviar.** Si el 80% de tus rechazos son `1302`, fortalecé la validación de RUC en tu frontend * **Notificá al usuario.** Si un rechazo se da en el contexto de una venta, mostrale al cajero/vendedor qué corregir * **Monitoreá la tasa de rechazos.** Una tasa > 1% indica un problema en tu integración **No reintentes el mismo payload.** Reenviar exactamente los mismos datos producirá el mismo rechazo. Siempre corregí los datos antes de reemitir. ## Próximos pasos [#próximos-pasos] * [Rechazos SIFEN](/docs/solucion-problemas/rechazos-sifen): tabla completa de códigos * [Inutilizar Numeración](/docs/guias/inutilizar-numeracion): cerrar el hueco del número rechazado * [Manejar Errores](/docs/guias/manejar-errores): patrones de error handling # Herramientas (/docs/herramientas) Las herramientas que mantenemos te permiten probar tu API key, validar payloads y ejecutar los flujos típicos contra la API de Sifende sin tener que armar `curl` a mano ni montar un proyecto entero. ## Disponibles [#disponibles] ## Próximamente [#próximamente] ## ¿Cuándo usarlas? [#cuándo-usarlas] * Para probar tu API key apenas la generás, antes de integrar. * Para reproducir un bug que te aparece en producción con un payload conocido. * En demos y pruebas manuales con QA o equipos no técnicos. * En scripts puntuales de carga, migración o reemisión. Si vas a integrar Sifende dentro de tu sistema, seguí la [Referencia API](/docs/referencia). Las herramientas son un complemento, no un reemplazo. # Sifende CLI (/docs/herramientas/sifende-cli) Sifende CLI es un cliente de línea de comandos que envuelve la API REST de Sifende. Sirve para ejecutar los flujos típicos (emitir, consultar estado, descargar KuDE, cancelar e inutilizar) contra `https://api.sifende.com.py/api/v1/` sin escribir código. Va bien para probar tu API key, validar payloads, reproducir bugs y armar scripts puntuales. Solo depende de la stdlib de Python y de `requests`. La CLI no reemplaza una integración. Para producción, integrá la API REST directamente desde tu backend siguiendo la [Referencia](/docs/referencia). ## Requisitos [#requisitos] * Python 3.9 o superior * `make` (opcional; todo se puede correr con `python` directo) * Una API key de Sifende. Si no tenés una, seguí [Paso 1: Credenciales](/docs/inicio-rapido/paso-1-credenciales) ## Instalación [#instalación] Cloná el repositorio y entrá al directorio del CLI: ```bash git clone https://github.com/ithdev/sifende-cli.git cd sifende-cli ``` El repo trae un `sample_factura.json` listo para emitir, así que podés probar todo antes de armar tu propio payload. ## Quickstart en 3 comandos [#quickstart-en-3-comandos] ```bash make setup # crea venv, instala deps, copia .env.example a .env nano .env # o tu editor preferido; poné tu SIFENDE_API_KEY make emitir FILE=sample_factura.json ``` `make setup` crea un entorno virtual en `.venv/`, instala las dependencias y copia `.env.example` a `.env` si todavía no existe. Después abrís `.env` y pegás tu `SIFENDE_API_KEY`. Si todo salió bien, vas a ver el `cdc` impreso en consola y un directorio `documentos/{cdc}/` con `payload.json`, `response.json` y, una vez aprobado, `kude.pdf`. ### Sin Make [#sin-make] ```bash python3 -m venv .venv source .venv/bin/activate # Windows PowerShell: .venv\Scripts\Activate.ps1 pip install -r requirements.txt cp .env.example .env # editá .env con tu API key python sifende.py emitir --file sample_factura.json ``` ## Configuración [#configuración] La CLI busca tu API key en este orden (gana el primero que la encuentre): 1. Flag `--api-key` en la línea de comandos 2. Variable de entorno `SIFENDE_API_KEY` 3. Archivo `.env` en el directorio actual 4. Archivo `.env` en la raíz del paquete CLI La API key nunca se ecoa, ni en logs ni en mensajes de error. El header `Authorization` se omite explícitamente al renderizar errores HTTP. Podés correr `--debug` con tranquilidad. ### Variables soportadas en `.env` [#variables-soportadas-en-env] | Variable | Default | | ------------------- | ------------------------------------ | | `SIFENDE_API_KEY` | (requerido) | | `SIFENDE_BASE_URL` | `https://api.sifende.com.py/api/v1/` | | `SIFENDE_TIMEOUT_S` | `30` | Cambiá `SIFENDE_BASE_URL` solo si te diéramos un ambiente alternativo. Para QA y producción usá el valor por defecto. ## Comandos [#comandos] Cada operación está disponible en dos formas: con `make` (más corto, ideal para uso interactivo) o con `python sifende.py` (más explícito, ideal para scripts). `emitir` espera la confirmación de SIFEN antes de retornar: la CLI hace polling automático del estado hasta que el documento quede `APROBADO` o `RECHAZADO`. Si querés solo enviar y no esperar, pasá `--no-wait`. Para polling manual sobre un CDC ya emitido, usá `estado`. | Operación | Make | Python | | -------------------- | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | Emitir desde archivo | `make emitir FILE=factura.json` | `python sifende.py emitir --file factura.json` | | Emitir interactivo | `make emitir-i` | `python sifende.py emitir` | | Consultar estado | `make estado CDC=` | `python sifende.py estado ` | | Descargar KuDE | `make kude CDC= OUT=factura.pdf` | `python sifende.py kude --out factura.pdf` | | Cancelar | `make cancelar CDC= MOTIVO="..."` | `python sifende.py cancelar --motivo "..."` | | Inutilizar | `make inutilizar TIPO=1 EST=001 PE=001 TIMBRADO= DESDE=1 HASTA=3 MOTIVO="..."` | `python sifende.py inutilizar --tipo-documento 1 --establecimiento 001 --punto-expedicion 001 --numero-timbrado --desde 1 --hasta 3 --motivo "..."` | | Debug HTTP | `make debug FILE=factura.json` | `python sifende.py --debug emitir --file factura.json` | ### Flags globales [#flags-globales] Estos flags funcionan en todos los comandos: | Flag | Qué hace | | ------------------ | -------------------------------------------------------------- | | `--api-key ` | Pisa la API key resuelta del entorno | | `--base-url ` | Pisa la URL base de la API | | `--quiet` | Solo imprime el dato esencial (CDC, estado) | | `--json` | Imprime la respuesta cruda como JSON | | `--debug` | Traza HTTP completa con headers (la API key queda enmascarada) | Con `make` se reenvían como `EXTRA="..."`: ```bash make emitir FILE=factura.json EXTRA="--json --quiet" ``` ## Comportamiento al aprobar [#comportamiento-al-aprobar] Al confirmarse la aprobación de un documento, la CLI guarda automáticamente: ``` documentos/ └── {cdc}/ ├── payload.json # request enviado ├── response.json # respuesta de la API └── kude.pdf # KuDE descargado ``` Esto te da un audit trail local para cada emisión sin trabajo extra. ## Ejemplo: emitir y descargar el KuDE [#ejemplo-emitir-y-descargar-el-kude] ```bash # 1. Emitir desde el archivo de muestra make emitir FILE=sample_factura.json # Salida: # CDC: 01800123451001001000000122026050910000000001 # Estado: PENDIENTE → APROBADO # 2. Consultar estado por CDC make estado CDC=01800123451001001000000122026050910000000001 # 3. Descargar el KuDE PDF make kude CDC=01800123451001001000000122026050910000000001 OUT=factura.pdf ``` ## Ejemplo: cancelar un documento aprobado [#ejemplo-cancelar-un-documento-aprobado] ```bash make cancelar \ CDC=01800123451001001000000122026050910000000001 \ MOTIVO="Error en datos del receptor" ``` Recordá que cancelar tiene 48 horas de plazo desde la emisión. Pasado ese tiempo, tenés que emitir una [Nota de Crédito](/docs/guias/nota-credito). ## Ejemplo: inutilizar un rango de numeración [#ejemplo-inutilizar-un-rango-de-numeración] Si saltaste números (por ejemplo, una falla puntual te dejó huecos del 5 al 8), inutilizalos antes de seguir emitiendo: ```bash make inutilizar \ TIPO=1 \ EST=001 \ PE=001 \ TIMBRADO=12345678 \ DESDE=5 \ HASTA=8 \ MOTIVO="Salto de numeración por incidente" ``` Para entender cuándo y cómo usar este flujo, revisá [Inutilizar Numeración](/docs/guias/inutilizar-numeracion). ## Estructura del payload [#estructura-del-payload] El archivo JSON que pasás a `emitir --file` es exactamente el mismo cuerpo que envía la API REST. El repositorio incluye `sample_factura.json` con un caso B2C completo. Para armar otros tipos de documento, ver: * [Modelos: Factura Electrónica](/docs/referencia/modelos/factura-electronica) * [Endpoint: Emitir](/docs/referencia/documentos-electronicos/emitir) ## Solución de problemas [#solución-de-problemas] | Síntoma | Causa probable | Cómo resolverlo | | -------------------------------- | ---------------------------------- | ------------------------------------------------------------ | | `SIFENDE_API_KEY no configurada` | El `.env` está vacío o no se cargó | Verificá `.env` en el directorio actual o pasá `--api-key` | | `401 Invalid or expired API key` | Key revocada o pegada con espacios | Rotá la credencial desde el panel → Configuración → API Keys | | `400 validation-error` | Payload mal armado | Corré con `--debug` y revisá `errores` en la respuesta | | `422 SIFEN 1108` | Timbrado vencido o mal cargado | Renová el timbrado en SET y resincronizá en el panel | | `make: command not found` | No tenés Make instalado | Usá la forma `python sifende.py ...` | Para errores de SIFEN (códigos 1xxx, 2xxx, 3xxx), revisá [Rechazos SIFEN](/docs/solucion-problemas/rechazos-sifen). ## Recursos [#recursos] * Repositorio: [github.com/ithdev/sifende-cli](https://github.com/ithdev/sifende-cli) (código fuente, ejemplos y `CHANGELOG.md`) * Issues: [reportá bugs o pedí mejoras](https://github.com/ithdev/sifende-cli/issues) * Referencia API: [Documentos Electrónicos](/docs/referencia/documentos-electronicos) * Inicio Rápido: [Paso 2: Tu Primera Factura](/docs/inicio-rapido/paso-2-primera-factura) # Inicio Rápido (/docs/inicio-rapido) Esta guía te lleva desde cero hasta tu primer documento electrónico aprobado por SIFEN. ## Lo que vas a lograr [#lo-que-vas-a-lograr] Al final de esta guía vas a tener: 1. Un API key configurado y listo para usar. 2. Tu primera Factura Electrónica emitida y su CDC generado. 3. El estado de procesamiento verificado vía SIFEN. ## Requisitos previos [#requisitos-previos] Antes de empezar, asegurate de tener: * Un certificado digital PKCS12 emitido por la SET. * RUC activo y autorizado para emitir electrónicamente. ¿No tenés estos datos? Empezá por [Requisitos Previos](/docs/inicio-rapido/requisitos-previos). ## Pasos [#pasos] [Configurar tus credenciales y obtener un API key](/docs/inicio-rapido/paso-1-credenciales) [Emitir tu primera Factura Electrónica](/docs/inicio-rapido/paso-2-primera-factura) [Verificar el estado de procesamiento](/docs/inicio-rapido/paso-3-verificar-estado) [Ir a producción](/docs/inicio-rapido/paso-4-produccion) ## ¿Qué sigue? [#qué-sigue] Una vez que tengas tu primera factura aprobada, podés seguir por dos caminos: # Paso 1: Configurar Credenciales (/docs/inicio-rapido/paso-1-credenciales) En este paso vas a configurar todo lo que hace falta en Sifende para hacer tu primera llamada a la API. ## 1.1 Crear tu cuenta y contribuyente [#11-crear-tu-cuenta-y-contribuyente] Si es tu primera vez, ingresá a [app.sifende.com.py](https://app.sifende.com.py) y creá tu cuenta. Después, creá un **Contribuyente**. Crear contribuyente en Sifende Llená la información que pide: RUC, datos básicos, datos de contacto, ubicación fiscal y actividades económicas. Rellenar información del contribuyente ## 1.2 Subir el certificado digital [#12-subir-el-certificado-digital] Desde el panel de Sifende, andá a [**Contribuyente → Certificado digital**](https://app.sifende.com.py/certificado-digital) y subí tu archivo PKCS12 junto con la contraseña y el CSC. Subir certificado digital ## 1.3 Configurar el timbrado [#13-configurar-el-timbrado] Desde [**Contribuyente → Timbrado**](https://app.sifende.com.py/timbrado), ingresá los datos de tu timbrado electrónico. Si estás en el ambiente de pruebas, podés usar: * Tu RUC (sin dígito verificador) como número de timbrado (rellenar con ceros a la izquierda si hace falta). * Fecha de inicio: el día de hoy. * Fecha de fin: un año después. Configurar el timbrado ## 1.4 Crear tu API key [#14-crear-tu-api-key] Desde [**API Keys**](https://app.sifende.com.py/api-keys), creá una clave nueva. Guardá el valor generado: no se muestra de nuevo. Crear api key ```bash # Te recomendamos guardar tu API Key, la vas a usar en los próximos pasos. export SIFENDE_API_KEY="sk_live_..." ``` ¿Querés probar tu API key en segundos? La [Sifende CLI](/docs/herramientas/sifende-cli) te deja emitir tu primer documento con 3 comandos, sin escribir código. Tu API key tiene permisos completos para el contribuyente. Tratala como una contraseña: nunca la incluyas en el código fuente ni en repositorios públicos. Cuando esté todo listo, seguí con [Paso 2: Tu primera Factura Electrónica](/docs/inicio-rapido/paso-2-primera-factura). # Paso 2: Tu Primera Factura Electrónica (/docs/inicio-rapido/paso-2-primera-factura) Un endpoint para todos los tipos de documento. Sifende usa un único endpoint `POST /api/v1/documento-electronico` para todos los tipos de documento electrónico. El campo `tipoDocumento` define qué tipo emitís. Más información en [Emitir Documento Electrónico](/docs/referencia/documentos-electronicos/emitir). ## Tu primera factura [#tu-primera-factura] Para tu primer ejemplo usamos un receptor INNOMINADO: un consumidor final anónimo (sin cédula, sin RUC). Es el caso más simple y permite emitir facturas válidas sin pedirle datos al comprador. ¿Querés hacer todo esto más rápido? Probá la [Sifende CLI](/docs/herramientas/sifende-cli): emite, consulta estado y descarga el KuDE desde la terminal con tres comandos. ```bash curl -X POST https://api.sifende.com.py/api/v1/documento-electronico \ -H "Authorization: Bearer $SIFENDE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tipoDocumento": "FACTURA_ELECTRONICA", "fechaEmision": "2026-04-15T10:30:00", "tipoEmision": "NORMAL", "numeroEstablecimiento": 1, "puntoExpedicion": 1, "tipoTransaccion": "VENTA_MERCADERIA", "monedaOperacion": "PYG", "receptor": { "tipoContribuyente": "INNOMINADO", "tipoOperacion": "B2C", "nombreRazonSocial": "Sin Nombre" }, "condicionOperacion": "CONTADO", "condicionPago": { "tipo": "CONTADO", "tipoPago": "EFECTIVO", "monedaPago": "PYG", "montoPago": 110000 }, "items": [ { "codigo": "PROD-001", "descripcion": "Resma de papel A4 75g", "cantidad": 10, "unidadMedida": "UNI", "precioUnitario": 10000, "afectacionTributaria": "GRAVADO", "tasaIVA": 10 } ] }' ``` ## Respuesta exitosa [#respuesta-exitosa] Si el documento se aceptó, la API responde `202 Accepted` con un DTO completo: ```json { "id": "550e8400-e29b-41d4-a716-446655440000", "cdc": "01800123451001001000000122026042710000000006", "estado": "PENDIENTE", "tipoDocumento": "FACTURA_ELECTRONICA", "iTiDe": 1, "numeroDocumento": 1, "numeroFormateado": "001-001-0000001", "fechaCreacion": "2026-04-27T10:30:00", "qrUrl": "https://ekuatia.set.gov.py/consultas-test/qr?...", "statusUrl": "https://api.sifende.com.py/api/v1/documento-electronico/status/01800123451001001000000122026042710000000006", "kudeUrl": "https://api.sifende.com.py/api/v1/documento-electronico/01800123451001001000000122026042710000000006/kude" } ``` El campo `cdc` contiene el Código de Control del Documento Electrónico: 44 caracteres que identifican a tu documento en SIFEN. Guardalo junto con el `id`: los vas a necesitar para consultar estado, descargar el KuDE y cancelar. 202 Accepted, no 200 OK. El estado inicial siempre es `PENDIENTE`: el documento aún no fue procesado por SIFEN. Mirá el [Paso 3](/docs/inicio-rapido/paso-3-verificar-estado) para hacer polling del estado real. Los montos en guaraníes (PYG) son números enteros sin decimales. `10000` = Gs. 10.000. Ver [Convenciones](/docs/referencia/convenciones) para más detalles. ## Con receptor identificado [#con-receptor-identificado] Si tu cliente da su cédula o RUC, podés emitir la factura a su nombre. Reemplazá el bloque `receptor` del ejemplo anterior por alguno de los siguientes. Receptor B2C con cédula: ```json "receptor": { "tipoContribuyente": "NO_CONTRIBUYENTE", "tipoOperacion": "B2C", "tipoDocumento": "CEDULA_PARAGUAYA", "numeroDocumento": "1234567", "nombreRazonSocial": "Juan Pérez" } ``` Para receptores B2B (con RUC) y otros casos, consultá la guía [Receptor B2B vs B2C](/docs/guias/receptor-b2b-b2c). ## ¿Algo salió mal? [#algo-salió-mal] Consultá [Errores Comunes](/docs/solucion-problemas/errores-comunes) o la [Referencia de Errores](/docs/referencia/errores). Seguí con [Paso 3: Verificar el Estado](/docs/inicio-rapido/paso-3-verificar-estado). # Paso 3: Verificar el Estado (/docs/inicio-rapido/paso-3-verificar-estado) El procesamiento en SIFEN es asíncrono: tu documento se envía en un lote y SIFEN lo procesa en segundos o minutos. Este paso te muestra cómo verificar si fue aprobado o rechazado. ## Consultar el estado por CDC [#consultar-el-estado-por-cdc] ```bash curl https://api.sifende.com.py/api/v1/documento-electronico/status/01800123451001001000000122026042710000000006 \ -H "Authorization: Bearer $SIFENDE_API_KEY" ``` Reemplazá el CDC del ejemplo por el que recibiste en el Paso 2. ## Respuesta [#respuesta] ```json { "cdc": "01800123451001001000000122026042710000000006", "estado": "APROBADO", "iTiDe": 1, "numeroDocumento": 1, "fechaCreacion": "2026-04-27T10:30:00", "protocoloAutorizacion": "01234567890123", "mensajeRechazo": null } ``` ## Estados posibles [#estados-posibles] | Estado | Significado | | ----------- | ------------------------------------------------------------- | | `PENDIENTE` | El documento fue recibido pero aún no se asignó a un lote | | `EN_LOTE` | Incluido en un lote, esperando envío a SIFEN | | `ENVIADO` | El lote fue enviado a SIFEN, esperando respuesta | | `APROBADO` | SIFEN lo aprobó. El documento es válido | | `RECHAZADO` | SIFEN lo rechazó. Ver `mensajeRechazo` en la respuesta | | `ERROR` | Error técnico durante el procesamiento (ver `mensajeRechazo`) | | `CANCELADO` | El documento fue cancelado después de su aprobación | ## Polling recomendado [#polling-recomendado] Si el estado es `PENDIENTE`, `EN_LOTE` o `ENVIADO`, esperá unos segundos y consultá de nuevo. El procesamiento típico tarda entre 15 y 60 segundos, pero en el ambiente de QA puede demorarse hasta 2 minutos o más. Configurá un timeout de al menos 5 minutos y un intervalo de polling razonable (5-10 s) para no saturar la API. ```typescript async function esperarAprobacion(cdc: string, timeoutMs = 5 * 60 * 1000): Promise { const inicio = Date.now(); const intervaloMs = 5000; // 5 segundos entre intentos while (Date.now() - inicio < timeoutMs) { const res = await fetch( `https://api.sifende.com.py/api/v1/documento-electronico/status/${cdc}`, { headers: { Authorization: `Bearer ${process.env.SIFENDE_API_KEY}` } } ); const data = await res.json(); if (data.estado === 'APROBADO') return; if (data.estado === 'RECHAZADO' || data.estado === 'ERROR') { throw new Error(data.mensajeRechazo ?? 'Documento rechazado por SIFEN'); } await new Promise(r => setTimeout(r, intervaloMs)); } throw new Error('Timeout esperando aprobación SIFEN (5 minutos)'); } ``` No uses timeouts cortos (menos de 2 minutos). En QA es habitual ver documentos que se quedan en `EN_LOTE` más de 2 minutos antes de aprobarse. Un timeout corto te va a generar falsos negativos. ## Documento APROBADO [#documento-aprobado] Si el estado es `APROBADO`, podés: * Descargar el KuDE (PDF para el cliente): [`GET /:cdc/kude`](/docs/referencia/documentos-electronicos/descargar-kude). * Descargar el XML firmado. ## Documento RECHAZADO [#documento-rechazado] Consultá [Rechazos SIFEN](/docs/solucion-problemas/rechazos-sifen) para entender el código de error y cómo corregirlo. El mensaje que devuelve SIFEN viene en el campo `mensajeRechazo`. *** ¡Felicitaciones! Si llegaste hasta acá con un estado `APROBADO`, tu integración básica está funcionando. Seguí con [Paso 4: Ir a Producción](/docs/inicio-rapido/paso-4-produccion). # Paso 4: Ir a Producción (/docs/inicio-rapido/paso-4-produccion) Antes de activar tu integración en producción, revisá este checklist. ¿Buscás el checklist completo? Esta es una versión resumida pensada para el flujo de Inicio Rápido. Para el checklist completo con verificaciones detalladas, criterios de aprobación y pasos de migración, consultá la guía [Ir a Producción](/docs/guias/ir-a-produccion). ## Checklist de producción [#checklist-de-producción] ### Credenciales [#credenciales] * [ ] API key de producción generado (distinto al de QA) * [ ] Certificado digital de producción subido (distinto al de pruebas) * [ ] Timbrado de producción configurado con fechas correctas * [ ] Variables de entorno actualizadas (`SIFENDE_API_KEY`, `SIFENDE_BASE_URL`) ### Manejo de errores [#manejo-de-errores] * [ ] Implementaste manejo de `429 Too Many Requests` con backoff exponencial * [ ] Implementaste reintentos para errores de red (timeouts, 5xx) * [ ] Guardás el CDC de cada documento emitido en tu base de datos * [ ] Implementaste polling de estado o procesás el estado al descargar el KuDE ### Calidad de datos [#calidad-de-datos] * [ ] Validás el RUC del receptor antes de enviarlo (formato `{número}-{dv}`) * [ ] Los montos en PYG son enteros sin decimales * [ ] Las fechas están en formato `ISO 8601` sin timezone ### Flujos críticos [#flujos-críticos] * [ ] Probaste el flujo completo: emitir → aprobado → descargar KuDE * [ ] Probaste el flujo de rechazo: recibir error SIFEN → corregir → reemitir * [ ] Sabés cómo cancelar un documento aprobado si hace falta ### Seguridad [#seguridad] * [ ] El API key no está en el código fuente ni en repositorios * [ ] Tenés una estrategia de rotación de API keys * [ ] El certificado digital está guardado de forma segura ## URL de producción [#url-de-producción] ``` https://api.sifende.com.py ``` ## Soporte [#soporte] ¿Tenés algún problema en producción? Consultá [Solución de Problemas](/docs/solucion-problemas) o contactá a [soporte@sifende.com.py](mailto:soporte@sifende.com.py). *** Para el checklist completo con verificaciones detalladas y criterios de aprobación, consultá la guía [Ir a Producción](/docs/guias/ir-a-produccion). # Requisitos Previos (/docs/inicio-rapido/requisitos-previos) Antes de emitir tu primer documento electrónico con Sifende, necesitás obtener algunos datos directamente de la **SET (Subsecretaría de Estado de Tributación)**. Esta página explica qué es cada uno y cómo conseguirlo. ## Certificado Digital (PKCS12) [#certificado-digital-pkcs12] El certificado digital es el equivalente a tu firma electrónica. SIFEN lo usa para firmar cada documento electrónico que emitís. **¿Qué necesitás?** * Un archivo `.p12` o `.pfx` (formato PKCS12). * La contraseña del certificado. **¿Cómo obtenés el certificado?** * Solicitalo directamente en la SET a través de [Marangatu](https://www.set.gov.py/portal/PARAGUAY-SET/PortalSET) con tu RUC activo. * El proceso incluye verificación presencial o digital según el tipo de contribuyente. El certificado tiene fecha de vencimiento. Sifende te avisa cuando se acerca el vencimiento, pero renovarlo a tiempo queda de tu lado. ## CSC (Código de Seguridad del Contribuyente) [#csc-código-de-seguridad-del-contribuyente] El CSC es un código numérico que la SET asigna a cada contribuyente para generar el código QR del KuDE. **¿Cómo obtenés el CSC?** * Se solicita junto con la habilitación como facturador electrónico en la SET. * Es un valor numérico de 4 dígitos que vas a configurar en Sifende. ## Timbrado Electrónico [#timbrado-electrónico] El timbrado es la habilitación de la SET para emitir documentos. El **timbrado electrónico** (e-timbrado) es distinto al timbrado tradicional en papel: es específico para documentos electrónicos. **¿Cómo obtenés el timbrado electrónico?** * Se solicita a través de Marangatu una vez que estás habilitado como facturador electrónico. * Tiene una fecha de inicio y fecha de fin de vigencia. * Un timbrado es válido para un establecimiento y punto de expedición específico. Si ya tenés timbrado para documentos en papel, no es el mismo. Necesitás solicitar un timbrado electrónico aparte. ## Ambiente de Pruebas (QA) [#ambiente-de-pruebas-qa] Antes de ir a producción, podés probar tu integración en el ambiente de pruebas de SIFEN. **Credenciales de prueba:** * La SET provee certificados de prueba para el ambiente QA. Consultá la documentación de SIFEN o contactá a soporte para obtenerlos. * Los timbrados de prueba son distintos a los de producción. * Las RUCs de receptores también tienen valores de prueba definidos por la SET. ## Checklist antes de integrar [#checklist-antes-de-integrar] * [ ] Certificado digital PKCS12 + contraseña * [ ] CSC de 4 dígitos * [ ] Número de timbrado electrónico * [ ] Fechas de vigencia del timbrado (`fechaInicio`, `fechaFin`) * [ ] Número de establecimiento y punto de expedición * [ ] Credenciales de ambiente QA (para pruebas) Una vez que tenés todo esto, seguí con [Paso 1: Configurar tus credenciales](/docs/inicio-rapido/paso-1-credenciales). # Características (/docs/plataforma/caracteristicas) Sifende cubre el ciclo completo del documento electrónico: firma digital, transmisión a SIFEN, seguimiento del estado y notificación al receptor. ## Conexión automatizada con e-kuatia [#conexión-automatizada-con-e-kuatia] Sifende gestiona la conexión con e-kuatia de la DNIT: firma digital con tu certificado P12, generación del XML según el Manual Técnico V150, empaquetado en lotes, transmisión vía SOAP y consulta de resultados. Vos enviás un POST con JSON. Del resto se encarga la plataforma. 99.9% de uptime, baja latencia y reintentos automáticos. Si la DNIT está caída, los documentos se encolan y reintentan con backoff exponencial. No perdés emisiones. ### Todos los documentos electrónicos [#todos-los-documentos-electrónicos] Emití Factura Electrónica (FE), Nota de Crédito Electrónica (NCE) y Nota de Débito Electrónica (NDE) desde una sola API REST. Cada documento queda firmado, validado y rastreable por su CDC (Código de Control del Documento Electrónico). > Próximamente: Autofactura Electrónica (AFE) y Nota de Remisión Electrónica (NRE) están en desarrollo. ### Procesamiento de alto volumen [#procesamiento-de-alto-volumen] Pipeline pensado para escalar: * Agrupación automática en lotes según las reglas de SIFEN. * Reintentos con backoff exponencial ante errores transitorios de red o de la DNIT. * Clasificación de errores que separa los recuperables de los definitivos, para que no pierdas tiempo reintentando lo que no se va a aprobar. * Miles de documentos por minuto sostenidos. ### Seguridad por diseño [#seguridad-por-diseño] La facturación electrónica maneja datos fiscales sensibles, así que la plataforma se diseñó con eso en mente desde el primer día: * Certificados P12 cifrados en reposo. Los subís una sola vez al panel web. * Claves API hasheadas con SHA-256. La clave en plano solo se muestra una vez al crearla. * Errores en formato RFC 9457 (Problem Details), para integraciones predecibles. * Multi-tenant con aislamiento estricto de datos por contribuyente. ### Panel web completo [#panel-web-completo] Más allá de la API, Sifende incluye un panel web donde podés: * Navegar, filtrar y rastrear todos tus documentos electrónicos. * Descargar el XML firmado y el KuDE en PDF. * Visualizar el código QR del documento. * Monitorear el estado de cada lote en tiempo real. * Gestionar contribuyentes, timbrados y certificados. ### API keys y multi-usuario [#api-keys-y-multi-usuario] Gestión completa del acceso a la plataforma: * API keys: crealas, rotalas y eliminalas desde el panel web. Podés tener varias claves activas para separar ambientes o servicios. * Equipos: invitá miembros con control de acceso por rol. * Multi-tenant: una sola cuenta puede administrar varios contribuyentes (RUCs), útil para contadores y proveedores de software. ### Notificaciones por email [#notificaciones-por-email] Configurá emails de receptor en tus DEs y Sifende envía automáticamente una notificación al cliente con el KuDE en PDF adjunto apenas el documento queda aprobado por SIFEN. Sin código adicional ni servicios SMTP que mantener. ## ¿Listo para empezar? [#listo-para-empezar] # Cómo Funciona (/docs/plataforma/como-funciona) Sifende reduce el flujo de e-kuatia (SIFEN) a tres pasos. No tenés que aprender SOAP, XSDs ni la mecánica de firma digital: mandás JSON y recibís el CDC aprobado. ¿Cuánto toma integrar? Una integración básica se completa en horas, no en semanas. Solo hace falta un POST HTTP con JSON, y todos los planes incluyen acceso a un sandbox para probar sin compromiso. ## El flujo en 3 pasos [#el-flujo-en-3-pasos] **Registrate y configurá** Creá tu cuenta gratis, subí tu certificado digital P12 y configurá tu timbrado electrónico. Todo desde el panel web, una sola vez. Sifende almacena tu certificado de forma segura y lo usa automáticamente para firmar cada documento. También te avisa cuando esté por vencer. **Enviá un JSON a nuestra API** Usá la API REST de Sifende para crear FE, NCE o NDE. Sin XML, sin SOAP: solo JSON. ```bash curl -X POST https://api.sifende.com.py/api/v1/documento-electronico \ -H "Authorization: Bearer $SIFENDE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tipoDocumento": "FACTURA_ELECTRONICA", ... }' ``` Un único endpoint maneja los tres tipos de documento: el campo `tipoDocumento` decide cuál emitís. **Recibí tu CDC aprobado** Sifende firma tu documento con el certificado P12, lo empaqueta en un lote, lo transmite a la DNIT y consulta el resultado automáticamente. Vos recibís el CDC aprobado y el estado de cada documento. El CDC es el identificador único de 44 caracteres asignado por SIFEN. Guardalo: con eso consultás estado, descargás el KuDE o cancelás. ## ¿Qué hace Sifende detrás de escena? [#qué-hace-sifende-detrás-de-escena] Aunque vos solo ves un POST y un CDC, en cada emisión Sifende: 1. Valida tu JSON contra las reglas del Manual Técnico V150 y las Notas Técnicas vigentes. 2. Genera el XML según el formato exacto que exige SIFEN. 3. Firma digitalmente el XML con tu certificado P12. 4. Empaqueta el documento en un lote junto con otros DEs si corresponde. 5. Transmite el lote a los servicios SOAP de la DNIT. 6. Consulta el resultado del lote periódicamente hasta obtener la respuesta de SIFEN. 7. Mapea los códigos de respuesta de SIFEN a estados claros (`APROBADO`, `RECHAZADO`, etc.). 8. Notifica por email al receptor (si configuraste su correo) con el KuDE adjunto. Si la DNIT rechaza el documento, Sifende te devuelve el código de error y el motivo en formato [RFC 9457](/docs/referencia/errores) para que puedas corregir y reintentar. ## Ambiente de pruebas [#ambiente-de-pruebas] Todos los planes (incluido el Starter gratis) tienen acceso a un sandbox conectado al ambiente de testing de la DNIT. Podés validar tu integración con datos de prueba antes de pasar a producción, sin gastar emisiones reales. Cuando estés listo para producción, solo cambiás de credenciales: el contrato de la API es idéntico. ## Próximos pasos [#próximos-pasos] # La Plataforma (/docs/plataforma) ## ¿Qué es Sifende? [#qué-es-sifende] Sifende es una plataforma SaaS paraguaya que se encarga de la integración con e-kuatia (SIFEN) de la DNIT: firma digital con certificado P12, generación de XML según el Manual Técnico V150, empaquetado en lotes, transmisión vía SOAP y consulta de resultados, todo detrás de una API REST en JSON. En lugar de implementar SOAP, XSD, certificados y reintentos vos mismo, mandás un POST con JSON y recibís el CDC aprobado. Sifende corre la integración con la DNIT por vos. ## ¿Para quién? [#para-quién] * Equipos de desarrollo que necesitan integrar facturación electrónica en un ERP, e-commerce o software propio sin construir la integración SOAP desde cero. * Empresas con alto volumen que requieren procesamiento por lotes, reintentos automáticos y trazabilidad por CDC. * Contadores y consultores que gestionan varios contribuyentes (RUCs) desde una sola cuenta. * Negocios multi-establecimiento que necesitan más capacidad que la que ofrece e-kuatia'i de la DNIT. ## Recorrido por la plataforma [#recorrido-por-la-plataforma] ## ¿Listo para integrar? [#listo-para-integrar] Si ya conocés la plataforma y querés ir directo al código, andá al [Inicio Rápido](/docs/inicio-rapido) o a la [Referencia API](/docs/referencia). # Planes y Precios (/docs/plataforma/planes) Sifende tiene cuatro planes que cubren desde un contribuyente que emite pocas facturas al mes hasta una empresa con muchos establecimientos y alto volumen. Modelo de facturación: los precios son por contribuyente (RUC) por mes. Una sola cuenta de Sifende puede administrar varios contribuyentes; cada RUC se factura por separado según el plan asignado. ## Resumen comparativo [#resumen-comparativo] | Característica | Starter | Professional | Business | Enterprise | | --------------------------------- | ---------------------------- | -------------------- | ------------------------- | ------------------------------- | | **Precio mensual** | Gs. 0 | Gs. 99.000 | Gs. 250.000 | desde Gs. 600.000 | | **Equivalente USD aprox.** | Gratis | \~USD 14 | \~USD 33 | a convenir | | **Documentos electrónicos / mes** | hasta 100 | Ilimitados | Ilimitados | Ilimitados | | **Tipos de DE soportados** | Solo FE | FE, NCE, NDE | FE, NCE, NDE | FE, NCE, NDE | | **Establecimientos** | 1 | 1 | hasta 10 | Ilimitados | | **Usuarios** | 2 base | 2 base + 1 adicional | 2 base + 13 adicionales | Ilimitados | | **Personalización del KuDE** | Estándar (con marca Sifende) | Con tu logo | Totalmente personalizable | White-label (sin marca Sifende) | | **Retención de datos** | 3 meses | 12 meses | 5 años (cumplimiento SET) | Ilimitada | | **Exportación** | — | — | CSV / Excel | CSV / Excel / JSON / API push | | **Analíticas** | — | Básicas | Avanzadas + auditoría | Avanzadas + auditoría | | **Onboarding** | Documentación | Documentación | 1 sesión incluida | Implementación dedicada | | **Soporte** | Documentación | Email (48h) | WhatsApp prioritario (4h) | 24/7 WhatsApp y teléfono | | **SLA** | — | — | — | Con penalidades contractuales | | **White-label de la plataforma** | — | — | — | ✓ | ## Detalle por plan [#detalle-por-plan] ### Starter (Gs. 0, gratis para siempre) [#starter-gs-0-gratis-para-siempre] Pensado para emprendedores y pequeños negocios que recién arrancan con facturación electrónica. Sin tarjeta, sin compromisos. * Hasta 100 documentos electrónicos por mes * Validez fiscal completa ante SIFEN * Solo Factura Electrónica (FE) * 1 establecimiento, 2 usuarios base * KuDE PDF estándar (con marca Sifende) * Retención de datos: 3 meses * Acceso a guía de inicio y documentación ### Professional (Gs. 99.000 / contribuyente / mes) [#professional-gs-99000--contribuyente--mes] Para PyMEs con volumen medio que necesitan emitir todos los tipos de documento y personalizar el KuDE con su marca. * Documentos electrónicos ilimitados * Todos los tipos: FE, NCE y NDE * 1 establecimiento, 2 usuarios base más 1 usuario adicional * KuDE con tu logo * Retención de datos: 12 meses * Analíticas básicas * Soporte por email (respuesta en 48 horas hábiles) ### Business (Gs. 250.000 / contribuyente / mes, el más popular) [#business-gs-250000--contribuyente--mes-el-más-popular] Para empresas con varias sucursales y operación intensiva. Cumple los 5 años de retención que exige la SET. * Documentos electrónicos ilimitados, todos los tipos * Hasta 10 establecimientos * 2 usuarios base más 13 usuarios adicionales * KuDE totalmente personalizable * Retención de datos: 5 años (cumplimiento SET) * Exportación a CSV y Excel * Analíticas avanzadas + log de auditoría * 1 sesión de onboarding incluida * Soporte prioritario por WhatsApp (respuesta en 4 horas hábiles) ### Enterprise (desde Gs. 600.000 / contribuyente / mes) [#enterprise-desde-gs-600000--contribuyente--mes] Para empresas grandes y proveedores de software que necesitan SLA contractual, white-label y volumen sin límites. Contrato personalizado. * Volumen ilimitado, multi-establecimiento sin límite * Usuarios y establecimientos ilimitados * KuDE white-label (sin marca Sifende) * Retención de datos ilimitada * Exportación a CSV, Excel, JSON y push vía API * SLA con penalidades contractuales * White-label de la plataforma * Soporte 24/7 por WhatsApp y teléfono * Contrato anual personalizado ## Notas importantes [#notas-importantes] * Por contribuyente (RUC): el precio es por cada RUC gestionado, no por cuenta. Una cuenta puede administrar varios contribuyentes; cada uno se factura según su plan. * Usuarios base incluidos: todos los planes incluyen 2 usuarios base sin costo adicional. * Sin contratos largos en planes mensuales: podés cambiar de plan o cancelar cuando quieras (excepto Enterprise, que es contrato anual). * Sandbox sin límites: todos los planes tienen acceso al ambiente de pruebas para integrar y validar antes de pasar a producción. Precios en Guaraníes (PYG), IVA incluido. La equivalencia en dólares es referencial al tipo de cambio del BCP. Vigente desde el 1 de mayo de 2026. ## ¿Qué plan elegir? [#qué-plan-elegir] | Si... | Plan recomendado | | ------------------------------------------------------------ | ---------------- | | Emitís menos de 100 facturas al mes y querés validar Sifende | Starter | | Tenés un solo local y necesitás todos los tipos de documento | Professional | | Operás varias sucursales o necesitás los 5 años de retención | Business | | Sos proveedor de software, necesitás SLA o white-label | Enterprise | ¿Listo para empezar? Mirá [Cómo funciona](/docs/plataforma/como-funciona) o saltá directo al [Inicio Rápido](/docs/inicio-rapido). # Autenticación (/docs/referencia/autenticacion) La API de Sifende soporta dos modos de autenticación según el caso de uso: ## API Key (integración ERP) [#api-key-integración-erp] El método principal para integraciones máquina a máquina. ### Formato del header [#formato-del-header] ``` Authorization: Bearer {tu-api-key} ``` El header es `Authorization: Bearer`, **no** `X-Api-Key`. El valor es tu API key completo incluyendo el prefijo `sk_live_` o `sk_test_`. ### Ejemplo [#ejemplo] ```bash curl https://api.sifende.com.py/api/v1/documento-electronico/status/{cdc} \ -H "Authorization: Bearer sk_live_abc123xyz..." ``` ### Obtener un API key [#obtener-un-api-key] 1. Ingresá al panel en [app.sifende.com.py](https://app.sifende.com.py) 2. Andá a **Configuración → API Keys** 3. Hacé click en **Nueva clave** 4. Guardá el valor — no se muestra de nuevo Cada API key está vinculado a **un solo contribuyente**. Si tenés múltiples contribuyentes, necesitás una clave por cada uno. ### Ciclo de vida del API key [#ciclo-de-vida-del-api-key] | Estado | Descripción | | -------- | ------------------------------------------------- | | Activo | Acepta requests | | Rotado | La clave anterior dejó de funcionar, usa la nueva | | Revocado | La clave fue eliminada permanentemente | Para rotar una clave sin downtime: ver [Rotar API Key](/docs/referencia/api-keys/rotar). ### Errores de autenticación [#errores-de-autenticación] | Status | Descripción | | ------------------ | ---------------------------------------------------- | | `401 Unauthorized` | API key ausente, inválido o revocado | | `403 Forbidden` | API key válido pero sin acceso al recurso solicitado | La respuesta `401` del filtro de autenticación tiene este formato (no es RFC 9457): ```json {"error": "Invalid or expired API key"} ``` *** ## Keycloak JWT (frontend personalizado) [#keycloak-jwt-frontend-personalizado] Para aplicaciones que construyen una interfaz propia sobre Sifende usando autenticación OAuth 2.0 / OpenID Connect. Si estás haciendo una integración ERP o un script backend, **no necesitás esto**. Usá API key. ### Flujo [#flujo] 1. El usuario se autentica vía Keycloak (OAuth 2.0) 2. Recibe un JWT `access_token` 3. Lo envía en cada request: `Authorization: Bearer {jwt}` Los endpoints de sesión usan el path `/api/v1/contribuyentes/{id}/...` y verifican que el usuario autenticado sea propietario del contribuyente. ### Errores de autenticación (sesión) [#errores-de-autenticación-sesión] ```json { "type": "https://sifende.com.py/probs/access-denied", "title": "Acceso denegado", "status": 403, "detail": "No tenés acceso a este contribuyente" } ``` *** ## ¿Cuál usar? [#cuál-usar] | Caso de uso | Auth recomendada | | -------------------------------- | ---------------- | | Integración ERP / script backend | API key | | Frontend web personalizado | Keycloak JWT | | Pruebas con curl / Postman | API key | | App móvil | Keycloak JWT | # Changelog (/docs/referencia/changelog) ## 2026-04-15 [#2026-04-15] ### Nuevo [#nuevo] * Endpoint `POST /nota-debito` para emitir Notas de Débito Electrónicas (NDE) * Soporte para receptor B2B en Facturas Electrónicas ### Corrección [#corrección] * Corrección en cálculo de IVA incluido para ítems `IVA_INCLUIDO` *** ## 2026-03-01 [#2026-03-01] ### Nuevo [#nuevo-1] * Soporte para Notas de Crédito Electrónicas (NCE) * Endpoint `GET /documentos-electronicos/{id}/xml` para descargar XML firmado *** *El changelog incluye solo cambios en la API pública. Ver el repositorio para el historial completo de cambios internos.* # Convenciones (/docs/referencia/convenciones) Esta página documenta las convenciones de datos específicas de Paraguay que aplican a toda la API de Sifende. ## RUC (Registro Único de Contribuyente) [#ruc-registro-único-de-contribuyente] El RUC es el número de identificación tributaria paraguayo. **Formato:** número + guión + dígito verificador ``` 80012345-1 ``` **En la API,** el RUC se envía en dos campos separados: * `numeroDocumento` o `ruc` — solo la parte numérica: `"80012345"` * `dv` o `digitoVerificador` — el dígito verificador: `"1"` **Validación:** Sifende valida el RUC contra el registro de SIFEN. Un RUC inválido o inexistente retorna `404 ruc-not-found`. ## Moneda — Guaraníes (PYG) [#moneda--guaraníes-pyg] El guaraní paraguayo **no tiene decimales** — todos los montos en PYG son enteros. ```json // Correcto "precioUnitario": 150000 // Incorrecto — no uses decimales en PYG "precioUnitario": 150000.00 ``` **Para otras monedas** (USD, BRL, etc.), sí se permiten decimales según la cantidad de decimales de la moneda. **Campos monetarios afectados:** `precioUnitario`, `montoPago`, `montoDescuento`, `montoTotal` y todos los sub-totales cuando `monedaOperacion` es `PYG`. ## Fechas y Timestamps [#fechas-y-timestamps] | Uso | Formato | Ejemplo | | ------------ | --------------------- | --------------------- | | Fecha y hora | ISO 8601 sin timezone | `2026-04-15T10:30:00` | | Solo fecha | ISO 8601 date | `2026-04-15` | No incluyas información de zona horaria en los timestamps. La API espera `LocalDateTime` de Paraguay (GMT-4). ## CDC (Código de Control del Documento Electrónico) [#cdc-código-de-control-del-documento-electrónico] El CDC es un identificador numérico de **44 dígitos** que identifica unívocamente cada documento en SIFEN. **Estructura del CDC (44 dígitos, 11 campos):** | Pos. | Largo | Campo | Descripción | | ----- | ----- | ------------------- | ----------------------------------------------------------- | | 1–2 | 2 | `iTiDE` | Tipo de documento (01=FE, 04=AFE, 05=NCE, 06=NDE, 07=NRE) | | 3–10 | 8 | RUC emisor | Parte numérica del RUC | | 11 | 1 | DV emisor | Dígito verificador del RUC | | 12–14 | 3 | Establecimiento | Código del establecimiento (001–999) | | 15–17 | 3 | Punto de expedición | Código del punto de expedición (001–999) | | 18–24 | 7 | Número de documento | Correlativo (0000001–9999999) | | 25 | 1 | `iTipCont` | Tipo de contribuyente emisor (1=persona física, 2=jurídica) | | 26–33 | 8 | Fecha emisión | `AAAAMMDD` | | 34 | 1 | `iTipEmi` | Tipo de emisión (1=Normal, 2=Contingencia) | | 35–43 | 9 | Código de seguridad | Aleatorio generado al firmar | | 44 | 1 | DV CDC | Dígito verificador del CDC completo | **Ejemplo:** ``` 01800123451001001000000122026042710000000006 ``` El CDC es generado por Sifende y retornado como respuesta al emitir un documento. **Guardalo en tu sistema** — es el identificador principal para todas las operaciones posteriores. ## Numeración de Documentos [#numeración-de-documentos] El número de documento se forma con tres componentes: ``` {establecimiento}-{puntoExpedicion}-{número} 001-001-0000001 ``` * **Establecimiento:** 3 dígitos, identifica la sucursal * **Punto de expedición:** 3 dígitos, identifica el punto de venta dentro del establecimiento * **Número:** 7 dígitos, auto-incremental por establecimiento/punto Sifende asigna los números automáticamente según tu timbrado. ## Enumeraciones SIFEN [#enumeraciones-sifen] Los campos de tipo enumeración usan valores de cadena descriptivos en la API (no los códigos numéricos internos de SIFEN): ```json // En la API de Sifende "tipoDocumento": "FACTURA_ELECTRONICA" // Código numérico de SIFEN (no usar en la API) // iTiDE = 1 ``` Para ver todos los valores disponibles, consultá [Enumeraciones](/docs/referencia/enumeraciones) o llamá a `GET /api/v1/public/enums`. ## Especificación OpenAPI [#especificación-openapi] > 📋 Planificado — la especificación OpenAPI estará disponible próximamente para facilitar la generación de clientes. ## SDKs [#sdks] > 📋 Planificado — no hay SDKs oficiales disponibles actualmente. Se recomienda implementar un cliente HTTP propio usando los ejemplos de esta documentación. # Enumeraciones (/docs/referencia/enumeraciones) Esta página lista todos los valores aceptados para los campos de tipo enumeración en la API. También podés obtener estos valores en tiempo real llamando a `GET /api/v1/public/enums`. Cada página de modelo (ej: [Factura Electrónica](/docs/referencia/modelos/factura-electronica)) incluye inline los valores válidos para cada campo de ese modelo. Esta página es la referencia global completa. ## tipoDocumento [#tipodocumento] | Valor | Código SIFEN | Descripción | Estado | | ------------------------------ | ------------ | ---------------------------- | ---------------- | | `FACTURA_ELECTRONICA` | 1 | Factura electrónica | ✅ Disponible | | `NOTA_DE_CREDITO_ELECTRONICA` | 5 | Nota de crédito electrónica | ✅ Disponible | | `NOTA_DE_DEBITO_ELECTRONICA` | 6 | Nota de débito electrónica | ✅ Disponible | | `AUTOFACTURA_ELECTRONICA` | 4 | Autofactura electrónica | 🚧 En desarrollo | | `NOTA_DE_REMISION_ELECTRONICA` | 7 | Nota de remisión electrónica | 🚧 En desarrollo | ## tipoEmision [#tipoemision] | Valor | Descripción | | -------------- | ---------------------------------------------- | | `NORMAL` | Emisión normal en línea con SIFEN | | `CONTINGENCIA` | Emisión en contingencia (sin conexión a SIFEN) | ## tipoTransaccion [#tipotransaccion] | Valor | Descripción | | ---------------------- | ------------------------------------ | | `VENTA_MERCADERIA` | Venta de mercadería | | `PRESTACION_SERVICIOS` | Prestación de servicios | | `MIXTO` | Venta de mercadería y servicios | | `VENTA_ACTIVO_FIJO` | Venta de activo fijo | | `VENTA_DIVISAS` | Venta de divisas | | `COMPRA_DIVISAS` | Compra de divisas | | `PROMOCION_O_MUESTRAS` | Promoción o entrega de muestras | | `DONACION` | Donación | | `ANTICIPO` | Anticipo | | `COMPRA_PRODUCTOS` | Compra de productos | | `COMPRA_SERVICIOS` | Compra de servicios | | `VENTA_CREDITO_FISCAL` | Venta de crédito fiscal | | `MUESTRAS_MEDICAS` | Muestras médicas (Art. 3 RG 24/2014) | ## afectacionTributaria (ítems) [#afectaciontributaria-ítems] | Valor | Descripción | IVA | | ----------------- | ----------------------------------------------------------- | ------- | | `GRAVADO` | Gravado con IVA — indicar la tasa en `tasaIVA` (`5` o `10`) | Sí | | `EXENTO` | Exento de IVA | No | | `EXONERADO` | Exonerado por ley específica | No | | `GRAVADO_PARCIAL` | Solo una porción del ítem está gravada | Parcial | ## condicionOperacion [#condicionoperacion] | Valor | Descripción | | --------- | --------------- | | `CONTADO` | Pago al contado | | `CREDITO` | Pago a crédito | ## tipoPago (condicionPago CONTADO) [#tipopago-condicionpago-contado] | Valor | Descripción | | ----------------------- | ------------------------------------------------------ | | `EFECTIVO` | Efectivo | | `CHEQUE` | Cheque | | `TARJETA_DE_CREDITO` | Tarjeta de crédito | | `TARJETA_DE_DEBITO` | Tarjeta de débito | | `TRANSFERENCIA` | Transferencia bancaria | | `GIRO` | Giro | | `BILLETERA_ELECTRONICA` | Billetera electrónica (Tigo Money, Personal Pay, etc.) | | `TARJETA_EMPRESARIAL` | Tarjeta empresarial | | `VALE` | Vale | | `RETENCION` | Retención | | `PAGO_POR_ANTICIPO` | Pago por anticipo | | `PAGO_MOVIL` | Pago móvil | | `PAGO_ELECTRONICO` | Pago electrónico | | `OTRO` | Otro medio de pago | ## monedaOperacion (CMondT) [#monedaoperacion-cmondt] | Valor | Descripción | | ----- | --------------------------------- | | `PYG` | Guaraní paraguayo (sin decimales) | | `USD` | Dólar estadounidense | | `BRL` | Real brasileño | | `ARS` | Peso argentino | | `EUR` | Euro | ## tipoOperacion (receptor) [#tipooperacion-receptor] | Valor | Descripción | | ----- | ------------------------------------------------- | | `B2B` | Venta a contribuyente (requiere RUC del receptor) | | `B2C` | Venta a consumidor final | | `B2G` | Venta a organismo público | | `B2F` | Venta a cliente del exterior (exportación) | ## naturalezaReceptor (`tipoContribuyente`) [#naturalezareceptor-tipocontribuyente] | Valor | Descripción | | ------------------ | ----------------------------------------------------- | | `CONTRIBUYENTE` | Receptor con RUC activo en SET (B2B / B2G) | | `NO_CONTRIBUYENTE` | Persona física sin RUC (B2C) | | `EXTRANJERO` | Cliente del exterior (B2F) | | `NOMINADO` | Receptor identificado por documento alternativo | | `INNOMINADO` | Sin identificación (solo permitido en FE bajo umbral) | ## tipoDocumentoReceptor [#tipodocumentoreceptor] | Valor | Descripción | | ---------------------- | ---------------------------------------- | | `CEDULA_PARAGUAYA` | Cédula de identidad paraguaya | | `PASAPORTE` | Pasaporte (nacionales y extranjeros) | | `CEDULA_EXTRANJERA` | Cédula de identidad extranjera | | `CARNET_DE_RESIDENCIA` | Carnet de residencia (inmigrantes) | | `INNOMINADO` | Sin identificación (solo FE bajo umbral) | | `TARJETA_DIPLOMATICA` | Tarjeta diplomática | | `OTRO` | Otro tipo de documento | ## unidadMedida (TcUniMed) [#unidadmedida-tcunimed] > Contenido en desarrollo — ver `GET /api/v1/public/enums` para la lista completa de unidades de medida SIFEN. # Errores (/docs/referencia/errores) ## Formato de respuesta de error [#formato-de-respuesta-de-error] La API de Sifende usa **RFC 9457 Problem Details** para todos los errores (excepto errores de autenticación): ```json { "type": "https://sifende.com.py/probs/validation-error", "title": "Error de validación", "status": 400, "detail": "La solicitud contiene 2 error(es) de validación", "errores": { "receptor.numeroDocumento": "Número de documento es obligatorio", "items[0].precioUnitario": "El precio no puede ser negativo" } } ``` Los endpoints de sesión (`/api/v1/contribuyentes/...`) usan un envelope `ApiResponse` diferente. Ver [API de Sesión](/docs/referencia/sesion). ### Error de autenticación (formato especial) [#error-de-autenticación-formato-especial] El filtro de API key retorna un JSON simple, **no** Problem Details: ```json {"error": "Invalid or expired API key"} ``` *** ## Tipos de error de Sifende [#tipos-de-error-de-sifende] | Tipo (`slug`) | Status | Descripción | | ---------------------------------------- | ------- | ----------------------------------------------------------- | | `validation-error` | 400 | Campos inválidos en el body — ver campo `errores` | | `invalid-enum-value` | 400 | Valor de enumeración no reconocido — ver `valoresAceptados` | | `invalid-format` | 400 | Formato de campo incorrecto (fecha, RUC, etc.) | | `contribuyente-not-found` | 404 | Contribuyente no encontrado por ID | | `documento-electronico-not-found` | 404 | DE no encontrado por ID o CDC | | `timbrado-not-found` | 404 | Timbrado no encontrado | | `api-key-not-found` | 404 | API key inexistente o revocado | | `certificate-not-found` | 404 | No hay certificado digital subido | | `ruc-not-found` | 404 | RUC no encontrado en el registro de SIFEN | | `evento-not-found` | 404 | Evento SIFEN no encontrado | | `access-denied` | 403 | Usuario sin acceso al contribuyente | | `duplicate-ruc` | 409 | RUC ya registrado para este usuario | | `duplicate-timbrado` | 409 | Número de timbrado ya existe | | `evento-cancelacion-error` | 400/409 | Error al enviar evento de cancelación | | `evento-inutilizacion-error` | 400/409 | Error al enviar evento de inutilización | | `documento-electronico-generation-error` | 422 | Error al generar el DE (problema de compliance SIFEN) | | `kude-generation-error` | 422 | Error al generar el KuDE PDF | | `kude-not-supported` | 501 | KuDE no disponible para este tipo de documento | ### Error de validación con detalle por campo [#error-de-validación-con-detalle-por-campo] ```json { "type": "https://sifende.com.py/probs/validation-error", "title": "Error de validación", "status": 400, "detail": "La solicitud contiene 1 error(es) de validación", "errores": { "items[0].tasaIVA": "La tasa de IVA debe ser 5, 10 o null para exentos" } } ``` ### Error de enumeración con valores aceptados [#error-de-enumeración-con-valores-aceptados] ```json { "type": "https://sifende.com.py/probs/invalid-enum-value", "title": "Valor de enumeración inválido", "status": 400, "detail": "El campo 'tipoDocumento' recibió 'INVOICE', que no es un valor permitido.", "campo": "tipoDocumento", "valorRecibido": "INVOICE", "valoresAceptados": [ {"codigo": "FACTURA_ELECTRONICA", "descripcion": "Factura electrónica"}, {"codigo": "NOTA_DE_CREDITO_ELECTRONICA", "descripcion": "Nota de crédito electrónica"}, {"codigo": "NOTA_DE_DEBITO_ELECTRONICA", "descripcion": "Nota de débito electrónica"} ] } ``` *** ## Códigos de respuesta SIFEN [#códigos-de-respuesta-sifen] Cuando SIFEN procesa un lote, retorna un código que Sifende interpreta: | Código SIFEN | Significado | Estado resultante | | ------------ | ------------------------------ | --------------------------- | | `0360` | Lote procesado correctamente | DE → APROBADO | | `0361` | Lote no existe en SIFEN | Reintento automático | | `0362` | Lote aún en procesamiento | Sifende reintenta más tarde | | `0363` | Lote con errores de validación | DE → RECHAZADO | | `0364` | Lote rechazado por SIFEN | DE → RECHAZADO | | `0320` | Evento procesado correctamente | Evento → PROCESADO | | `0340` | Evento rechazado | Evento → RECHAZADO | ### Códigos de rechazo de documentos individuales [#códigos-de-rechazo-de-documentos-individuales] Para ver el significado de los códigos de rechazo específicos (ej: `1108`, `1302`-`1306`) y cómo corregirlos, consultá [Rechazos SIFEN](/docs/solucion-problemas/rechazos-sifen). # Referencia API (/docs/referencia) ## URL base [#url-base] ``` https://api.sifende.com.py ``` Todos los endpoints usan el prefijo `/api/v1/`. ## Autenticación [#autenticación] Todas las llamadas a la API de integración requieren: ``` Authorization: Bearer {tu-api-key} ``` Ver [Autenticación](/docs/referencia/autenticacion) para más detalles. ## Secciones [#secciones] ### API de integración (API key) [#api-de-integración-api-key] La API principal para integraciones ERP: * [Documentos Electrónicos](/docs/referencia/documentos-electronicos) — emitir, consultar, cancelar, inutilizar * [Modelos de Datos](/docs/referencia/modelos) — schemas de FE, NCE, NDE y entidades compartidas * [Eventos SIFEN](/docs/referencia/eventos) — cancelación e inutilización * [API Keys](/docs/referencia/api-keys) — crear, rotar y revocar claves ### Datos de referencia [#datos-de-referencia] * [Catálogos](/docs/referencia/catalogos) — enumeraciones SIFEN, geografía, actividades económicas ### API de sesión (Keycloak JWT) [#api-de-sesión-keycloak-jwt] Para frontends personalizados sobre Sifende: * [Endpoints de Sesión](/docs/referencia/sesion) — contribuyentes, lotes, documentos, timbrado, usuarios ### Referencia global [#referencia-global] * [Autenticación](/docs/referencia/autenticacion) * [Convenciones](/docs/referencia/convenciones) — RUC, guaraníes, fechas, CDC * [Errores](/docs/referencia/errores) — Problem Details, códigos SIFEN * [Enumeraciones](/docs/referencia/enumeraciones) — todos los valores SIFEN válidos * [Versionado](/docs/referencia/versionado) * [Changelog](/docs/referencia/changelog) # Versionado (/docs/referencia/versionado) ## Versión actual [#versión-actual] **v1** — todos los endpoints usan el prefijo `/api/v1/`. ## Política de breaking changes [#política-de-breaking-changes] Un cambio **no** es breaking si: * Se agregan nuevos campos opcionales al request o response * Se agregan nuevos endpoints * Se agregan nuevos valores a enumeraciones existentes Un cambio **es** breaking si: * Se eliminan o renombran campos existentes * Se cambia el tipo de un campo * Se cambia el comportamiento observable de un endpoint * Se remueve un endpoint Los breaking changes se anuncian con al menos **30 días de anticipación** vía email y en el [Changelog](/docs/referencia/changelog). ## Deprecación [#deprecación] Cuando un endpoint se depreca: * Se documenta con una nota de deprecación en esta documentación * Se retorna el header `Sunset` con la fecha de remoción planeada * Se retorna el header `Deprecation` con la fecha en que fue deprecado ## Versiones futuras [#versiones-futuras] Cuando se lance v2, los endpoints v1 seguirán funcionando durante el período de migración. La documentación de v1 permanecerá disponible con un banner de deprecación. # Certificado Digital (/docs/solucion-problemas/certificado-digital) ## Certificado expirado [#certificado-expirado] Los certificados digitales tienen una fecha de vencimiento. Cuando el certificado expira, todos los intentos de emitir documentos fallan. **Síntomas:** Error `422 documento-electronico-generation-error` con mensaje de certificado inválido. **Solución:** Solicitá un nuevo certificado a la SET y subilo en Configuración → Certificado. ## Contraseña incorrecta [#contraseña-incorrecta] **Síntomas:** Error al subir el certificado indicando contraseña incorrecta. **Solución:** Verificá que la contraseña sea la del archivo `.p12`, no la contraseña de Marangatu. ## Formato incorrecto [#formato-incorrecto] Sifende acepta certificados en formato PKCS12 (extensión `.p12` o `.pfx`). **Síntomas:** Error al subir indicando formato inválido. **Solución:** Pedile a la SET el certificado en formato PKCS12. # Errores Comunes (/docs/solucion-problemas/errores-comunes) ## Error 401 — Invalid or expired API key [#error-401--invalid-or-expired-api-key] **Causa:** El API key es incorrecto, fue revocado, o el header está mal formateado. **Solución:** * Verificá que el header sea exactamente: `Authorization: Bearer {tu-api-key}` * No uses `X-API-Key` ni otros formatos * Si el key fue rotado, actualizá al nuevo valor ## Error 400 — validation-error en montos PYG [#error-400--validation-error-en-montos-pyg] **Causa:** Enviaste montos con decimales para PYG. **Solución:** Los montos en guaraníes son enteros. Cambiá `10000.00` → `10000`. ## Error 422 — documento-electronico-generation-error [#error-422--documento-electronico-generation-error] **Causa:** El documento no cumple con las validaciones de SIFEN. **Solución:** Revisá el campo `detail` del error Problem Details para el mensaje específico. Los problemas más comunes son: timbrado vencido, certificado expirado, campos faltantes para el tipo de documento. ## Error 404 — timbrado-not-found [#error-404--timbrado-not-found] **Causa:** No hay un timbrado configurado para el contribuyente. **Solución:** Configurá el timbrado electrónico en el panel de Sifende antes de emitir documentos. ## Error 404 — certificate-not-found [#error-404--certificate-not-found] **Causa:** No hay un certificado digital subido para el contribuyente. **Solución:** Subí tu certificado PKCS12 desde el panel en Configuración → Certificado. ## Documento queda en PENDIENTE por más de 5 minutos [#documento-queda-en-pendiente-por-más-de-5-minutos] **Causa:** El scheduler de Sifende podría tener un retraso o el lote está en reintento. **Solución:** Consultá el estado nuevamente. Si persiste por más de 15 minutos, contactá [soporte](/docs/solucion-problemas/soporte). # Preguntas Frecuentes (/docs/solucion-problemas/faq) ## ¿Qué es Sifende y cómo se diferencia de e-kuatia'i? [#qué-es-sifende-y-cómo-se-diferencia-de-e-kuatiai] Sifende es una plataforma API para integrar facturación electrónica en tu software, ERP o e-commerce. e-kuatia'i, en cambio, es la herramienta gratuita oficial de la DNIT pensada para emitir manualmente desde un navegador. Las diferencias principales: * API REST: Sifende ofrece una integración programática vía JSON; e-kuatia'i no. * Multi-establecimiento: Sifende permite varias sucursales (hasta 10 en Business, ilimitadas en Enterprise); e-kuatia'i está limitada a 1 establecimiento. * Alto volumen: Sifende procesa miles de DEs por minuto con reintentos automáticos. * Integración con ERP o software propio: flujo automatizado en vez de carga manual. * Soporte dedicado: Sifende incluye soporte humano (email, WhatsApp, 24/7 según el plan). ## ¿Qué tipos de documentos electrónicos puedo emitir? [#qué-tipos-de-documentos-electrónicos-puedo-emitir] Sifende soporta: * Factura Electrónica (FE): disponible * Nota de Crédito Electrónica (NCE): disponible * Nota de Débito Electrónica (NDE): disponible Próximamente: Autofactura Electrónica (AFE) y Nota de Remisión Electrónica (NRE) están en desarrollo. Todos los documentos se firman digitalmente, se transmiten a la DNIT y son rastreables por su CDC. ## ¿Necesito saber SOAP o XML para integrar Sifende? [#necesito-saber-soap-o-xml-para-integrar-sifende] No. Solo necesitás hacer un POST con JSON a la API REST. Sifende se encarga del resto: 1. Genera el XML según el Manual Técnico V150. 2. Firma con tu certificado P12. 3. Empaqueta en lotes. 4. Transmite vía SOAP a la DNIT. 5. Consulta los resultados. Tu equipo trabaja con JSON; Sifende absorbe toda la complejidad SOAP/XSD/XAdES. ## ¿Cómo manejo mi certificado digital P12? [#cómo-manejo-mi-certificado-digital-p12] Lo subís una sola vez desde el panel web. Sifende lo almacena de forma segura (cifrado en reposo) y lo usa automáticamente para firmar cada documento. Cuando esté próximo a vencer, recibís una notificación. Si tu certificado tiene problemas, mirá [Certificado Digital](/docs/solucion-problemas/certificado-digital). ## ¿Qué pasa si el servidor de la DNIT está caído? [#qué-pasa-si-el-servidor-de-la-dnit-está-caído] Sifende incluye un sistema de reintentos con backoff exponencial y clasificación de errores. Si la DNIT no responde, los documentos se encolan y se reenvían automáticamente cuando el servicio vuelve. Sifende mantiene un 99.9% de disponibilidad en su propia plataforma. Los problemas de la DNIT no detienen tu operación, solo demoran la confirmación del CDC. ## ¿Cuánto tiempo toma integrar Sifende? [#cuánto-tiempo-toma-integrar-sifende] Una integración básica se completa en horas, no en semanas. Solo necesitás hacer un POST HTTP con JSON. Para validar sin compromiso: * El plan Starter es gratuito e incluye 100 DEs por mes. * Todos los planes tienen acceso a un sandbox conectado al ambiente de pruebas de la DNIT. Andá al [Inicio Rápido](/docs/inicio-rapido) para emitir tu primer documento. ## ¿Sifende tiene un ambiente de sandbox? [#sifende-tiene-un-ambiente-de-sandbox] Sí. Podés probar con credenciales de prueba de SIFEN (certificado de pruebas + timbrado de pruebas) en el ambiente de testing de la DNIT. Cuando estés listo para producción, cambiás de credenciales: el contrato de la API es idéntico. Ver [Requisitos Previos](/docs/inicio-rapido/requisitos-previos) y [Ir a Producción](/docs/guias/ir-a-produccion). ## ¿Puedo usar Sifende sin tener el timbrado listo? [#puedo-usar-sifende-sin-tener-el-timbrado-listo] No. El timbrado electrónico es requerido para emitir cualquier documento. Podés completar la configuración de la cuenta y subir el certificado, pero no podrás emitir hasta tener el timbrado activo. ## ¿Los precios incluyen IVA? ¿Hay contratos? [#los-precios-incluyen-iva-hay-contratos] * Los precios incluyen IVA. Se facturan en guaraníes (PYG). * Sin contratos a largo plazo en planes mensuales: Starter, Professional y Business se cancelan cuando quieras. * Starter es gratis para siempre, sin tarjeta ni compromiso. * Enterprise se contrata por contrato anual, con SLA y penalidades. Detalle completo en [Planes y precios](/docs/plataforma/planes). ## ¿Sifende cumple con todas las Notas Técnicas de la DNIT? [#sifende-cumple-con-todas-las-notas-técnicas-de-la-dnit] Sí. Sifende implementa el Manual Técnico V150 y todas las Notas Técnicas vigentes (NT-001 a NT-026 a la fecha). Mantenemos la plataforma al día con cada cambio normativo de la DNIT, así no tenés que monitorearlas vos. ## ¿Hay un límite de requests por minuto? [#hay-un-límite-de-requests-por-minuto] > Contenido en desarrollo. La política de rate limiting se documentará próximamente. ## ¿Sifende tiene SDKs oficiales? [#sifende-tiene-sdks-oficiales] > 📋 Planificado. Actualmente no hay SDKs oficiales. Recomendamos implementar un cliente HTTP usando los ejemplos de esta documentación. ## ¿Puedo descargar la especificación OpenAPI? [#puedo-descargar-la-especificación-openapi] > 📋 Planificado. La especificación OpenAPI estará disponible próximamente. ## ¿Cómo facturo a una empresa extranjera? [#cómo-facturo-a-una-empresa-extranjera] Usá `tipoDocumento: "PASAPORTE"` (o `"CARNET_DE_RESIDENCIA"` para residentes, `"CEDULA_EXTRANJERA"` cuando aplique) para el receptor extranjero, junto con `monedaOperacion` en la moneda correspondiente y `pais` con el código ISO del país. Los valores aceptados para `tipoDocumento` son: `CEDULA_PARAGUAYA`, `PASAPORTE`, `CEDULA_EXTRANJERA`, `CARNET_DE_RESIDENCIA`, `INNOMINADO`, `TARJETA_DIPLOMATICA`, `OTRO`. Consultá [Enumeraciones](/docs/referencia/enumeraciones#tipodocumentoreceptor) para la referencia completa. Ver [Moneda Extranjera](/docs/guias/moneda-extranjera). # Solución de Problemas (/docs/solucion-problemas) ## ¿Dónde está el problema? [#dónde-está-el-problema] Usá este árbol para encontrar la sección correcta: * **Error HTTP 400/422 al emitir un documento** → [Errores Comunes](/docs/solucion-problemas/errores-comunes) * **Documento en estado RECHAZADO por SIFEN** → [Rechazos SIFEN](/docs/solucion-problemas/rechazos-sifen) * **Problema con el certificado digital** → [Certificado Digital](/docs/solucion-problemas/certificado-digital) * **Error de autenticación (401)** → [Autenticación](/docs/referencia/autenticacion) * **Pregunta general** → [FAQ](/docs/solucion-problemas/faq) * **Necesitás ayuda humana** → [Soporte](/docs/solucion-problemas/soporte) # Rechazos SIFEN (/docs/solucion-problemas/rechazos-sifen) Cuando SIFEN procesa un documento, devuelve un código de respuesta y un mensaje. Sifende guarda esos datos en el documento y los expone vía [`GET /status/:cdc`](/docs/referencia/documentos-electronicos/consultar-estado). ## Tipos de resultado SIFEN [#tipos-de-resultado-sifen] SIFEN clasifica cada respuesta con un tipo: | Tipo | Significado | Resultado en Sifende | | ---- | ------------------------ | ---------------------------------------------------------------------------------------------------------- | | A | Aprobado | `estado: APROBADO`. DE registrado y válido. | | AO | Aprobado con Observación | `estado: APROBADO_OBSERVACION`. DE registrado pero con advertencia (`mensajeRechazo` contiene el detalle). | | R | Rechazado | `estado: RECHAZADO`. El DE no quedó registrado en SIFEN; hay que corregir y reenviar. | ## Formato del rechazo [#formato-del-rechazo] Cuando un DE es rechazado, el campo `mensajeRechazo` contiene el código y la descripción retornados por SIFEN: ```json { "estado": "RECHAZADO", "mensajeRechazo": "[1108] Fecha fin de vigencia del timbrado incorrecta (timbrado vencido)" } ``` El campo se llama siempre `mensajeRechazo`. Si tu sistema buscaba `motivoRechazo`, actualizalo: ese nombre nunca existió en la API de Sifende. ## Timbrado y numeración [#timbrado-y-numeración] Errores que indican un problema con el timbrado, el establecimiento, el punto de expedición o el correlativo asignado. | Código | Campo | Tipo | Descripción | Solución | | ------ | ----- | ---- | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | | `1003` | A003 | R | DV del CDC inválido (el DV calculado no coincide) | No deberías ver esto: Sifende calcula el DV. Si aparece, contactá soporte. | | `1101` | C004 | R | Número de timbrado inválido | Verificá el timbrado configurado en `/contribuyentes/:id/timbrado` y que coincida con el del Registro de SIFEN. | | `1105` | C005 | R | Código de establecimiento incorrecto | Confirmá que `numeroEstablecimiento` esté registrado en tu timbrado activo. | | `1106` | C006 | R | Punto de expedición incorrecto | Confirmá que `puntoExpedicion` esté autorizado para ese establecimiento. | | `1107` | C008 | R | Fecha de inicio de vigencia del timbrado incorrecta | Verificá que la fecha de emisión sea posterior al inicio de vigencia. | | `1108` | C009 | R | Fecha fin de vigencia del timbrado incorrecta (timbrado vencido) | Renová el timbrado en SET y actualizá la fecha en Sifende. | | `1109` | C007 | R | Número de documento ha sido inutilizado anteriormente | Ese correlativo ya fue inutilizado vía evento. Sifende debería saltarlo automáticamente; si persiste, contactá soporte. | ## Datos del receptor [#datos-del-receptor] Errores en el bloque D del XML, relacionados con el receptor del documento. La mayoría se debe a discrepancias entre `tipoOperacion` (B2B / B2C / B2G / B2F) y los datos enviados. | Código | Campo | Tipo | Descripción | Solución | | ------ | ----- | ---- | ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | | `1150` | D002 | R | Fecha y hora de emisión con más de 30 días de atraso | SIFEN solo acepta DEs emitidos hasta 30 días antes del envío. Emitilo dentro de la ventana o usá un timbrado de contingencia. | | `1300` | D202 | R | Tipo de operación no compatible con la naturaleza del receptor | Revisá la combinación `tipoOperacion` × `tipoContribuyente` (ej: B2B requiere `CONTRIBUYENTE`). | | `1301` | D204 | R | Descripción del país receptor no corresponde al código | El código ISO de `pais` no coincide con la descripción almacenada. Usá el catálogo `pais` de `/public/enums`. | | `1302` | D205 | R | Es obligatorio informar el tipo de contribuyente receptor (para B2B) | Para B2B, asegurate de enviar `tipoContribuyente` con un valor distinto de `NO_CONTRIBUYENTE`. | | `1303` | D205a | R | Tipo de contribuyente receptor inválido (informado cuando es NO\_CONTRIBUYENTE) | No envíes `tipoContribuyente` distinto de `NO_CONTRIBUYENTE` para operaciones B2C. | | `1304` | D206 | R | Es obligatorio informar el RUC del receptor contribuyente (para B2B) | Para B2B, `numeroDocumento` debe ser el RUC y `digitoVerificador` el DV correspondiente. | | `1305` | D206a | R | RUC del receptor no requerido (informado cuando es NO\_CONTRIBUYENTE) | No envíes RUC en B2C. Usá `tipoDocumento = CEDULA_PARAGUAYA` u otro. | | `1306` | D206b | R | RUC del receptor inexistente en Marangatu | El RUC no está registrado en SET. Verificalo o pedile al cliente que lo confirme. | | `1307` | D206c | R | RUC del receptor en estado CANCELADO o SUSPENSIÓN TEMPORAL | El RUC del cliente no está activo. No podés facturarle como B2B hasta que regularice. | | `1309` | D207 | R | DV del RUC del receptor incorrecto | Recalculá el DV del RUC del receptor con el algoritmo oficial. | | `1319` | D208b | R | Documento INNOMINADO no permitido para esta operación | NCE/NDE no admiten receptor INNOMINADO. Identificá al receptor con CI, RUC u otro documento. | | `1321` | D208c | R | INNOMINADO no permitido cuando el total supera el umbral | El monto excede el umbral DNIT para receptor sin identificación. Pedí los datos al cliente. | ## Contenido del documento [#contenido-del-documento] Errores en los grupos E (campos específicos por tipo) y F (totales). | Código | Campo | Tipo | Descripción | Solución | | ------ | ----- | ---- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | | `1503` | E600 | R | Condición de operación inválida | Revisá `condicionOperacion` (`CONTADO` o `CREDITO`) y los campos asociados. | | `1907` | E734 | R | Tasa de IVA inválida | Las tasas válidas son `0`, `5` y `10`. Verificá `tasaIVA` en cada ítem. | | `2362` | F008 | R | Cálculo del total de la operación incorrecto | Sifende calcula los totales; si ves esto, hay datos inconsistentes en los ítems. Contactá soporte con el CDC. | ## Eventos (cancelación / inutilización) [#eventos-cancelación--inutilización] | Código | Campo | Tipo | Descripción | Solución | | ------ | ------ | ---- | --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | `4001` | Evento | R | CDC inválido en evento de cancelación/inutilización | Verificá que el CDC tenga 44 dígitos y corresponda a un DE aprobado. Cancelaciones solo aplican a DEs en estado `APROBADO` o `APROBADO_OBSERVACION`. | ## ¿El código no está en la tabla? [#el-código-no-está-en-la-tabla] SIFEN tiene cientos de códigos. Si tu mensaje no aparece arriba: 1. Revisá [Errores comunes](/docs/solucion-problemas/errores-comunes) por si el problema está en el request HTTP (antes de SIFEN). 2. Consultá el Manual Técnico de SIFEN V150 §12 o la NT correspondiente. 3. [Contactá soporte](/docs/solucion-problemas/soporte) con el CDC y el `mensajeRechazo` completo. # Soporte (/docs/solucion-problemas/soporte) ## Canales de soporte [#canales-de-soporte] ### Email técnico [#email-técnico] Para problemas de integración, errores no documentados o preguntas sobre la API: **[soporte@sifende.com.py](mailto:soporte@sifende.com.py)** Incluí en tu mensaje: * Tu RUC de contribuyente * El CDC del documento afectado (si aplica) * El body completo del request y la respuesta de error * El ambiente (QA / Producción) ### Panel de Sifende [#panel-de-sifende] Para problemas de configuración (certificado, timbrado, API keys): **[app.sifende.com.py](https://app.sifende.com.py)** ## Antes de contactar soporte [#antes-de-contactar-soporte] Revisá primero: 1. [Errores Comunes](/docs/solucion-problemas/errores-comunes) 2. [Rechazos SIFEN](/docs/solucion-problemas/rechazos-sifen) 3. [FAQ](/docs/solucion-problemas/faq) 4. La [Referencia de Errores](/docs/referencia/errores) para interpretar el código de error # Crear API Key (/docs/referencia/api-keys/crear) ## POST /api/v1/contribuyentes/:contribuyenteId/api-keys [#post-apiv1contribuyentescontribuyenteidapi-keys] Crea un nuevo API key para el contribuyente indicado. La clave en texto plano se devuelve **una única vez** en esta respuesta. ### Autenticación [#autenticación] `Authorization: Bearer {jwt}` — JWT de Keycloak. El usuario autenticado debe ser propietario del contribuyente. ### Path parameters [#path-parameters] | Parámetro | Tipo | Descripción | | ----------------- | --------- | ---------------------------- | | `contribuyenteId` | `integer` | ID interno del contribuyente | ### Request body [#request-body] ```json { "nombre": "Integración ERP Producción", "expiraEn": "2027-01-01T00:00:00" } ``` | Campo | Tipo | Req. | Descripción | | ---------- | ---------- | ---- | -------------------------------------------------------------- | | `nombre` | `string` | Sí | Nombre descriptivo de la clave (máx. 100 caracteres) | | `expiraEn` | `datetime` | No | Fecha de expiración ISO 8601. Si es `null`, la clave no expira | ### Respuesta exitosa [#respuesta-exitosa] **Status:** `201 Created` ```json { "status": "success", "payload": { "apiKeyId": 42, "nombre": "Integración ERP Producción", "key": "sk_live_8f3a9c2e7b5d4a1f6e0c9b8a7d3f2e1c", "keyPrefix": "sk_live_8f3a9c2e", "estaActivo": true, "ultimoUso": null, "expiraEn": "2027-01-01T00:00:00", "fechaCreacion": "2026-04-27T14:32:18" } } ``` El campo `key` se muestra **únicamente** en esta respuesta. No se almacena en texto plano y no puede recuperarse después. Guardalo de forma segura inmediatamente — si lo perdés, vas a tener que rotar la clave. El prefijo `sk_live_` indica una clave de producción; `sk_test_` corresponde al ambiente de prueba. ### Errores [#errores] | Status | Tipo | Descripción | | ------ | ------------------------- | ---------------------------------------------- | | 401 | — | JWT inválido, expirado o ausente | | 403 | `access-denied` | El usuario no es propietario del contribuyente | | 404 | `contribuyente-not-found` | Contribuyente no existe | | 422 | `validation-error` | `nombre` ausente o supera los 100 caracteres | ### Ejemplos [#ejemplos] ```bash curl -X POST https://api.sifende.com.py/api/v1/contribuyentes/42/api-keys \ -H "Authorization: Bearer $JWT" \ -H "Content-Type: application/json" \ -d '{ "nombre": "Integración ERP Producción", "expiraEn": "2027-01-01T00:00:00" }' ``` ```ts const res = await fetch( `https://api.sifende.com.py/api/v1/contribuyentes/${contribuyenteId}/api-keys`, { method: 'POST', headers: { Authorization: `Bearer ${jwt}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ nombre: 'Integración ERP Producción', expiraEn: '2027-01-01T00:00:00', }), } ); const { payload } = await res.json(); // ¡Guardá payload.key inmediatamente! No se va a mostrar de nuevo. console.log('API Key:', payload.key); ``` # Eliminar API Key (/docs/referencia/api-keys/eliminar) ## DELETE /api/v1/contribuyentes/:contribuyenteId/api-keys/:id [#delete-apiv1contribuyentescontribuyenteidapi-keysid] Elimina permanentemente un API key. La clave queda revocada y no puede recuperarse. ### Autenticación [#autenticación] `Authorization: Bearer {jwt}` — JWT de Keycloak. El usuario debe ser propietario del contribuyente. ### Path parameters [#path-parameters] | Parámetro | Tipo | Descripción | | ----------------- | --------- | --------------------------------- | | `contribuyenteId` | `integer` | ID interno del contribuyente | | `id` | `integer` | `apiKeyId` de la clave a eliminar | ### Respuesta exitosa [#respuesta-exitosa] **Status:** `204 No Content` Sin cuerpo. La eliminación tiene **efecto inmediato**. Cualquier integración que use la clave eliminada recibirá `401 Unauthorized` en el próximo request. La operación es **permanente** — no hay forma de restaurar una clave eliminada. Si solo querés generar un nuevo secreto manteniendo el registro, usá [Rotar API Key](/docs/referencia/api-keys/rotar) en su lugar. ### Errores [#errores] | Status | Tipo | Descripción | | ------ | ------------------- | ------------------------------------------------------- | | 401 | — | JWT inválido, expirado o ausente | | 403 | `access-denied` | El usuario no es propietario del contribuyente | | 404 | `api-key-not-found` | El `apiKeyId` no existe o no pertenece al contribuyente | ### Ejemplo [#ejemplo] ```bash curl -X DELETE \ https://api.sifende.com.py/api/v1/contribuyentes/42/api-keys/42 \ -H "Authorization: Bearer $JWT" ``` # API Keys (/docs/referencia/api-keys) Cada API key está vinculado a un contribuyente específico y permite autenticar llamadas a la API de integración. ## Endpoints [#endpoints] | Método | Path | Descripción | | -------- | ---------------------------------------------------- | ------------------------------------------------------ | | `POST` | `/api/v1/contribuyentes/{id}/api-keys` | [Crear API key](/docs/referencia/api-keys/crear) | | `GET` | `/api/v1/contribuyentes/{id}/api-keys` | [Listar API keys](/docs/referencia/api-keys/listar) | | `POST` | `/api/v1/contribuyentes/{id}/api-keys/{keyId}/rotar` | [Rotar API key](/docs/referencia/api-keys/rotar) | | `DELETE` | `/api/v1/contribuyentes/{id}/api-keys/{keyId}` | [Eliminar API key](/docs/referencia/api-keys/eliminar) | > **Nota:** Estos endpoints usan autenticación Keycloak JWT (sesión de usuario del panel), no API key. # Listar API Keys (/docs/referencia/api-keys/listar) ## GET /api/v1/contribuyentes/:contribuyenteId/api-keys [#get-apiv1contribuyentescontribuyenteidapi-keys] Lista todos los API keys del contribuyente. **Nunca** devuelve la clave en texto plano — solo el prefijo identificador. ### Autenticación [#autenticación] `Authorization: Bearer {jwt}` — JWT de Keycloak. El usuario debe ser propietario del contribuyente. ### Path parameters [#path-parameters] | Parámetro | Tipo | Descripción | | ----------------- | --------- | ---------------------------- | | `contribuyenteId` | `integer` | ID interno del contribuyente | ### Respuesta exitosa [#respuesta-exitosa] **Status:** `200 OK` ```json { "status": "success", "payload": [ { "apiKeyId": 42, "nombre": "Integración ERP Producción", "keyPrefix": "sk_live_8f3a9c2e", "estaActivo": true, "ultimoUso": "2026-04-27T09:15:42", "expiraEn": "2027-01-01T00:00:00", "fechaCreacion": "2026-01-15T11:00:00" }, { "apiKeyId": 43, "nombre": "Pruebas locales", "keyPrefix": "sk_test_5b7d1e9f", "estaActivo": true, "ultimoUso": null, "expiraEn": null, "fechaCreacion": "2026-03-20T16:45:00" } ] } ``` ### Campos del ApiKeyDTO [#campos-del-apikeydto] | Campo | Tipo | Descripción | | --------------- | ---------------- | ------------------------------------------------------------ | | `apiKeyId` | `integer` | ID interno de la clave | | `nombre` | `string` | Nombre descriptivo asignado al crearla | | `keyPrefix` | `string` | Primeros caracteres de la clave (para identificación visual) | | `estaActivo` | `boolean` | `true` si la clave acepta requests | | `ultimoUso` | `datetime\|null` | Última vez que se autenticó con esta clave | | `expiraEn` | `datetime\|null` | Fecha de expiración. `null` = no expira | | `fechaCreacion` | `datetime` | Fecha de creación de la clave | ### Errores [#errores] | Status | Tipo | Descripción | | ------ | ------------------------- | ---------------------------------------------- | | 401 | — | JWT inválido, expirado o ausente | | 403 | `access-denied` | El usuario no es propietario del contribuyente | | 404 | `contribuyente-not-found` | Contribuyente no existe | ### Ejemplo [#ejemplo] ```bash curl https://api.sifende.com.py/api/v1/contribuyentes/42/api-keys \ -H "Authorization: Bearer $JWT" ``` # Rotar API Key (/docs/referencia/api-keys/rotar) ## POST /api/v1/contribuyentes/:contribuyenteId/api-keys/:id/rotar [#post-apiv1contribuyentescontribuyenteidapi-keysidrotar] Genera una nueva clave en texto plano para un API key existente. La clave anterior queda **inmediatamente revocada**. ### Autenticación [#autenticación] `Authorization: Bearer {jwt}` — JWT de Keycloak. El usuario debe ser propietario del contribuyente. ### Path parameters [#path-parameters] | Parámetro | Tipo | Descripción | | ----------------- | --------- | ------------------------------ | | `contribuyenteId` | `integer` | ID interno del contribuyente | | `id` | `integer` | `apiKeyId` de la clave a rotar | ### Request body [#request-body] Sin cuerpo. La operación se invoca solo con el `POST`. ### Respuesta exitosa [#respuesta-exitosa] **Status:** `200 OK` ```json { "status": "success", "payload": { "apiKeyId": 42, "nombre": "Integración ERP Producción", "key": "sk_live_2d6f8a1c4b9e7d3f0a5c8b6e9d1f4a7c", "keyPrefix": "sk_live_2d6f8a1c", "estaActivo": true, "ultimoUso": null, "expiraEn": "2027-01-01T00:00:00", "fechaCreacion": "2026-01-15T11:00:00" } } ``` La rotación **invalida la clave anterior al instante**. Cualquier integración que siga usando la clave vieja recibirá `401 Unauthorized` en el siguiente request. El nuevo valor `key` se muestra **una única vez**. Guardalo inmediatamente — no se puede recuperar después. ### Patrón recomendado (sin downtime) [#patrón-recomendado-sin-downtime] Llamá al endpoint de rotación y guardá la nueva `key` en tu vault de secretos. Actualizá la configuración de tu integración con la nueva clave. Reiniciá o recargá el servicio para que tome la nueva variable. Verificá que los nuevos requests responden `200` . Si tu integración corre en múltiples instancias, hacé el rollout gradual: la clave nueva ya está activa, pero la vieja deja de funcionar. Coordiná la actualización antes de invocar `rotar`. ### Errores [#errores] | Status | Tipo | Descripción | | ------ | ------------------- | ------------------------------------------------------- | | 401 | — | JWT inválido, expirado o ausente | | 403 | `access-denied` | El usuario no es propietario del contribuyente | | 404 | `api-key-not-found` | El `apiKeyId` no existe o no pertenece al contribuyente | ### Ejemplo [#ejemplo] ```bash curl -X POST \ https://api.sifende.com.py/api/v1/contribuyentes/42/api-keys/42/rotar \ -H "Authorization: Bearer $JWT" ``` # Actividades Económicas (/docs/referencia/catalogos/actividades-economicas) ## GET /api/v1/actividades-economicas [#get-apiv1actividades-economicas] Devuelve el árbol completo de actividades económicas (códigos CIIU adaptados a Paraguay). Usado durante la configuración del contribuyente para seleccionar las actividades declaradas ante SIFEN. ### Autenticación [#autenticación] `Authorization: Bearer {jwt}` — JWT de Keycloak. Endpoint protegido. ### Respuesta exitosa [#respuesta-exitosa] **Status:** `200 OK` Devuelve un `List` con la jerarquía completa (1.051 registros). El árbol tiene 4 niveles: sector → división → grupo → clase. ```json [ { "codigo": "G", "descripcion": "Comercio al por mayor y al por menor", "nivel": 1, "padreCodigo": null, "sector": "G", "esHoja": false, "hijos": [ { "codigo": "47", "descripcion": "Comercio al por menor", "nivel": 2, "padreCodigo": "G", "sector": "G", "esHoja": false, "hijos": [ { "codigo": "471", "descripcion": "Venta al por menor en almacenes no especializados", "nivel": 3, "padreCodigo": "47", "sector": "G", "esHoja": false, "hijos": [ { "codigo": "47110", "descripcion": "Venta al por menor en almacenes no especializados con predominio de la venta de alimentos, bebidas o tabaco", "nivel": 4, "padreCodigo": "471", "sector": "G", "esHoja": true, "hijos": [] } ] } ] } ] } ] ``` ### Campos del ActividadEconomicaDTO [#campos-del-actividadeconomicadto] | Campo | Tipo | Descripción | | ------------- | -------------- | --------------------------------------------- | | `codigo` | `string` | Código CIIU (1 a 5 caracteres según el nivel) | | `descripcion` | `string` | Descripción oficial | | `nivel` | `integer` | Nivel jerárquico (1 a 4) | | `padreCodigo` | `string\|null` | Código del nodo padre | | `sector` | `string` | Letra del sector raíz (`A`-`U`) | | `esHoja` | `boolean` | `true` si es nodo terminal seleccionable | | `hijos` | `array` | Subnodos del árbol | ### Solo las hojas son seleccionables [#solo-las-hojas-son-seleccionables] Solo los registros con `esHoja: true` pueden asignarse a un contribuyente. Los nodos intermedios (sectores, divisiones, grupos) sirven para navegar el árbol pero **no** son válidos como actividad declarada. Filtrar las hojas con `jq`: ```bash curl https://api.sifende.com.py/api/v1/actividades-economicas \ -H "Authorization: Bearer $JWT" \ | jq '[.. | objects | select(.esHoja == true)]' ``` ### Errores [#errores] | Status | Tipo | Descripción | | ------ | ---- | -------------------------------- | | 401 | — | JWT inválido, expirado o ausente | ### Ejemplo [#ejemplo] ```bash curl https://api.sifende.com.py/api/v1/actividades-economicas \ -H "Authorization: Bearer $JWT" ``` # Enumeraciones SIFEN (Endpoint) (/docs/referencia/catalogos/enumeraciones) ## GET /api/v1/public/enums [#get-apiv1publicenums] Devuelve todas las enumeraciones SIFEN soportadas con sus valores y etiquetas. Endpoint **público** — no requiere autenticación. CORS habilitado para uso desde cualquier frontend. ### Autenticación [#autenticación] Ninguna. No envíes `Authorization`. ### Respuesta exitosa [#respuesta-exitosa] **Status:** `200 OK` Mapa de 18 categorías. Cada entrada es un array de `EnumValueDTO`: ```json { "tipoDocumento": [ { "name": "FACTURA_ELECTRONICA", "label": "Factura Electrónica", "val": 1 }, { "name": "AUTOFACTURA_ELECTRONICA", "label": "Autofactura Electrónica", "val": 4 }, { "name": "NOTA_DE_CREDITO_ELECTRONICA", "label": "Nota de Crédito Electrónica", "val": 5 }, { "name": "NOTA_DE_DEBITO_ELECTRONICA", "label": "Nota de Débito Electrónica", "val": 6 }, { "name": "NOTA_REMISION_ELECTRONICA", "label": "Nota de Remisión Electrónica", "val": 7 } ], "tipoEmision": [ { "name": "NORMAL", "label": "Normal", "val": 1 }, { "name": "CONTINGENCIA", "label": "Contingencia", "val": 2 } ], "moneda": [...], "...": "..." } ``` ### EnumValueDTO [#enumvaluedto] | Campo | Tipo | Descripción | | ------- | --------- | ---------------------------------------------------- | | `name` | `string` | Identificador en mayúsculas usado al enviar requests | | `label` | `string` | Texto legible para mostrar en UI | | `val` | `integer` | Código numérico SIFEN (cuando aplica) | Al enviar requests, usá siempre el valor del campo `name` (string), nunca el `val` numérico. El `val` se expone solo como referencia al código SIFEN — la API rechaza enums numéricos con `400 invalid-enum-value`. ### Categorías disponibles [#categorías-disponibles] | Clave | Descripción | Uso | | ----------------------- | ------------------------------------ | --------------------------------------- | | `tipoDocumento` | Tipo de documento electrónico | Campo `tipoDocumento` | | `tipoEmision` | Tipo de emisión | Campo `tipoEmision` | | `tipoTransaccion` | Tipo de transacción | Campo `tipoTransaccion` (solo FE) | | `condicionOperacion` | Condición de operación | Campo `condicionOperacion` | | `tipoPago` | Tipo de pago | Campo `condicionPago.tipoPago` | | `naturalezaReceptor` | Naturaleza del receptor | Campo `receptor.tipoContribuyente` | | `tipoOperacion` | Tipo de operación B2B/B2C | Campo `receptor.tipoOperacion` | | `tipoDocumentoReceptor` | Tipo de documento del receptor | Campo `receptor.tipoDocumento` | | `unidadMedida` | Unidades de medida (con abreviatura) | Campo `items[].unidadMedida` | | `afectacionIVA` | Afectación tributaria | Campo `items[].afectacionTributaria` | | `motivoEmision` | Motivo de emisión (NCE/NDE) | Campo `motivoEmision` | | `tipoDocumentoAsociado` | Tipo de documento asociado | Campo `documentoAsociado.tipoDocumento` | | `moneda` | Monedas comunes (15 valores) | Campo `monedaOperacion` | | `pais` | Países comunes (16 valores) | Campo `receptor.pais` | Usá este endpoint para poblar dropdowns y selects en tu UI. Los valores se mantienen sincronizados con las versiones de SIFEN aceptadas por la API. Para la referencia estática completa, ver [Enumeraciones](/docs/referencia/enumeraciones). ### Errores [#errores] Este endpoint no produce errores de autenticación. Solo `500` en caso de fallo del servidor. ### Ejemplo [#ejemplo] ```bash curl https://api.sifende.com.py/api/v1/public/enums ``` Filtrar una categoría específica con `jq`: ```bash curl https://api.sifende.com.py/api/v1/public/enums | jq '.tipoDocumento' ``` # Geografía (/docs/referencia/catalogos/geografia) ## Geografía de Paraguay [#geografía-de-paraguay] Tres endpoints públicos jerárquicos para poblar selectores de dirección. Se usan principalmente al configurar el contribuyente (`departamentoId`, `distritoId`, `ciudadId`). ### Autenticación [#autenticación] Ninguna. Los tres endpoints son públicos. *** ## GET /api/v1/geografia/departamentos [#get-apiv1geografiadepartamentos] Lista los 18 departamentos de Paraguay. ### Respuesta exitosa [#respuesta-exitosa] **Status:** `200 OK` ```json [ { "departamentoId": 1, "codigo": "01", "nombre": "Concepción" }, { "departamentoId": 2, "codigo": "02", "nombre": "San Pedro" }, { "departamentoId": 11, "codigo": "11", "nombre": "Central" }, { "departamentoId": 0, "codigo": "00", "nombre": "Capital" } ] ``` ### Ejemplo [#ejemplo] ```bash curl https://api.sifende.com.py/api/v1/geografia/departamentos ``` *** ## GET /api/v1/geografia/departamentos/:departamentoId/distritos [#get-apiv1geografiadepartamentosdepartamentoiddistritos] Lista los distritos de un departamento. ### Path parameters [#path-parameters] | Parámetro | Tipo | Descripción | | ---------------- | --------- | ------------------- | | `departamentoId` | `integer` | ID del departamento | ### Respuesta exitosa [#respuesta-exitosa-1] **Status:** `200 OK` ```json [ { "distritoId": 105, "departamentoId": 11, "codigo": "01", "nombre": "Asunción" }, { "distritoId": 106, "departamentoId": 11, "codigo": "02", "nombre": "Lambaré" }, { "distritoId": 107, "departamentoId": 11, "codigo": "03", "nombre": "Fernando de la Mora" } ] ``` ### Ejemplo [#ejemplo-1] ```bash curl https://api.sifende.com.py/api/v1/geografia/departamentos/11/distritos ``` *** ## GET /api/v1/geografia/distritos/:distritoId/ciudades [#get-apiv1geografiadistritosdistritoidciudades] Lista las ciudades de un distrito. ### Path parameters [#path-parameters-1] | Parámetro | Tipo | Descripción | | ------------ | --------- | --------------- | | `distritoId` | `integer` | ID del distrito | ### Respuesta exitosa [#respuesta-exitosa-2] **Status:** `200 OK` ```json [ { "ciudadId": 2401, "distritoId": 105, "codigo": "01", "nombre": "Asunción" }, { "ciudadId": 2402, "distritoId": 105, "codigo": "02", "nombre": "Sajonia" } ] ``` ### Ejemplo [#ejemplo-2] ```bash curl https://api.sifende.com.py/api/v1/geografia/distritos/105/ciudades ``` *** ## Patrón de uso jerárquico [#patrón-de-uso-jerárquico] Cargá los departamentos al iniciar el formulario y mostralos en un select. Cuando el usuario elige uno, llamá a `/departamentos/:id/distritos` y poblá el segundo select. Cuando elige distrito, llamá a `/distritos/:id/ciudades` para el tercer select. Guardá los tres IDs ( `departamentoId` , `distritoId` , `ciudadId` ) al crear o actualizar un contribuyente. Los `codigo` son los códigos oficiales SIFEN; los `id` son internos de la API. Al enviar datos a `/contribuyentes`, usá los **IDs** internos, no los códigos. ### Errores [#errores] | Status | Tipo | Descripción | | ------ | ------------------------ | ---------------------- | | 404 | `departamento-not-found` | Departamento no existe | | 404 | `distrito-not-found` | Distrito no existe | # Cancelar Documento (/docs/referencia/documentos-electronicos/cancelar) ## POST /api/v1/documento-electronico/:cdc/cancelar [#post-apiv1documento-electronicocdccancelar] Envía el evento de cancelación a SIFEN. Solo se pueden cancelar documentos en estado `APROBADO`. ### Autenticación [#autenticación] `Authorization: Bearer {api-key}` — requerido ### Path parameters [#path-parameters] | Parámetro | Tipo | Descripción | | --------- | -------- | ---------------------------- | | `cdc` | `string` | CDC del documento a cancelar | ### Request body [#request-body] ```json { "motivo": "Error en datos del cliente" } ``` | Campo | Tipo | Req. | Descripción | | -------- | -------- | ---- | -------------------------------------- | | `motivo` | `string` | Sí | Motivo de la cancelación (texto libre) | ### Respuesta exitosa [#respuesta-exitosa] **Status:** `200 OK` ### Errores [#errores] | Status | Tipo | Descripción | | ------ | --------------------------------- | ---------------------------------------------------- | | 400 | `evento-cancelacion-error` | Error al procesar el evento en SIFEN | | 404 | `documento-electronico-not-found` | CDC no encontrado | | 409 | `evento-cancelacion-error` | El documento no puede cancelarse en su estado actual | # Consultar Estado (/docs/referencia/documentos-electronicos/consultar-estado) ## GET /api/v1/documento-electronico/status/:cdc [#get-apiv1documento-electronicostatuscdc] Retorna el estado de procesamiento de un documento electrónico en SIFEN. ### Autenticación [#autenticación] `Authorization: Bearer {api-key}` — requerido ### Path parameters [#path-parameters] | Parámetro | Tipo | Descripción | | --------- | -------- | ---------------------------------- | | `cdc` | `string` | CDC de 44 caracteres del documento | ### Respuesta exitosa [#respuesta-exitosa] **Status:** `200 OK` ```json { "cdc": "01800123451001001000000122026042710000000006", "estado": "APROBADO", "fechaEmision": "2026-04-15T10:30:00", "tipoDocumento": "FACTURA_ELECTRONICA", "numeroDocumento": "001-001-0000001", "mensajeRechazo": null } ``` ### Estados posibles [#estados-posibles] | Estado | Descripción | | ----------- | ------------------------------------------------- | | `PENDIENTE` | Recibido, aún no incluido en un lote | | `EN_LOTE` | Incluido en un lote, esperando respuesta de SIFEN | | `APROBADO` | Aprobado por SIFEN — documento válido | | `RECHAZADO` | Rechazado por SIFEN — ver `mensajeRechazo` | | `CANCELADO` | Cancelado tras aprobación | ### Errores [#errores] | Status | Tipo | Descripción | | ------ | --------------------------------- | ----------------- | | 404 | `documento-electronico-not-found` | CDC no encontrado | ### Ejemplo [#ejemplo] ```bash curl https://api.sifende.com.py/api/v1/documento-electronico/status/01800123451001001000000122026042710000000006 \ -H "Authorization: Bearer $SIFENDE_API_KEY" ``` # Descargar KuDE (/docs/referencia/documentos-electronicos/descargar-kude) ## GET /api/v1/documento-electronico/:cdc/kude [#get-apiv1documento-electronicocdckude] Retorna el KuDE (Kuatia Ñe'ẽ Mba'eporu — comprobante electrónico) en formato PDF binario. ### Autenticación [#autenticación] `Authorization: Bearer {api-key}` — requerido ### Path parameters [#path-parameters] | Parámetro | Tipo | Descripción | | --------- | -------- | -------------------------- | | `cdc` | `string` | CDC del documento aprobado | ### Respuesta exitosa [#respuesta-exitosa] **Status:** `200 OK` **Content-Type:** `application/pdf` El body es el PDF binario del KuDE. ### Errores [#errores] | Status | Tipo | Descripción | | ------ | --------------------------------- | --------------------------------- | | 404 | `documento-electronico-not-found` | CDC no encontrado | | 422 | `kude-generation-error` | Error generando el PDF | | 501 | `kude-not-supported` | Tipo de documento no soporta KuDE | ### Ejemplo — guardar PDF a disco [#ejemplo--guardar-pdf-a-disco] ```bash curl -o factura.pdf \ https://api.sifende.com.py/api/v1/documento-electronico/{cdc}/kude \ -H "Authorization: Bearer $SIFENDE_API_KEY" ``` # Emitir Documento Electrónico (/docs/referencia/documentos-electronicos/emitir) ## POST /api/v1/documento-electronico [#post-apiv1documento-electronico] Emite un documento electrónico y lo encola para envío a SIFEN. La respuesta es **asíncrona**: la API retorna inmediatamente con `estado: PENDIENTE` y un CDC ya calculado. El procesamiento real ocurre en segundo plano (lote → firma → envío SIFEN → resultado). Usá [`GET /status/:cdc`](/docs/referencia/documentos-electronicos/consultar-estado) o el `statusUrl` retornado para hacer polling del estado final. ### Autenticación [#autenticación] `Authorization: Bearer {api-key}` — requerido ### Request body [#request-body] El campo `tipoDocumento` determina el schema completo del request. Ver los modelos: * [Factura Electrónica](/docs/referencia/modelos/factura-electronica) * [Nota de Crédito Electrónica](/docs/referencia/modelos/nota-credito) * [Nota de Débito Electrónica](/docs/referencia/modelos/nota-debito) ### Campos comunes a todos los tipos [#campos-comunes-a-todos-los-tipos] | Campo | Tipo | Req. | Descripción | | ----------------------- | ---------- | ---- | ----------------------------------------------------------------------------- | | `tipoDocumento` | `enum` | Sí | Tipo de documento — ver tabla arriba | | `fechaEmision` | `datetime` | Sí | Fecha y hora de emisión (`YYYY-MM-DDTHH:mm:ss`) | | `tipoEmision` | `enum` | Sí | `NORMAL` o `CONTINGENCIA` | | `numeroEstablecimiento` | `integer` | Sí | Número de establecimiento (1-999) | | `puntoExpedicion` | `integer` | Sí | Punto de expedición (1-999) | | `tipoTransaccion` | `enum` | Sí | Naturaleza de la operación | | `monedaOperacion` | `enum` | Sí | Moneda (ej: `PYG`, `USD`) | | `receptor` | `object` | Sí | Datos del receptor — ver [Modelo Receptor](/docs/referencia/modelos/receptor) | | `condicionOperacion` | `enum` | Sí | `CONTADO` o `CREDITO` | | `condicionPago` | `object` | Sí\* | Requerido para `CONTADO` | | `items` | `array` | Sí | Lista de ítems — ver [Modelo Ítem](/docs/referencia/modelos/item) | ### Respuesta exitosa [#respuesta-exitosa] **Status:** `202 Accepted` La respuesta es un objeto JSON con el CDC, el estado inicial (`PENDIENTE`) y URLs auxiliares para seguimiento. ```json { "id": "550e8400-e29b-41d4-a716-446655440000", "cdc": "01800123451001001000000122026042710000000006", "estado": "PENDIENTE", "tipoDocumento": "FACTURA_ELECTRONICA", "iTiDe": 1, "numeroDocumento": 1, "numeroFormateado": "001-001-0000001", "fechaCreacion": "2026-04-27T10:30:00", "qrUrl": "https://ekuatia.set.gov.py/consultas-test/qr?...", "statusUrl": "https://api.sifende.com.py/api/v1/documento-electronico/status/01800123451001001000000122026042710000000006", "kudeUrl": "https://api.sifende.com.py/api/v1/documento-electronico/01800123451001001000000122026042710000000006/kude" } ``` #### Headers de respuesta [#headers-de-respuesta] | Header | Valor | Descripción | | ---------- | ------------- | --------------------------------------------------- | | `Location` | `{statusUrl}` | URL absoluta para consultar el estado del documento | #### Campos del DTO [#campos-del-dto] | Campo | Tipo | Descripción | | ------------------ | ------------ | --------------------------------------------------------------------------------------- | | `id` | `uuid` | ID interno del documento en Sifende | | `cdc` | `string(44)` | Código de Control del Documento Electrónico — identificador único en SIFEN | | `estado` | `enum` | Estado inicial: siempre `PENDIENTE` en la respuesta de emisión | | `tipoDocumento` | `enum` | Tipo del documento creado (eco del request) | | `iTiDe` | `integer` | Código numérico SIFEN del tipo (1=FE, 5=NCE, 6=NDE) | | `numeroDocumento` | `integer` | Número correlativo asignado dentro del punto de expedición | | `numeroFormateado` | `string` | Número en formato `establecimiento-puntoExpedicion-secuencia` (ej: `001-001-0000001`) | | `fechaCreacion` | `datetime` | Timestamp de creación del registro | | `qrUrl` | `string` | URL del QR oficial de SIFEN para el KuDE | | `statusUrl` | `string` | URL absoluta para consultar el estado | | `kudeUrl` | `string` | URL absoluta para descargar el KuDE en PDF (disponible solo cuando `estado = APROBADO`) | El estado inicial es siempre `PENDIENTE`. Hacé polling a `statusUrl` o `GET /status/:cdc` cada 5–10 segundos hasta obtener `APROBADO`, `RECHAZADO` o `APROBADO_OBSERVACION`. Ver [Polling de resultados](/docs/guias/polling-resultados). ### Errores comunes [#errores-comunes] | Status | Tipo | Cuándo ocurre | | ------ | ---------------------------------------- | ------------------------------------------ | | 400 | `validation-error` | Campos inválidos o faltantes | | 400 | `invalid-enum-value` | Valor de enumeración no reconocido | | 404 | `timbrado-not-found` | No hay timbrado configurado | | 404 | `certificate-not-found` | No hay certificado digital subido | | 422 | `documento-electronico-generation-error` | Error de compliance SIFEN en la generación | ### Ejemplos [#ejemplos] ```bash curl -X POST https://api.sifende.com.py/api/v1/documento-electronico \ -H "Authorization: Bearer $SIFENDE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tipoDocumento": "FACTURA_ELECTRONICA", "fechaEmision": "2026-04-15T10:30:00", "tipoEmision": "NORMAL", "numeroEstablecimiento": 1, "puntoExpedicion": 1, "tipoTransaccion": "VENTA_MERCADERIA", "monedaOperacion": "PYG", "receptor": { "tipoContribuyente": "NO_CONTRIBUYENTE", "tipoOperacion": "B2C", "tipoDocumento": "CEDULA_PARAGUAYA", "numeroDocumento": "1234567", "nombreRazonSocial": "Juan Pérez" }, "condicionOperacion": "CONTADO", "condicionPago": { "tipo": "CONTADO", "tipoPago": "EFECTIVO", "monedaPago": "PYG", "montoPago": 110000 }, "items": [ { "codigo": "PROD-001", "descripcion": "Resma de papel A4 75g", "cantidad": 10, "unidadMedida": "UNI", "precioUnitario": 10000, "afectacionTributaria": "GRAVADO", "tasaIVA": 10 } ] }' ``` ```typescript const response = await fetch( 'https://api.sifende.com.py/api/v1/documento-electronico', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.SIFENDE_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ tipoDocumento: 'FACTURA_ELECTRONICA', fechaEmision: '2026-04-15T10:30:00', tipoEmision: 'NORMAL', numeroEstablecimiento: 1, puntoExpedicion: 1, tipoTransaccion: 'VENTA_MERCADERIA', monedaOperacion: 'PYG', receptor: { tipoContribuyente: 'NO_CONTRIBUYENTE', tipoOperacion: 'B2C', tipoDocumento: 'CEDULA_PARAGUAYA', numeroDocumento: '1234567', nombreRazonSocial: 'Juan Pérez', }, condicionOperacion: 'CONTADO', condicionPago: { tipo: 'CONTADO', tipoPago: 'EFECTIVO', monedaPago: 'PYG', montoPago: 110000, }, items: [ { codigo: 'PROD-001', descripcion: 'Resma de papel A4 75g', cantidad: 10, unidadMedida: 'UNI', precioUnitario: 10000, afectacionTributaria: 'GRAVADO', tasaIVA: 10, }, ], }), } ); const data = await response.json(); // data.cdc → "01800123451001001000000122026042710000000006" // data.estado → "PENDIENTE" // data.statusUrl → "https://api.sifende.com.py/api/v1/documento-electronico/status/..." ``` ```python import requests, os response = requests.post( 'https://api.sifende.com.py/api/v1/documento-electronico', headers={ 'Authorization': f'Bearer {os.environ["SIFENDE_API_KEY"]}', 'Content-Type': 'application/json', }, json={ 'tipoDocumento': 'FACTURA_ELECTRONICA', 'fechaEmision': '2026-04-15T10:30:00', 'tipoEmision': 'NORMAL', 'numeroEstablecimiento': 1, 'puntoExpedicion': 1, 'tipoTransaccion': 'VENTA_MERCADERIA', 'monedaOperacion': 'PYG', 'receptor': { 'tipoContribuyente': 'NO_CONTRIBUYENTE', 'tipoOperacion': 'B2C', 'tipoDocumento': 'CEDULA_PARAGUAYA', 'numeroDocumento': '1234567', 'nombreRazonSocial': 'Juan Pérez', }, 'condicionOperacion': 'CONTADO', 'condicionPago': { 'tipo': 'CONTADO', 'tipoPago': 'EFECTIVO', 'monedaPago': 'PYG', 'montoPago': 110000, }, 'items': [{ 'codigo': 'PROD-001', 'descripcion': 'Resma de papel A4 75g', 'cantidad': 10, 'unidadMedida': 'UNI', 'precioUnitario': 10000, 'afectacionTributaria': 'GRAVADO', 'tasaIVA': 10, }], } ) data = response.json() # data['cdc'] → "01800123451001001000000122026042710000000006" # data['estado'] → "PENDIENTE" # data['statusUrl'] → "https://api.sifende.com.py/api/v1/documento-electronico/status/..." ``` # Documentos Electrónicos (/docs/referencia/documentos-electronicos) ## Endpoint único y polimórfico [#endpoint-único-y-polimórfico] **Diseño importante:** Sifende usa **un solo endpoint** para todos los tipos de documento electrónico. El campo `tipoDocumento` en el body determina qué tipo emitís. No hay endpoints separados para facturas, notas de crédito, etc. ``` POST /api/v1/documento-electronico ``` | `tipoDocumento` | Tipo de documento | Estado | | ------------------------------ | ------------------------ | ---------------- | | `FACTURA_ELECTRONICA` | Factura Electrónica (FE) | ✅ Disponible | | `NOTA_DE_CREDITO_ELECTRONICA` | Nota de Crédito (NCE) | ✅ Disponible | | `NOTA_DE_DEBITO_ELECTRONICA` | Nota de Débito (NDE) | ✅ Disponible | | `AUTOFACTURA_ELECTRONICA` | Autofactura (AFE) | 🚧 En desarrollo | | `NOTA_DE_REMISION_ELECTRONICA` | Nota de Remisión (NRE) | 🚧 En desarrollo | Cada tipo tiene su propio schema de request. Ver [Modelos de Datos](/docs/referencia/modelos). ## Autenticación [#autenticación] Todos los endpoints de esta sección requieren API key: ``` Authorization: Bearer {tu-api-key} ``` ## URL base [#url-base] ``` https://api.sifende.com.py/api/v1/documento-electronico ``` ## Endpoints [#endpoints] | Método | Path | Descripción | | ------ | ---------------- | ------------------------------------------------------------------------------------- | | `POST` | `/` | [Emitir un documento electrónico](/docs/referencia/documentos-electronicos/emitir) | | `GET` | `/status/:cdc` | [Consultar estado por CDC](/docs/referencia/documentos-electronicos/consultar-estado) | | `GET` | `/:cdc/kude` | [Descargar KuDE PDF](/docs/referencia/documentos-electronicos/descargar-kude) | | `POST` | `/:cdc/cancelar` | [Cancelar un documento](/docs/referencia/documentos-electronicos/cancelar) | | `POST` | `/inutilizar` | [Inutilizar numeración](/docs/referencia/documentos-electronicos/inutilizar) | # Inutilizar Numeración (/docs/referencia/documentos-electronicos/inutilizar) ## POST /api/v1/documento-electronico/inutilizar [#post-apiv1documento-electronicoinutilizar] Envía el evento de inutilización a SIFEN para un rango de números de documento no emitidos. ### Autenticación [#autenticación] `Authorization: Bearer {api-key}` — requerido ### Request body [#request-body] ```json { "tipoDocumento": "FACTURA_ELECTRONICA", "establecimiento": 1, "puntoExpedicion": 1, "desde": 50, "hasta": 55, "motivo": "Números no utilizados por error de sistema" } ``` | Campo | Tipo | Req. | Descripción | | ----------------- | --------- | ---- | ------------------------------------ | | `tipoDocumento` | `enum` | Sí | Tipo de documento a inutilizar | | `establecimiento` | `integer` | Sí | Número de establecimiento | | `puntoExpedicion` | `integer` | Sí | Punto de expedición | | `desde` | `integer` | Sí | Primer número del rango a inutilizar | | `hasta` | `integer` | Sí | Último número del rango a inutilizar | | `motivo` | `string` | Sí | Motivo de la inutilización | ### Respuesta exitosa [#respuesta-exitosa] **Status:** `200 OK` ### Errores [#errores] | Status | Tipo | Descripción | | ------ | ---------------------------- | -------------------------------- | | 400 | `evento-inutilizacion-error` | Error al procesar en SIFEN | | 409 | `evento-inutilizacion-error` | Rango ya utilizado o inutilizado | # Detalle de Evento (/docs/referencia/eventos/detalle) ## GET /eventos/:eventoId [#get-eventoseventoid] Devuelve el detalle completo de un evento SIFEN, incluyendo el protocolo de autorización si SIFEN lo confirmó. ### Variantes [#variantes] ``` GET /api/v1/documento-electronico/eventos/{eventoId} ``` `Authorization: Bearer {api-key}` — la clave determina el contribuyente. ``` GET /api/v1/contribuyentes/{contribuyenteId}/eventos/{eventoId} ``` `Authorization: Bearer {jwt}` — el usuario debe ser propietario del contribuyente. ### Path parameters [#path-parameters] | Parámetro | Tipo | Descripción | | ---------- | --------- | -------------------------- | | `eventoId` | `integer` | `eventoSifenId` del evento | ### Respuesta exitosa [#respuesta-exitosa] **Status:** `200 OK` Devuelve un `EventoSifenDTO` completo (mismos campos que [Listar Eventos](/docs/referencia/eventos/listar)). #### Ejemplo — evento de CANCELACION confirmado [#ejemplo--evento-de-cancelacion-confirmado] ```json { "status": "success", "payload": { "eventoSifenId": 1024, "documentoElectronicoId": 8821, "contribuyenteId": 42, "tipoEvento": "CANCELACION", "estadoEvento": "PROCESADO", "cdc": "01800123451001001000000122026042710000000006", "motivo": "Error en datos del cliente", "protocoloAutorizacion": "20260427143218000", "codigoRespuesta": "0391", "mensajeRespuesta": "Evento de cancelación registrado exitosamente", "fechaCreacion": "2026-04-27T14:32:18", "fechaProcesamiento": "2026-04-27T14:32:45" } } ``` #### Ejemplo — evento de INUTILIZACION pendiente [#ejemplo--evento-de-inutilizacion-pendiente] ```json { "status": "success", "payload": { "eventoSifenId": 1025, "documentoElectronicoId": null, "contribuyenteId": 42, "tipoEvento": "INUTILIZACION", "estadoEvento": "PENDIENTE", "cdc": null, "motivo": "Numeración no utilizada por error de sistema", "protocoloAutorizacion": null, "codigoRespuesta": null, "mensajeRespuesta": null, "fechaCreacion": "2026-04-27T15:01:09", "fechaProcesamiento": null, "numeroTimbrado": "12557896", "establecimiento": 1, "puntoExpedicion": 1, "numeroInicio": 50, "numeroFin": 55, "tipoDocumento": 1, "serieNumero": null } } ``` ### Cuándo aparece `protocoloAutorizacion` [#cuándo-aparece-protocoloautorizacion] El campo se completa **solo cuando SIFEN confirma el evento** (`estadoEvento = PROCESADO` y `codigoRespuesta` exitoso, ej. `0391` para cancelaciones, `0392` para inutilizaciones). Si el evento todavía está `PENDIENTE`, `protocoloAutorizacion`, `codigoRespuesta` y `mensajeRespuesta` serán `null`. ### Errores [#errores] | Status | Tipo | Descripción | | ------ | ------------------ | ------------------------------------------------------- | | 401 | — | API key o JWT inválido | | 403 | `access-denied` | El usuario no es propietario del contribuyente | | 404 | `evento-not-found` | El `eventoId` no existe o no pertenece al contribuyente | ### Ejemplo [#ejemplo] ```bash curl https://api.sifende.com.py/api/v1/documento-electronico/eventos/1024 \ -H "Authorization: Bearer $API_KEY" ``` # Eventos SIFEN (/docs/referencia/eventos) Los eventos SIFEN son acciones enviadas sobre documentos ya emitidos: **cancelación** e **inutilización** de numeración. ## Endpoints [#endpoints] | Método | Path | Descripción | | ------ | -------------------------------------------- | ------------------------------------------------------------ | | `GET` | `/api/v1/documento-electronico/eventos` | [Listar eventos](/docs/referencia/eventos/listar) | | `GET` | `/api/v1/documento-electronico/eventos/{id}` | [Ver detalle de un evento](/docs/referencia/eventos/detalle) | Para **enviar** eventos, ver [Cancelar Documento](/docs/referencia/documentos-electronicos/cancelar) e [Inutilizar Numeración](/docs/referencia/documentos-electronicos/inutilizar). # Listar Eventos (/docs/referencia/eventos/listar) ## GET /eventos [#get-eventos] Lista los eventos SIFEN —cancelaciones e inutilizaciones— enviados desde la API. Soporta paginación y filtrado por tipo. ### Variantes [#variantes] Hay dos rutas equivalentes según el modo de autenticación: ``` GET /api/v1/documento-electronico/eventos ``` `Authorization: Bearer {api-key}` — la clave determina el contribuyente automáticamente. ``` GET /api/v1/contribuyentes/{contribuyenteId}/eventos ``` `Authorization: Bearer {jwt}` — el usuario debe ser propietario del contribuyente. ### Query parameters [#query-parameters] | Parámetro | Tipo | Default | Descripción | | ------------ | --------- | ------- | ------------------------------------------------ | | `page` | `integer` | `0` | Página solicitada (base 0) | | `size` | `integer` | `10` | Cantidad de elementos por página | | `tipoEvento` | `enum` | — | Filtra por tipo: `CANCELACION` o `INUTILIZACION` | ### Respuesta exitosa [#respuesta-exitosa] **Status:** `200 OK` La versión sesión (JWT) envuelve la respuesta en `{ status, payload }`. La versión API key devuelve directamente el `PageDTO`. ```json { "status": "success", "payload": { "content": [ { "eventoSifenId": 1024, "documentoElectronicoId": 8821, "contribuyenteId": 42, "tipoEvento": "CANCELACION", "estadoEvento": "PROCESADO", "cdc": "01800123451001001000000122026042710000000006", "motivo": "Error en datos del cliente", "protocoloAutorizacion": "20260427143218000", "codigoRespuesta": "0391", "mensajeRespuesta": "Evento de cancelación registrado exitosamente", "fechaCreacion": "2026-04-27T14:32:18", "fechaProcesamiento": "2026-04-27T14:32:45" } ], "totalElements": 87, "totalPages": 9, "size": 10, "number": 0 } } ``` ### Campos del EventoSifenDTO [#campos-del-eventosifendto] | Campo | Tipo | Descripción | | ------------------------ | ---------------- | -------------------------------------------- | | `eventoSifenId` | `integer` | ID interno del evento | | `documentoElectronicoId` | `integer\|null` | DE asociado (null en inutilizaciones) | | `contribuyenteId` | `integer` | Contribuyente emisor del evento | | `tipoEvento` | `enum` | `CANCELACION` o `INUTILIZACION` | | `estadoEvento` | `enum` | `PENDIENTE`, `PROCESADO`, `RECHAZADO` | | `cdc` | `string\|null` | CDC del documento (solo cancelación) | | `motivo` | `string` | Motivo declarado al enviar el evento | | `protocoloAutorizacion` | `string\|null` | Número de protocolo SIFEN cuando se confirma | | `codigoRespuesta` | `string\|null` | Código de respuesta SIFEN | | `mensajeRespuesta` | `string\|null` | Mensaje SIFEN | | `fechaCreacion` | `datetime` | Fecha de envío del evento | | `fechaProcesamiento` | `datetime\|null` | Fecha de respuesta de SIFEN | #### Solo en `tipoEvento = INUTILIZACION` [#solo-en-tipoevento--inutilizacion] | Campo | Tipo | Descripción | | ----------------- | -------------- | ---------------------------------------------------- | | `numeroTimbrado` | `string` | Timbrado del rango inutilizado | | `establecimiento` | `integer` | Establecimiento (3 dígitos) | | `puntoExpedicion` | `integer` | Punto de expedición (3 dígitos) | | `numeroInicio` | `integer` | Primer número del rango | | `numeroFin` | `integer` | Último número del rango | | `tipoDocumento` | `short` | Código numérico del tipo de documento (ej. `1` = FE) | | `serieNumero` | `string\|null` | Serie utilizada (si aplica) | ### Errores [#errores] | Status | Tipo | Descripción | | ------ | -------------------- | ---------------------------------------------- | | 401 | — | API key o JWT inválido | | 403 | `access-denied` | El usuario no es propietario del contribuyente | | 422 | `invalid-enum-value` | `tipoEvento` con valor no válido | ### Ejemplo [#ejemplo] ```bash curl "https://api.sifende.com.py/api/v1/documento-electronico/eventos?page=0&size=20&tipoEvento=CANCELACION" \ -H "Authorization: Bearer $API_KEY" ``` # Autofactura Electrónica (/docs/referencia/modelos/autofactura) **`tipoDocumento`:** `AUTOFACTURA_ELECTRONICA` 🚧 En desarrollo **En desarrollo.** La Autofactura Electrónica (AFE) todavía no está disponible en la API de Sifende. Si intentás emitir una AFE, el endpoint responderá con error. Esta página describe el caso de uso y el schema planificado. ## ¿Qué es una Autofactura? [#qué-es-una-autofactura] La Autofactura Electrónica documenta una compra a un vendedor que no está obligado a emitir factura: personas no inscriptas en el RUC, productores agrícolas o vendedores ocasionales. En este caso, el comprador (contribuyente formal) emite el documento por sí mismo para registrar la operación con respaldo fiscal ante SET. ## Casos de uso típicos [#casos-de-uso-típicos] * Compra de productos agrícolas a productores no inscriptos * Adquisición de bienes usados a particulares * Pago a profesionales independientes sin facturación electrónica habilitada * Compras menores a vendedores ambulantes o informales ## Schema planificado [#schema-planificado] El request seguirá el patrón polimórfico estándar con `tipoDocumento = "AUTOFACTURA_ELECTRONICA"` y los [campos comunes de la base](/docs/referencia/modelos/factura-electronica#campos-comunes-base), más campos específicos que identifican al **vendedor no contribuyente** y la **dirección de la operación**. ## Estado de implementación [#estado-de-implementación] | Componente | Estado | | ----------------- | ----------------------- | | Diseño funcional | ✅ Documentado | | Schema de request | 🚧 Definición pendiente | | Servicio backend | ❌ No implementado | | Validación SIFEN | ❌ Pendiente | Mientras tanto, las operaciones que correspondan a Autofactura deben gestionarse fuera de Sifende. Suscribite al [changelog](/docs/referencia/changelog) para enterarte cuando esté disponible. ## Próximos pasos [#próximos-pasos] * [Tipos de documentos electrónicos](/docs/conceptos/documentos-electronicos) * [Changelog de la API](/docs/referencia/changelog) # Condición de Pago (/docs/referencia/modelos/condicion-pago) El objeto `condicionPago` describe cómo paga el cliente un documento electrónico. Es obligatorio cuando `condicionOperacion = CONTADO` en una Factura Electrónica. La condición **CREDITO** (con cuotas, plazos, entrega inicial) está parcialmente soportada. Para producción usá `CONTADO` mientras tanto. ## Schema [#schema] | Campo | Tipo | Requerido | Descripción | | --------------------- | --------- | ------------------- | ------------------------------------ | | `tipo` | `enum` | Sí | `CONTADO` o `CREDITO` | | `tipoPago` | `enum` | Sí | Medio de pago — ver tabla abajo | | `monedaPago` | `enum` | Sí | `PYG`, `USD`, `EUR`, etc. | | `montoPago` | `number` | Sí para `CONTADO` | Monto total pagado (entero para PYG) | | `plazoCredito` | `string` | Sí para `CREDITO` | Plazo de crédito, ej. `"30 días"` | | `cuotas` | `integer` | Sí para `CREDITO` | Cantidad de cuotas | | `montoEntregaInicial` | `number` | No (sólo `CREDITO`) | Anticipo o entrega inicial | ## Valores de `tipoPago` [#valores-de-tipopago] | Valor | Descripción | | ----------------------- | ------------------------------------------------------ | | `EFECTIVO` | Pago en efectivo | | `CHEQUE` | Cheque | | `TARJETA_DE_CREDITO` | Tarjeta de crédito | | `TARJETA_DE_DEBITO` | Tarjeta de débito | | `TRANSFERENCIA` | Transferencia bancaria | | `GIRO` | Giro o orden de pago | | `BILLETERA_ELECTRONICA` | Billetera electrónica (Tigo Money, Personal Pay, etc.) | | `TARJETA_EMPRESARIAL` | Tarjeta empresarial | | `PAGO_MOVIL` | Pago móvil | | `OTRO` | Otros medios no contemplados | Consultá [enumeraciones](/docs/referencia/enumeraciones#tipopago) para el listado completo y actualizado. ## Ejemplo — Pago contado en efectivo [#ejemplo--pago-contado-en-efectivo] ```json { "condicionPago": { "tipo": "CONTADO", "tipoPago": "EFECTIVO", "monedaPago": "PYG", "montoPago": 110000 } } ``` ## Ejemplo — Pago contado con tarjeta en USD [#ejemplo--pago-contado-con-tarjeta-en-usd] ```json { "condicionPago": { "tipo": "CONTADO", "tipoPago": "TARJETA_DE_CREDITO", "monedaPago": "USD", "montoPago": 25.50 } } ``` ## Ejemplo — Crédito (planificado) [#ejemplo--crédito-planificado] ```json { "condicionPago": { "tipo": "CREDITO", "tipoPago": "TRANSFERENCIA", "monedaPago": "PYG", "plazoCredito": "30 días", "cuotas": 3, "montoEntregaInicial": 50000 } } ``` ## Próximos pasos [#próximos-pasos] * [Modelo Factura Electrónica](/docs/referencia/modelos/factura-electronica) * [Enumeraciones SIFEN](/docs/referencia/enumeraciones) # Documento Asociado (/docs/referencia/modelos/documento-asociado) El objeto `documentoAsociado` referencia un documento previo desde otro documento electrónico. Es obligatorio en Notas de Crédito y Notas de Débito (deben apuntar a la FE original que están modificando), y opcional en Facturas Electrónicas (típicamente para anticipos). ## Schema [#schema] | Campo | Tipo | Requerido | Descripción | | --------------- | -------- | --------------------- | --------------------------------------------------- | | `tipoDocumento` | `enum` | Sí | `ELECTRONICO`, `IMPRESO` o `CONSTANCIA_ELECTRONICA` | | `cdc` | `string` | Sí para `ELECTRONICO` | CDC de 44 caracteres del documento referenciado | **Sólo `ELECTRONICO` está soportado actualmente.** Los valores `IMPRESO` y `CONSTANCIA_ELECTRONICA` están definidos en el enum pero el backend lanza `UnsupportedOperationException` si se usan. Si necesitás referenciar un documento pre-electrónico (papel timbrado), contactá a soporte. ## Reglas [#reglas] * El CDC debe pertenecer a un documento aprobado por SIFEN (estado `APROBADO`). No podés referenciar un documento pendiente o rechazado. * El receptor del documento original y el de la NCE/NDE deben coincidir (mismo RUC, CI, etc.). * Para NCE: el documento original debe ser una FE (no se permite NCE sobre NCE). ## Ejemplo — Referencia electrónica [#ejemplo--referencia-electrónica] ```json { "documentoAsociado": { "tipoDocumento": "ELECTRONICO", "cdc": "01800123451001001000000122026042710000000006" } } ``` El CDC tiene exactamente **44 caracteres** y codifica RUC del emisor, tipo de documento, establecimiento, punto de expedición, número, fecha de emisión y un código de seguridad. Ver el [concepto CDC](/docs/conceptos/cdc) para el desglose completo. ## Uso típico — Nota de Crédito [#uso-típico--nota-de-crédito] ```json { "tipoDocumento": "NOTA_DE_CREDITO_ELECTRONICA", "motivoEmision": "DEVOLUCION", "documentoAsociado": { "tipoDocumento": "ELECTRONICO", "cdc": "01800123451001001000000122026042710000000006" } } ``` ## Próximos pasos [#próximos-pasos] * [Modelo Nota de Crédito](/docs/referencia/modelos/nota-credito) * [Modelo Nota de Débito](/docs/referencia/modelos/nota-debito) * [Concepto: CDC](/docs/conceptos/cdc) # Factura Electrónica (/docs/referencia/modelos/factura-electronica) **`tipoDocumento`:** `FACTURA_ELECTRONICA` ✅ Disponible El request de FE contiene todos los campos obligatorios para emitir una Factura Electrónica conforme a SIFEN. Se envía al endpoint polimórfico [`POST /api/v1/documento-electronico`](/docs/referencia/documentos-electronicos/emitir). ## Campos comunes (base) [#campos-comunes-base] | Campo | Tipo | Requerido | Descripción | | ----------------------- | ---------- | --------- | ----------------------------------------------------------- | | `tipoDocumento` | `enum` | Sí | Debe ser `FACTURA_ELECTRONICA` | | `fechaEmision` | `datetime` | Sí | ISO 8601 sin zona — `YYYY-MM-DDTHH:mm:ss` | | `tipoEmision` | `enum` | Sí | `NORMAL` (por defecto) o `CONTINGENCIA` | | `numeroEstablecimiento` | `integer` | Sí | 1–999, por defecto `1` | | `puntoExpedicion` | `integer` | Sí | 1–999, por defecto `1` | | `monedaOperacion` | `enum` | No | Por defecto `PYG`. Acepta `USD`, `EUR`, etc. | | `infoEmisor` | `string` | No | Texto libre adicional del emisor (campo SIFEN B005) | | `receptor` | `object` | Sí | Ver [Receptor](/docs/referencia/modelos/receptor) | | `items` | `array` | Sí | Al menos 1 ítem — ver [Ítem](/docs/referencia/modelos/item) | ## Campos específicos de FE [#campos-específicos-de-fe] | Campo | Tipo | Requerido | Descripción | | -------------------- | -------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `tipoTransaccion` | `enum` | Sí | `VENTA_MERCADERIA`, `PRESTACION_SERVICIOS`, `MIXTO`, `VENTA_ACTIVO_FIJO`, `VENTA_DIVISAS`, `ANTICIPO`, `VENTA_CREDITO_FISCAL` — ver [tipoTransaccion](/docs/referencia/enumeraciones#tipotransaccion) para la lista completa | | `condicionOperacion` | `enum` | Sí | `CONTADO` o `CREDITO` | | `condicionPago` | `object` | Sí\* | Obligatorio cuando `condicionOperacion = CONTADO`. Ver [Condición de Pago](/docs/referencia/modelos/condicion-pago) | | `documentoAsociado` | `object` | No | Para anticipos o documentos de referencia. Ver [Documento Asociado](/docs/referencia/modelos/documento-asociado) | La condición `CREDITO` (cuotas, plazos) está parcialmente soportada. Para producción usá `CONTADO` mientras tanto. ## Ejemplo completo — FE B2C contado [#ejemplo-completo--fe-b2c-contado] ```json { "tipoDocumento": "FACTURA_ELECTRONICA", "fechaEmision": "2026-04-15T10:30:00", "tipoEmision": "NORMAL", "numeroEstablecimiento": 1, "puntoExpedicion": 1, "monedaOperacion": "PYG", "tipoTransaccion": "VENTA_MERCADERIA", "condicionOperacion": "CONTADO", "receptor": { "tipoContribuyente": "NO_CONTRIBUYENTE", "tipoOperacion": "B2C", "tipoDocumento": "CEDULA_PARAGUAYA", "numeroDocumento": "1234567", "nombreRazonSocial": "Juan Pérez" }, "condicionPago": { "tipo": "CONTADO", "tipoPago": "EFECTIVO", "monedaPago": "PYG", "montoPago": 110000 }, "items": [ { "codigo": "PROD-001", "descripcion": "Resma de papel A4 75g", "cantidad": 10, "unidadMedida": "UNI", "precioUnitario": 10000, "afectacionTributaria": "GRAVADO", "tasaIVA": 10 } ] } ``` ## Respuesta [#respuesta] `202 Accepted` con el DTO del documento creado en estado `PENDIENTE`: ```json { "id": "550e8400-e29b-41d4-a716-446655440000", "cdc": "01800123451001001000000122026042710000000006", "estado": "PENDIENTE", "tipoDocumento": "FACTURA_ELECTRONICA", "iTiDe": 1, "numeroDocumento": 1, "numeroFormateado": "001-001-0000001", "fechaCreacion": "2026-04-27T10:30:00", "qrUrl": "https://ekuatia.set.gov.py/consultas-test/qr?...", "statusUrl": "https://api.sifende.com.py/api/v1/documento-electronico/status/01800123451001001000000122026042710000000006", "kudeUrl": "https://api.sifende.com.py/api/v1/documento-electronico/01800123451001001000000122026042710000000006/kude" } ``` Ver el detalle completo de la respuesta en [Emitir Documento Electrónico](/docs/referencia/documentos-electronicos/emitir#respuesta-exitosa). ## Próximos pasos [#próximos-pasos] * [Consultar el estado del documento](/docs/referencia/documentos-electronicos/consultar-estado) * [Descargar el KuDE en PDF](/docs/referencia/documentos-electronicos/descargar-kude) * [Receptor B2B vs B2C](/docs/guias/receptor-b2b-b2c) # Modelos de Datos (/docs/referencia/modelos) Cada tipo de documento electrónico tiene su propio schema de request. Todos se envían al mismo endpoint [`POST /api/v1/documento-electronico`](/docs/referencia/documentos-electronicos/emitir) con el campo `tipoDocumento` correspondiente. ## Documentos [#documentos] | Modelo | tipoDocumento | Estado | | ------------------------------------------------------------------- | ------------------------------ | ---------------- | | [Factura Electrónica](/docs/referencia/modelos/factura-electronica) | `FACTURA_ELECTRONICA` | ✅ Disponible | | [Nota de Crédito](/docs/referencia/modelos/nota-credito) | `NOTA_DE_CREDITO_ELECTRONICA` | ✅ Disponible | | [Nota de Débito](/docs/referencia/modelos/nota-debito) | `NOTA_DE_DEBITO_ELECTRONICA` | ✅ Disponible | | [Autofactura](/docs/referencia/modelos/autofactura) | `AUTOFACTURA_ELECTRONICA` | 🚧 En desarrollo | | [Nota de Remisión](/docs/referencia/modelos/nota-remision) | `NOTA_DE_REMISION_ELECTRONICA` | 🚧 En desarrollo | ## Entidades compartidas [#entidades-compartidas] | Modelo | Descripción | | ----------------------------------------------------------------- | -------------------------------------------- | | [Receptor](/docs/referencia/modelos/receptor) | Datos del receptor (B2B y B2C) | | [Ítem](/docs/referencia/modelos/item) | Línea de producto o servicio | | [Condición de Pago](/docs/referencia/modelos/condicion-pago) | Contado y crédito | | [Documento Asociado](/docs/referencia/modelos/documento-asociado) | Referencia a documento previo (para NCE/NDE) | # Ítem (/docs/referencia/modelos/item) Cada documento electrónico contiene un arreglo `items` con al menos un elemento. Cada ítem representa un producto o servicio facturado, con su precio, cantidad e información tributaria. Sifende calcula automáticamente todos los totales (subtotales, IVA discriminado, total general). No los mandes en el request: se computan a partir de `cantidad`, `precioUnitario`, `afectacionTributaria` y `tasaIVA`. ## Schema [#schema] | Campo | Tipo | Requerido | Descripción | | ---------------------- | --------- | ----------- | ----------------------------------------------------------------------------------- | | `codigo` | `string` | Sí | Código interno del producto en tu sistema | | `descripcion` | `string` | Sí | Descripción del producto o servicio | | `cantidad` | `number` | Sí | Cantidad — mínimo `0.000001` | | `unidadMedida` | `enum` | Sí | `UNI`, `KG`, `LT`, `MT`, etc. — ver [enumeraciones](/docs/referencia/enumeraciones) | | `precioUnitario` | `number` | Sí | Precio unitario **con IVA incluido**, mayor o igual a `0` | | `afectacionTributaria` | `enum` | Sí | `GRAVADO`, `EXENTO`, `EXONERADO`, `GRAVADO_PARCIAL` | | `tasaIVA` | `integer` | Condicional | `5` o `10` — obligatorio cuando `afectacionTributaria = GRAVADO` | | `descuentoParticular` | `number` | No | Descuento absoluto aplicado a este ítem | | `descuentoPorcentaje` | `number` | No | Porcentaje de descuento global aplicado por ítem | | `paisOrigen` | `string` | No | Código ISO del país de origen (para mercaderías importadas) | ## Cálculo de IVA [#cálculo-de-iva] Para ítems `GRAVADO`, Sifende calcula el IVA a partir del precio total: * **IVA 10%:** `iva = total / 11` * **IVA 5%:** `iva = total / 21` * **IVA 0% / EXENTO / EXONERADO:** sin IVA discriminado El precio unitario que enviás es IVA incluido. Esa es la convención SIFEN estándar. ## Valores comunes de `unidadMedida` [#valores-comunes-de-unidadmedida] | Valor | Significado | | ------ | ---------------- | | `UNI` | Unidad | | `KG` | Kilogramo | | `LT` | Litro | | `MT` | Metro | | `M2` | Metro cuadrado | | `M3` | Metro cúbico | | `HORA` | Hora (servicios) | | `DIA` | Día | Ver el listado completo en [enumeraciones](/docs/referencia/enumeraciones#unidadmedida). ## Ejemplo — Producto gravado IVA 10% [#ejemplo--producto-gravado-iva-10] ```json { "codigo": "PROD-001", "descripcion": "Resma de papel A4 75g", "cantidad": 10, "unidadMedida": "UNI", "precioUnitario": 11000, "afectacionTributaria": "GRAVADO", "tasaIVA": 10 } ``` Total del ítem: `10 × 11.000 = 110.000 PYG` (incluye IVA 10% = `10.000 PYG`). ## Ejemplo — Servicio exento [#ejemplo--servicio-exento] ```json { "codigo": "SRV-EDU-01", "descripcion": "Curso de capacitación — 8 horas", "cantidad": 8, "unidadMedida": "HORA", "precioUnitario": 50000, "afectacionTributaria": "EXENTO" } ``` ## Próximos pasos [#próximos-pasos] * [Modelo Factura Electrónica](/docs/referencia/modelos/factura-electronica) * [Concepto: Ítems e IVA](/docs/conceptos/items-iva) * [Enumeraciones SIFEN](/docs/referencia/enumeraciones) # Nota de Crédito Electrónica (/docs/referencia/modelos/nota-credito) **`tipoDocumento`:** `NOTA_DE_CREDITO_ELECTRONICA` ✅ Disponible La Nota de Crédito Electrónica (NCE) reduce o anula el monto de una Factura Electrónica previamente aprobada por SIFEN. Siempre debe referenciar al documento original mediante `documentoAsociado`. ## Restricciones [#restricciones] La NCE no admite receptor `INNOMINADO` (NT-010 / NT-023 de SIFEN). El receptor debe estar identificado con CI, RUC u otro documento válido. El campo `tipoTransaccion` no se envía: se hereda de la Factura Electrónica original referenciada. ## Campos específicos de NCE [#campos-específicos-de-nce] | Campo | Tipo | Requerido | Descripción | | ------------------- | -------- | --------- | ------------------------------------------------------------------------------------------------------------------- | | `motivoEmision` | `enum` | Sí | Motivo de emisión: `DEVOLUCION`, `DESCUENTO`, `BONIFICACION`, `PRECIO`, `otros` | | `documentoAsociado` | `object` | Sí | Referencia obligatoria al documento original. Ver [Documento Asociado](/docs/referencia/modelos/documento-asociado) | Incluye además todos los [campos comunes de la base](/docs/referencia/modelos/factura-electronica#campos-comunes-base): `fechaEmision`, `tipoEmision`, `numeroEstablecimiento`, `puntoExpedicion`, `monedaOperacion`, `receptor`, `items`. ## Valores de `motivoEmision` [#valores-de-motivoemision] | Valor | Cuándo usarlo | | -------------- | ------------------------------------------------- | | `DEVOLUCION` | Devolución total o parcial de mercadería | | `DESCUENTO` | Descuento aplicado posteriormente a la emisión | | `BONIFICACION` | Bonificación comercial otorgada al cliente | | `PRECIO` | Ajuste por error de precio en la factura original | | `otros` | Cualquier otro motivo no contemplado arriba | ## Ejemplo [#ejemplo] ```json { "tipoDocumento": "NOTA_DE_CREDITO_ELECTRONICA", "fechaEmision": "2026-04-20T14:00:00", "tipoEmision": "NORMAL", "numeroEstablecimiento": 1, "puntoExpedicion": 1, "monedaOperacion": "PYG", "motivoEmision": "DEVOLUCION", "documentoAsociado": { "tipoDocumento": "ELECTRONICO", "cdc": "01800123451001001000000122026042710000000006" }, "receptor": { "tipoContribuyente": "CONTRIBUYENTE", "tipoOperacion": "B2B", "numeroDocumento": "80054321", "digitoVerificador": "7", "nombreRazonSocial": "Distribuidora Asunción S.R.L." }, "items": [ { "codigo": "PROD-001", "descripcion": "Resma de papel A4 75g (devolución)", "cantidad": 2, "unidadMedida": "UNI", "precioUnitario": 10000, "afectacionTributaria": "GRAVADO", "tasaIVA": 10 } ] } ``` ## Próximos pasos [#próximos-pasos] * [Cómo emitir una NCE — guía paso a paso](/docs/guias/nota-credito) * [Modelo Documento Asociado](/docs/referencia/modelos/documento-asociado) # Nota de Débito Electrónica (/docs/referencia/modelos/nota-debito) **`tipoDocumento`:** `NOTA_DE_DEBITO_ELECTRONICA` ✅ Disponible La Nota de Débito Electrónica (NDE) aumenta el monto facturado de una Factura Electrónica previamente aprobada por SIFEN. Se usa para cargar intereses, gastos adicionales o ajustes al alza. Siempre referencia al documento original mediante `documentoAsociado`. ## Restricciones [#restricciones] El campo `tipoTransaccion` no se envía: se hereda de la Factura Electrónica original referenciada. ## Campos específicos de NDE [#campos-específicos-de-nde] | Campo | Tipo | Requerido | Descripción | | ------------------- | -------- | --------- | ------------------------------------------------------------------------------------------------------------------- | | `motivoEmision` | `enum` | Sí | Motivo: `INTERES`, `GASTO_TRANSPORTE`, `AUMENTO_PRECIO`, `otros` | | `documentoAsociado` | `object` | Sí | Referencia obligatoria al documento original. Ver [Documento Asociado](/docs/referencia/modelos/documento-asociado) | Incluye además todos los [campos comunes de la base](/docs/referencia/modelos/factura-electronica#campos-comunes-base): `fechaEmision`, `tipoEmision`, `numeroEstablecimiento`, `puntoExpedicion`, `monedaOperacion`, `receptor`, `items`. ## Valores de `motivoEmision` [#valores-de-motivoemision] | Valor | Cuándo usarlo | | ------------------ | ------------------------------------------- | | `INTERES` | Intereses por mora o financiación | | `GASTO_TRANSPORTE` | Gastos de flete o envío adicionales | | `AUMENTO_PRECIO` | Ajuste al alza del precio facturado | | `otros` | Cualquier otro motivo no contemplado arriba | ## Ejemplo [#ejemplo] ```json { "tipoDocumento": "NOTA_DE_DEBITO_ELECTRONICA", "fechaEmision": "2026-04-22T09:15:00", "tipoEmision": "NORMAL", "numeroEstablecimiento": 1, "puntoExpedicion": 1, "monedaOperacion": "PYG", "motivoEmision": "INTERES", "documentoAsociado": { "tipoDocumento": "ELECTRONICO", "cdc": "01800123451001001000000122026042710000000006" }, "receptor": { "tipoContribuyente": "CONTRIBUYENTE", "tipoOperacion": "B2B", "numeroDocumento": "80054321", "digitoVerificador": "7", "nombreRazonSocial": "Distribuidora Asunción S.R.L." }, "items": [ { "codigo": "INT-MORA", "descripcion": "Interés por mora — 30 días", "cantidad": 1, "unidadMedida": "UNI", "precioUnitario": 25000, "afectacionTributaria": "EXENTO" } ] } ``` ## Próximos pasos [#próximos-pasos] * [Cómo emitir una NDE — guía paso a paso](/docs/guias/nota-debito) * [Modelo Documento Asociado](/docs/referencia/modelos/documento-asociado) # Nota de Remisión Electrónica (/docs/referencia/modelos/nota-remision) **`tipoDocumento`:** `NOTA_DE_REMISION_ELECTRONICA` 🚧 En desarrollo **En desarrollo.** La Nota de Remisión Electrónica (NRE) todavía no está disponible en la API de Sifende. Si intentás emitir una NRE, el endpoint responderá con error. Esta página describe el caso de uso y el schema planificado. ## ¿Qué es una Nota de Remisión? [#qué-es-una-nota-de-remisión] La Nota de Remisión Electrónica documenta el traslado físico de mercaderías entre dos puntos (origen y destino), sin que necesariamente exista una venta asociada en ese momento. Es el documento que acompaña a la mercadería durante el transporte y permite a las autoridades fiscalizadoras verificar el origen y destino legítimo de los bienes en tránsito. ## Casos de uso típicos [#casos-de-uso-típicos] * Traslado de mercadería entre sucursales del mismo contribuyente * Envío a depósitos o centros de distribución propios * Entrega a clientes cuando la facturación se realiza con posterioridad * Devolución de productos al proveedor * Traslado para procesamiento, reparación o exhibición ## Schema planificado [#schema-planificado] El request seguirá el patrón polimórfico estándar con `tipoDocumento = "NOTA_DE_REMISION_ELECTRONICA"` y los [campos comunes de la base](/docs/referencia/modelos/factura-electronica#campos-comunes-base), más campos específicos para: * Motivo del traslado (venta, traslado interno, devolución, exhibición, etc.) * Datos del transportista (RUC, vehículo, conductor) * Direcciones de origen y destino detalladas * Fechas de inicio y fin del traslado ## Estado de implementación [#estado-de-implementación] | Componente | Estado | | ----------------- | ----------------------- | | Diseño funcional | ✅ Documentado | | Schema de request | 🚧 Definición pendiente | | Servicio backend | ❌ No implementado | | Validación SIFEN | ❌ Pendiente | Mientras tanto, los traslados deben documentarse mediante el sistema legacy o la NRE manual. Suscribite al [changelog](/docs/referencia/changelog) para enterarte cuando esté disponible. ## Próximos pasos [#próximos-pasos] * [Tipos de documentos electrónicos](/docs/conceptos/documentos-electronicos) * [Changelog de la API](/docs/referencia/changelog) # Receptor (/docs/referencia/modelos/receptor) El objeto `receptor` identifica al destinatario del documento electrónico. Su estructura cambia según el tipo de operación: B2C (consumidor final), B2B (otro contribuyente con RUC), B2G (organismo público) o B2F (cliente del exterior). ## Schema [#schema] | Campo | Tipo | Requerido | Descripción | | ------------------- | --------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `tipoContribuyente` | `enum` | Sí | `CONTRIBUYENTE`, `NO_CONTRIBUYENTE`, `EXTRANJERO`, `NOMINADO`, `INNOMINADO` | | `tipoOperacion` | `enum` | Sí | `B2B`, `B2C`, `B2G`, `B2F` | | `tipoDocumento` | `enum` | Condicional | `CEDULA_PARAGUAYA`, `PASAPORTE`, `CEDULA_EXTRANJERA`, `CARNET_DE_RESIDENCIA`, `INNOMINADO`, `TARJETA_DIPLOMATICA`, `OTRO` — no aplica para B2B | | `numeroDocumento` | `string` | Sí | RUC base (sin DV), número de CI, pasaporte, etc. | | `nombreRazonSocial` | `string` | Sí | Nombre completo o razón social | | `digitoVerificador` | `string` | Condicional | Solo cuando `tipoOperacion = B2B` o `B2G` (es el DV del RUC) | | `direccion` | `string` | Condicional | Opcional para B2B / B2C / B2G. Obligatoria solo para Nota de Remisión Electrónica (`tipoDocumento = NOTA_REMISION_ELECTRONICA`) y operaciones B2F (cliente del exterior con `tipoOperacion = B2F`). | | `numeroCasa` | `integer` | No | Número de casa | | `departamento` | `string` | No | Departamento (Central, Alto Paraná, etc.) | | `codigoDistrito` | `integer` | No | Código del distrito (catálogo geográfico SIFEN) | | `ciudad` | `string` | No | Ciudad | | `telefono` | `string` | No | Teléfono fijo | | `celular` | `string` | No | Teléfono móvil | | `email` | `string` | No | Email — Sifende lo usa para enviar el KuDE automáticamente si está configurado | | `pais` | `enum` | No | Por defecto `PRY`. Usar el código ISO para extranjeros | ## Reglas clave [#reglas-clave] * **B2B:** No envíes `tipoDocumento`. El receptor se identifica por `tipoOperacion: "B2B"` + `tipoContribuyente: "CONTRIBUYENTE"` + `numeroDocumento` (RUC) + `digitoVerificador`. La `direccion` es opcional. * **B2C:** Generalmente `tipoDocumento = CEDULA_PARAGUAYA`. El receptor `INNOMINADO` (sin identificación) sólo se permite en Factura Electrónica, no en Nota de Crédito. La `direccion` es opcional. * **B2G (gobierno):** Igual que B2B con `tipoOperacion = B2G`. La `direccion` es opcional. * **B2F (exterior):** Usar `tipoContribuyente = EXTRANJERO`, `tipoDocumento = PASAPORTE` (u otro) y `pais` con el código ISO correspondiente. La `direccion` es obligatoria porque SIFEN exige el domicilio del cliente extranjero (D210). * **Nota de Remisión Electrónica (NRE):** la `direccion` del receptor es obligatoria porque la NRE documenta un traslado físico de mercadería. ## Ejemplos [#ejemplos] ```json { "receptor": { "tipoContribuyente": "NO_CONTRIBUYENTE", "tipoOperacion": "B2C", "tipoDocumento": "CEDULA_PARAGUAYA", "numeroDocumento": "1234567", "nombreRazonSocial": "Juan Pérez", "email": "juan.perez@example.com.py" } } ``` ```json { "receptor": { "tipoContribuyente": "CONTRIBUYENTE", "tipoOperacion": "B2B", "numeroDocumento": "80054321", "digitoVerificador": "7", "nombreRazonSocial": "Distribuidora Asunción S.R.L.", "direccion": "Av. Mariscal López 1234", "ciudad": "Asunción", "departamento": "Capital", "email": "facturacion@distribuidora-asuncion.com.py" } } ``` ```json { "receptor": { "tipoContribuyente": "EXTRANJERO", "tipoOperacion": "B2F", "tipoDocumento": "PASAPORTE", "numeroDocumento": "AB1234567", "nombreRazonSocial": "John Smith", "direccion": "1234 Main Street, Miami, FL 33101", "pais": "USA" } } ``` ## Próximos pasos [#próximos-pasos] * [Guía: Receptor B2B vs B2C](/docs/guias/receptor-b2b-b2c) * [Concepto: Receptor](/docs/conceptos/receptor) # Certificado Digital (/docs/referencia/sesion/certificado) Esta API utiliza autenticación Keycloak. Ver [Autenticación](/docs/referencia/autenticacion) para obtener un token JWT. Cada contribuyente debe tener cargado un certificado digital válido y su Código de Seguridad del Contribuyente (CSC) emitido por SET para poder firmar y transmitir documentos electrónicos. ## POST /api/v1/contribuyentes/:id/certificado [#post-apiv1contribuyentesidcertificado] Sube el certificado PKCS12 y el CSC asociado al contribuyente. ### Autenticación [#autenticación] `Authorization: Bearer {keycloak-jwt}` (requerido). El usuario debe ser propietario del contribuyente. ### Content-Type [#content-type] `multipart/form-data` ### Form fields [#form-fields] | Campo | Tipo | Requerido | Descripción | | ---------- | -------- | --------- | ---------------------------------------------------------- | | `file` | `file` | ✅ | Archivo PKCS12 (`.p12` o `.pfx`) | | `password` | `string` | ✅ | Contraseña del certificado | | `idCsc` | `string` | ✅ | ID del CSC (1, 2, etc. — el que tengas activo en e-Kuatia) | | `csc` | `string` | ✅ | Valor del CSC asignado por SET | | `ambiente` | `string` | ✅ | `TEST` o `PROD` | El archivo del certificado y la contraseña se almacenan encriptados en reposo. Nunca compartas la contraseña por canales inseguros (chat, email no cifrado, repos públicos). Si sospechás que se filtró, revocá y reemplazá el certificado en SET y volvé a subirlo. ### Respuesta exitosa [#respuesta-exitosa] **Status:** `200 OK` ```json { "status": "success", "payload": { "certificadoId": 87 } } ``` ### Errores [#errores] | Status | Tipo | Causa | | ------ | ------------------------- | --------------------------------------------------- | | `400` | `validation-error` | Faltan campos o el archivo no es PKCS12 válido | | `400` | `validation-error` | Contraseña incorrecta — no se puede abrir el `.p12` | | `403` | `access-denied` | El usuario no es propietario del contribuyente | | `404` | `contribuyente-not-found` | El `:id` no existe | ## Ejemplo [#ejemplo] ```bash curl -X POST https://api.sifende.com.py/api/v1/contribuyentes/12/certificado \ -H "Authorization: Bearer $KEYCLOAK_JWT" \ -F "file=@./certificado-80012345.p12" \ -F "password=miClaveSecreta" \ -F "idCsc=1" \ -F "csc=ABCD1234EFGH5678IJKL9012MNOP3456" \ -F "ambiente=TEST" ``` Solo hay un certificado activo a la vez. Subir uno nuevo reemplaza al anterior; si todavía era válido, se desactiva. Esta operación no es reversible: guardá una copia segura del archivo original. ## Verificar el estado del certificado [#verificar-el-estado-del-certificado] No existe un endpoint `GET /certificado`. El estado del certificado se expone como el campo `tieneCertificado` (boolean) dentro del [`ContribuyenteDTO`](/docs/referencia/sesion/contribuyentes). ```bash curl https://api.sifende.com.py/api/v1/contribuyentes/12/perfil \ -H "Authorization: Bearer $KEYCLOAK_JWT" \ | jq '.payload.tieneCertificado' ``` ## Cambiar de ambiente (TEST → PROD) [#cambiar-de-ambiente-test--prod] Para pasar de pruebas a producción debés: 1. Generar un certificado de producción en SET (si tenés uno separado) 2. Solicitar el CSC de producción en e-Kuatia 3. Volver a llamar este endpoint con `ambiente=PROD` y los datos productivos Ver la [guía de producción](/docs/guias/ir-a-produccion) para el checklist completo. # Contribuyentes (/docs/referencia/sesion/contribuyentes) Esta API utiliza autenticación Keycloak. Ver [Autenticación](/docs/referencia/autenticacion) para obtener un token JWT. Estos endpoints permiten administrar los contribuyentes asociados al usuario autenticado: listar, consultar perfil, crear, actualizar datos básicos y consultar el padrón SIFEN por RUC. ## Endpoints [#endpoints] | Método | Path | Descripción | | ------- | -------------------------------------- | ------------------------------------------------- | | `GET` | `/api/v1/contribuyentes` | Lista todos los contribuyentes del usuario | | `GET` | `/api/v1/contribuyentes/:id` | Detalle básico del contribuyente | | `GET` | `/api/v1/contribuyentes/:id/perfil` | Perfil completo (incluye dirección y actividades) | | `POST` | `/api/v1/contribuyentes/crear` | Crea un nuevo contribuyente | | `PATCH` | `/api/v1/contribuyentes/:id/perfil` | Actualiza email/teléfono | | `GET` | `/api/v1/contribuyentes/consulta/:ruc` | Consulta el padrón SIFEN por RUC | Todas las respuestas usan el envelope estándar: ```json { "status": "success", "payload": { /* ... */ } } ``` ## GET /api/v1/contribuyentes [#get-apiv1contribuyentes] Lista los contribuyentes del usuario autenticado. ```bash curl https://api.sifende.com.py/api/v1/contribuyentes \ -H "Authorization: Bearer $KEYCLOAK_JWT" ``` Respuesta `200 OK`: ```json { "status": "success", "payload": [ { "contribuyenteId": 12, "ruc": "80012345", "dv": "1", "razonSocial": "Comercial Guaraní S.A.", "tipoContribuyente": "JURIDICA", "estadoRuc": "ACTIVO", "esFacturadorElectronico": true, "tieneCertificado": true, "fechaCreacion": "2026-01-15T09:12:30", "fechaEdicion": "2026-04-02T11:45:00" } ] } ``` ## GET /api/v1/contribuyentes/:id/perfil [#get-apiv1contribuyentesidperfil] Devuelve el perfil completo, incluyendo dirección y actividades económicas. ### ContribuyenteDTO [#contribuyentedto] | Campo | Tipo | Descripción | | ------------------------- | ---------- | ------------------------------------------------------- | | `contribuyenteId` | `long` | ID interno | | `ruc` | `string` | RUC sin DV | | `dv` | `string` | Dígito verificador | | `razonSocial` | `string` | Razón social registrada | | `tipoContribuyente` | `string` | `JURIDICA` o `FISICA` | | `email` | `string?` | Email de contacto | | `telefono` | `string?` | Teléfono | | `direccion` | `object` | Dirección fiscal (departamento, distrito, ciudad, etc.) | | `actividadesEconomicas` | `array` | Lista de actividades CIIU asociadas | | `estadoRuc` | `string` | Estado en SET (`ACTIVO`, `SUSPENDIDO`, etc.) | | `esFacturadorElectronico` | `boolean` | Si está habilitado para emitir DEs | | `tieneCertificado` | `boolean` | Si tiene certificado activo cargado | | `fechaCreacion` | `datetime` | Fecha de alta en Sifende | | `fechaEdicion` | `datetime` | Última modificación | ## POST /api/v1/contribuyentes/crear [#post-apiv1contribuyentescrear] Crea un nuevo contribuyente para el usuario autenticado. ### CreateContribuyenteRequest [#createcontribuyenterequest] | Campo | Tipo | Requerido | Descripción | | ------------------------------ | --------------- | --------- | ------------------------------------------- | | `ruc` | `string` (≤20) | ✅ | RUC sin dígito verificador | | `dv` | `string` (1) | ✅ | Dígito verificador | | `razonSocial` | `string` (≤255) | ✅ | Razón social | | `tipoContribuyente` | `string` (≤10) | — | `JURIDICA` o `FISICA` | | `email` | `string` (≤100) | — | Email de contacto | | `telefono` | `string` (≤20) | — | Teléfono | | `estadoRuc` | `string` (≤10) | — | Estado SET | | `esFacturadorElectronico` | `boolean` | — | Habilitado para DEs | | `actividadesEconomicasCodigos` | `string[]` | — | Códigos CIIU (usar items con `esHoja=true`) | | `direccion` | `object` | ✅ | Dirección fiscal (ver abajo) | ### Objeto `direccion` [#objeto-direccion] | Campo | Tipo | Requerido | | ----------------- | --------------- | -------------------- | | `departamentoId` | `int` | ✅ | | `distritoId` | `int` | — | | `ciudadId` | `int` | ✅ | | `direccion` | `string` (≤255) | ✅ | | `numeroCasa` | `int` | ✅ (`0` si no aplica) | | `complementoDir1` | `string` (≤255) | — | | `complementoDir2` | `string` (≤255) | — | ### Ejemplo [#ejemplo] ```json { "ruc": "80012345", "dv": "1", "razonSocial": "Comercial Guaraní S.A.", "tipoContribuyente": "JURIDICA", "email": "facturacion@guarani.com.py", "telefono": "021 555 1234", "estadoRuc": "ACTIVO", "esFacturadorElectronico": true, "actividadesEconomicasCodigos": ["4711"], "direccion": { "departamentoId": 1, "distritoId": 1, "ciudadId": 1, "direccion": "Avda. Mariscal López 1234", "numeroCasa": 1234, "complementoDir1": "Edificio Torre Sur", "complementoDir2": "Piso 4" } } ``` Respuesta `201 Created` con el `ContribuyenteDTO` recién creado. ## PATCH /api/v1/contribuyentes/:id/perfil [#patch-apiv1contribuyentesidperfil] Actualiza únicamente `email` y `telefono`. Otros campos (RUC, razón social, dirección) son inmutables desde este endpoint; para corregirlos contactá a soporte. ## GET /api/v1/contribuyentes/consulta/:ruc [#get-apiv1contribuyentesconsultaruc] Consulta el padrón de SIFEN para un RUC específico. Útil para validar datos antes de crear el contribuyente o registrar un receptor. ```bash curl https://api.sifende.com.py/api/v1/contribuyentes/consulta/80012345 \ -H "Authorization: Bearer $KEYCLOAK_JWT" ``` La consulta golpea el web service de SIFEN, así que puede tardar algunos segundos. Cacheá el resultado del lado del cliente cuando puedas. # Documentos Electrónicos (Sesión) (/docs/referencia/sesion/documentos) Esta API utiliza autenticación Keycloak. Ver [Autenticación](/docs/referencia/autenticacion) para obtener un token JWT. Estos endpoints son sólo lectura. Para emitir documentos electrónicos usá el [endpoint con API key](/docs/referencia/documentos-electronicos/emitir). ## Endpoints [#endpoints] | Método | Path | Descripción | | ------ | ---------------------------------------------------------------- | -------------------------- | | `GET` | `/api/v1/contribuyentes/:id/documentos-electronicos` | Lista paginada con filtros | | `GET` | `/api/v1/contribuyentes/:id/documentos-electronicos/:deId` | Detalle de un DE | | `GET` | `/api/v1/contribuyentes/:id/documentos-electronicos/:deId/xml` | Descarga del XML firmado | | `GET` | `/api/v1/contribuyentes/:id/documentos-electronicos/:deId/kude` | Descarga del KuDE en PDF | | `GET` | `/api/v1/contribuyentes/:id/documentos-electronicos/:deId/lotes` | Historial de lotes del DE | ## Filtros del listado [#filtros-del-listado] | Query | Tipo | Descripción | | ------------ | --------------------- | ------------------------------------------------- | | `page` | `int` | Página (default `0`) | | `size` | `int` | Tamaño de página (default `10`) | | `iTiDe` | `short` | `1`=FE, `5`=NCE, `6`=NDE | | `estado` | `string` | `PENDIENTE`, `APROBADO`, `RECHAZADO`, `CANCELADO` | | `fechaDesde` | `date` (`YYYY-MM-DD`) | Fecha de creación desde | | `fechaHasta` | `date` (`YYYY-MM-DD`) | Fecha de creación hasta | ### Ejemplo con filtros [#ejemplo-con-filtros] ```bash curl "https://api.sifende.com.py/api/v1/contribuyentes/12/documentos-electronicos?iTiDe=1&estado=APROBADO&fechaDesde=2026-04-01&fechaHasta=2026-04-30&page=0&size=20" \ -H "Authorization: Bearer $KEYCLOAK_JWT" ``` ## DocumentoElectronicoDTO (listado) [#documentoelectronicodto-listado] | Campo | Tipo | Descripción | | ------------------------ | ------------- | -------------------------------------------------- | | `documentoElectronicoId` | `long` | ID interno | | `cdc` | `string` (44) | Código de Control | | `iTiDe` | `short` | Tipo de DE | | `tipoDocumentoLabel` | `string` | Label legible | | `estado` | `string` | Ver [ciclo de vida](/docs/conceptos/ciclo-de-vida) | | `estadoLabel` | `string` | Label para UI | | `numeroDocumento` | `string` | Formato `001-001-0000123` | | `fechaCreacion` | `datetime` | Alta en Sifende | ### Ejemplo de respuesta [#ejemplo-de-respuesta] ```json { "status": "success", "payload": { "content": [ { "documentoElectronicoId": 9871, "cdc": "01800123451001001000000122026042710000000006", "iTiDe": 1, "tipoDocumentoLabel": "FACTURA_ELECTRONICA", "estado": "APROBADO", "estadoLabel": "Aprobado", "numeroDocumento": "001-001-0000123", "fechaCreacion": "2026-04-15T10:30:00" } ], "page": 0, "size": 20, "totalElements": 432, "totalPages": 22 } } ``` ## DocumentoElectronicoDetalleDTO [#documentoelectronicodetalledto] Incluye todos los campos del listado, más: | Campo | Tipo | Descripción | | ----------------------- | ---------- | ------------------------------------------- | | `numeroEstablecimiento` | `int` | Establecimiento del DE | | `numeroTimbrado` | `int` | Timbrado vigente al emitir | | `loteId` | `long?` | Lote asociado (si ya fue agrupado) | | `estadoLote` | `string?` | Estado del lote | | `qrUrl` | `string?` | URL del QR de SIFEN para el KuDE | | `tieneXml` | `boolean` | Si el XML firmado está disponible | | `tieneCdc` | `boolean` | Si el CDC ya fue generado | | `mensajeRechazo` | `string?` | Mensaje devuelto por SIFEN si fue rechazado | | `codigoRechazo` | `string?` | Código SIFEN del rechazo | | `fechaEdicion` | `datetime` | Última actualización | ### Ejemplo de detalle [#ejemplo-de-detalle] ```bash curl https://api.sifende.com.py/api/v1/contribuyentes/12/documentos-electronicos/9871 \ -H "Authorization: Bearer $KEYCLOAK_JWT" ``` ```json { "status": "success", "payload": { "documentoElectronicoId": 9871, "cdc": "01800123451001001000000122026042710000000006", "iTiDe": 1, "tipoDocumentoLabel": "FACTURA_ELECTRONICA", "estado": "APROBADO", "estadoLabel": "Aprobado", "numeroDocumento": "001-001-0000123", "numeroEstablecimiento": 1, "numeroTimbrado": 12345678, "loteId": 4521, "estadoLote": "PROCESADO", "qrUrl": "https://ekuatia.set.gov.py/consultas/qr?...", "tieneXml": true, "tieneCdc": true, "mensajeRechazo": null, "codigoRechazo": null, "fechaCreacion": "2026-04-15T10:30:00", "fechaEdicion": "2026-04-15T10:31:45" } } ``` ## Descargas binarias [#descargas-binarias] ```bash # XML firmado curl -OJ https://api.sifende.com.py/api/v1/contribuyentes/12/documentos-electronicos/9871/xml \ -H "Authorization: Bearer $KEYCLOAK_JWT" # KuDE en PDF curl -OJ https://api.sifende.com.py/api/v1/contribuyentes/12/documentos-electronicos/9871/kude \ -H "Authorization: Bearer $KEYCLOAK_JWT" ``` Ambos retornan el archivo binario directamente (`application/xml` o `application/pdf`) sin envelope `payload`. ## Historial de lotes del DE [#historial-de-lotes-del-de] Un mismo DE puede haber estado en varios lotes (por reintentos). Este endpoint devuelve la lista completa. ```bash curl https://api.sifende.com.py/api/v1/contribuyentes/12/documentos-electronicos/9871/lotes \ -H "Authorization: Bearer $KEYCLOAK_JWT" ``` # API de Sesión (/docs/referencia/sesion) Estos endpoints están diseñados para el panel web de Sifende y frontends personalizados. Para integraciones ERP, usá los [endpoints de integración](/docs/referencia/documentos-electronicos) con API key. Los endpoints de sesión se autentican con JWT Keycloak y usan el path base: ``` /api/v1/contribuyentes/{contribuyenteId}/... ``` ## Endpoints disponibles [#endpoints-disponibles] | Recurso | Descripción | | -------------------------------------------------------- | --------------------------------------- | | [Contribuyentes](/docs/referencia/sesion/contribuyentes) | CRUD de contribuyentes del usuario | | [Certificado](/docs/referencia/sesion/certificado) | Subida del certificado digital | | [Timbrado](/docs/referencia/sesion/timbrado) | Gestión del timbrado electrónico | | [Lotes](/docs/referencia/sesion/lotes) | Consulta de lotes y transmisiones SIFEN | | [Documentos](/docs/referencia/sesion/documentos) | Listado y detalle de DEs por sesión | | [Usuarios](/docs/referencia/sesion/usuarios) | Perfil del usuario autenticado | | [Métricas](/docs/referencia/sesion/metricas) | Métricas del dashboard | # Lotes (/docs/referencia/sesion/lotes) Esta API utiliza autenticación Keycloak. Ver [Autenticación](/docs/referencia/autenticacion) para obtener un token JWT. Sifende agrupa los documentos electrónicos en lotes antes de transmitirlos a SIFEN. Cada lote pasa por una serie de estados hasta que SIFEN devuelve el resultado de procesamiento. Estos endpoints permiten consultar lotes, reintentar los fallidos y revisar el historial de transmisiones. ## Endpoints [#endpoints] | Método | Path | Query | Descripción | | ------ | -------------------------------------------------------- | -------------- | -------------------------------- | | `GET` | `/api/v1/contribuyentes/:id/lotes` | `page`, `size` | Lista paginada de lotes | | `GET` | `/api/v1/contribuyentes/:id/lotes/:loteId` | — | Detalle de un lote | | `POST` | `/api/v1/contribuyentes/:id/lotes/:loteId/reintentar` | — | Re-encola un lote fallido | | `GET` | `/api/v1/contribuyentes/:id/lotes/:loteId/transmisiones` | — | Historial de transmisiones SIFEN | ## Estados de un lote [#estados-de-un-lote] ``` PREPARADO → INTENTANDO → ENVIADO → PROCESADO ↓ ↓ ERROR FALLIDO ``` | Estado | Descripción | | ------------ | ------------------------------------------------------ | | `PREPARADO` | Lote armado, listo para envío | | `INTENTANDO` | El scheduler está intentando transmitirlo | | `ENVIADO` | Recibido por SIFEN, esperando resultado | | `PROCESADO` | Resultado aplicado a los DEs (APROBADO/RECHAZADO) | | `ERROR` | Error técnico permanente | | `FALLIDO` | Reintentable: podés re-encolarlo (`reintentable=true`) | ## LoteDTO [#lotedto] | Campo | Tipo | Descripción | | ---------------------------- | ----------- | ------------------------------------------- | | `loteId` | `long` | ID interno | | `contribuyenteId` | `long` | Contribuyente emisor | | `numeroLote` | `long` | Número de lote enviado a SIFEN | | `iTiDe` | `short` | Tipo de DE: `1`=FE, `5`=NCE, `6`=NDE | | `tipoDocumentoLabel` | `string` | Label legible (`FACTURA_ELECTRONICA`, etc.) | | `cantidadDocumentos` | `int` | DEs incluidos en el lote | | `estado` | `string` | Ver estados arriba | | `codigoRespuesta` | `string?` | Código SIFEN (`0360`, `0363`, …) | | `mensajeRespuesta` | `string?` | Mensaje devuelto por SIFEN | | `fechaCreacion` | `datetime` | Alta del lote | | `fechaEnvio` | `datetime?` | Cuando se transmitió | | `fechaProcesamientoCompleto` | `datetime?` | Cuando se aplicó el resultado final | | `reintentable` | `boolean` | `true` si `estado=FALLIDO` | | `intentosConsulta` | `int` | Número de consultas a SIFEN realizadas | | `intentosEnvio` | `int` | Número de envíos intentados | ## GET /api/v1/contribuyentes/:id/lotes [#get-apiv1contribuyentesidlotes] Lista paginada (server-side) de lotes. Default: `page=0&size=10`. ```bash curl "https://api.sifende.com.py/api/v1/contribuyentes/12/lotes?page=0&size=20" \ -H "Authorization: Bearer $KEYCLOAK_JWT" ``` Respuesta `200 OK`: ```json { "status": "success", "payload": { "content": [ { "loteId": 4521, "contribuyenteId": 12, "numeroLote": 9876543, "iTiDe": 1, "tipoDocumentoLabel": "FACTURA_ELECTRONICA", "cantidadDocumentos": 8, "estado": "PROCESADO", "codigoRespuesta": "0360", "mensajeRespuesta": "Lote procesado correctamente", "fechaCreacion": "2026-04-15T10:30:00", "fechaEnvio": "2026-04-15T10:30:12", "fechaProcesamientoCompleto": "2026-04-15T10:31:45", "reintentable": false, "intentosConsulta": 2, "intentosEnvio": 1 } ], "page": 0, "size": 20, "totalElements": 87, "totalPages": 5 } } ``` ## POST /api/v1/contribuyentes/:id/lotes/:loteId/reintentar [#post-apiv1contribuyentesidlotesloteidreintentar] Re-encola un lote `FALLIDO` para que el scheduler vuelva a intentar transmitirlo. Sólo funciona si el campo `reintentable` es `true`. ```bash curl -X POST https://api.sifende.com.py/api/v1/contribuyentes/12/lotes/4521/reintentar \ -H "Authorization: Bearer $KEYCLOAK_JWT" ``` Respuesta `200 OK`. Ver la guía [Reintentar rechazados](/docs/guias/reintentar-rechazados) para la estrategia recomendada. ## GET /api/v1/contribuyentes/:id/lotes/:loteId/transmisiones [#get-apiv1contribuyentesidlotesloteidtransmisiones] Historial completo de transmisiones SIFEN para un lote. Útil para depurar problemas de comunicación. ### TransmisionSifenDTO [#transmisionsifendto] | Campo | Tipo | Descripción | | --------------------- | ----------- | ------------------------- | | `transmisionSifenId` | `long` | ID interno | | `loteId` | `long` | Lote asociado | | `tipoTransmision` | `string` | `ENVIO`, `CONSULTA`, etc. | | `numeroIntento` | `int` | Número de intento | | `estadoProcesamiento` | `string` | Resultado del intento | | `fechaEnvio` | `datetime` | Cuándo se envió | | `fechaRespuesta` | `datetime?` | Cuándo respondió SIFEN | Los códigos SIFEN más relevantes en `codigoRespuesta`: `0360` (procesado OK → `PROCESADO`), `0361` (lote no encontrado, reintentar consulta), `0362` (aún procesando), `0363` (errores de validación → `ERROR`) y `0364` (rechazado). # Métricas (/docs/referencia/sesion/metricas) Esta API utiliza autenticación Keycloak. Ver [Autenticación](/docs/referencia/autenticacion) para obtener un token JWT. Endpoint que devuelve un snapshot de KPIs del contribuyente. Pensado para alimentar el dashboard del panel web o tu propio reporte interno. ## GET /api/v1/contribuyentes/:id/metricas [#get-apiv1contribuyentesidmetricas] Retorna métricas agregadas del contribuyente. ### Autenticación [#autenticación] `Authorization: Bearer {keycloak-jwt}` (requerido). El usuario debe ser propietario o miembro del contribuyente. ### Path parameters [#path-parameters] | Parámetro | Tipo | Descripción | | --------- | ------ | -------------------- | | `id` | `long` | ID del contribuyente | ### DashboardMetricasDTO [#dashboardmetricasdto] | Campo | Tipo | Descripción | | ---------------------- | ------------------- | -------------------------------------------- | | `totalDocumentos` | `long` | Cantidad histórica total de DEs emitidos | | `documentosEsteMes` | `long` | DEs emitidos en el mes calendario actual | | `documentosPendientes` | `long` | DEs en estado `PENDIENTE` o `EN_LOTE` | | `documentosAprobados` | `long` | DEs en estado `APROBADO` | | `documentosPorTipo` | `Map` | Conteo por tipo (key = `tipoDocumentoLabel`) | ### Ejemplo [#ejemplo] ```bash curl https://api.sifende.com.py/api/v1/contribuyentes/12/metricas \ -H "Authorization: Bearer $KEYCLOAK_JWT" ``` ### Respuesta exitosa [#respuesta-exitosa] **Status:** `200 OK` ```json { "status": "success", "payload": { "totalDocumentos": 4321, "documentosEsteMes": 287, "documentosPendientes": 12, "documentosAprobados": 4180, "documentosPorTipo": { "FACTURA_ELECTRONICA": 4012, "NOTA_DE_CREDITO_ELECTRONICA": 245, "NOTA_DE_DEBITO_ELECTRONICA": 64 } } } ``` ### Errores [#errores] | Status | Tipo | Causa | | ------ | ------------------------- | --------------------------------------------------------- | | `403` | `access-denied` | El usuario no es propietario ni miembro del contribuyente | | `404` | `contribuyente-not-found` | El `:id` no existe | ## Caso de uso [#caso-de-uso] Útil para construir un dashboard propio en tu sistema: ```typescript const res = await fetch( `https://api.sifende.com.py/api/v1/contribuyentes/${contribuyenteId}/metricas`, { headers: { Authorization: `Bearer ${jwt}` } } ); const { payload } = await res.json(); console.log(`Emitidos este mes: ${payload.documentosEsteMes}`); console.log(`Pendientes de procesamiento: ${payload.documentosPendientes}`); ``` Las métricas se calculan en tiempo real sobre la base de datos del contribuyente; no hay caché. En contribuyentes con cientos de miles de DEs el endpoint puede tardar unos cientos de milisegundos. Este endpoint no acepta filtros de fecha personalizados. Si necesitás métricas con rango personalizado, podés construirlas a partir del listado de [documentos electrónicos](/docs/referencia/sesion/documentos) usando `fechaDesde` / `fechaHasta`. # Timbrado (/docs/referencia/sesion/timbrado) Esta API utiliza autenticación Keycloak. Ver [Autenticación](/docs/referencia/autenticacion) para obtener un token JWT. El timbrado es la autorización emitida por SET (registrada en MARANGATÚ) que habilita al contribuyente a emitir documentos electrónicos. Cada contribuyente tiene un único timbrado activo a la vez. ## Endpoints [#endpoints] | Método | Path | Descripción | | -------- | ------------------------------------- | -------------------------- | | `GET` | `/api/v1/contribuyentes/:id/timbrado` | Obtener el timbrado actual | | `POST` | `/api/v1/contribuyentes/:id/timbrado` | Crear el timbrado | | `PUT` | `/api/v1/contribuyentes/:id/timbrado` | Actualizar el timbrado | | `DELETE` | `/api/v1/contribuyentes/:id/timbrado` | Eliminar el timbrado | El número de timbrado, `fechaInicio` y `fechaFin` deben coincidir exactamente con lo que SET registró en MARANGATÚ. Datos incorrectos producen el rechazo SIFEN 1108 (Timbrado no válido) en cada documento emitido. ## CreateTimbradoRequest / UpdateTimbradoRequest [#createtimbradorequest--updatetimbradorequest] | Campo | Tipo | Requerido (POST) | Descripción | | ---------------- | --------------------------- | ---------------- | ----------------------------------- | | `numeroTimbrado` | `int` (8 dígitos, positivo) | ✅ | Número de timbrado asignado por SET | | `fechaInicio` | `date` (`YYYY-MM-DD`) | ✅ | Fecha de inicio de vigencia | | `fechaFin` | `date` (`YYYY-MM-DD`) | ✅ | Fecha de expiración del timbrado | En `PUT` todos los campos son opcionales: sólo se actualizan los que vengan en el body. `fechaFin` es la fecha de expiración del timbrado. No se pueden emitir documentos electrónicos con fecha posterior a `fechaFin`. Renová el timbrado en SET antes de que expire y actualizalo aquí. ## TimbradoDTO [#timbradodto] | Campo | Tipo | Descripción | | ----------------- | ---------- | ------------------------------ | | `timbradoId` | `long` | ID interno | | `contribuyenteId` | `long` | Contribuyente al que pertenece | | `numeroTimbrado` | `int` | Número de timbrado | | `fechaInicio` | `date` | Inicio de vigencia | | `fechaFin` | `date` | Fin de vigencia | | `fechaCreacion` | `datetime` | Alta en Sifende | | `fechaEdicion` | `datetime` | Última modificación | ## Ejemplos [#ejemplos] ### Crear timbrado [#crear-timbrado] ```bash curl -X POST https://api.sifende.com.py/api/v1/contribuyentes/12/timbrado \ -H "Authorization: Bearer $KEYCLOAK_JWT" \ -H "Content-Type: application/json" \ -d '{ "numeroTimbrado": 12345678, "fechaInicio": "2026-01-01", "fechaFin": "2027-12-31" }' ``` Respuesta `201 Created`: ```json { "status": "success", "payload": { "timbradoId": 34, "contribuyenteId": 12, "numeroTimbrado": 12345678, "fechaInicio": "2026-01-01", "fechaFin": "2027-12-31", "fechaCreacion": "2026-04-15T10:30:00", "fechaEdicion": "2026-04-15T10:30:00" } } ``` ### Consultar timbrado actual [#consultar-timbrado-actual] ```bash curl https://api.sifende.com.py/api/v1/contribuyentes/12/timbrado \ -H "Authorization: Bearer $KEYCLOAK_JWT" ``` ### Eliminar timbrado [#eliminar-timbrado] ```bash curl -X DELETE https://api.sifende.com.py/api/v1/contribuyentes/12/timbrado \ -H "Authorization: Bearer $KEYCLOAK_JWT" ``` Respuesta: `204 No Content`. ## Errores [#errores] | Status | Tipo | Causa | | ------ | -------------------- | ------------------------------------------------------------------ | | `400` | `validation-error` | Número fuera de rango, fecha inválida, `fechaFin` \< `fechaInicio` | | `404` | `timbrado-not-found` | El contribuyente no tiene timbrado cargado | | `409` | `validation-error` | Ya existe un timbrado activo (usá `PUT` o `DELETE` primero) | # Usuarios (/docs/referencia/sesion/usuarios) Esta API utiliza autenticación Keycloak. Ver [Autenticación](/docs/referencia/autenticacion) para obtener un token JWT. Estos endpoints exponen el perfil del usuario autenticado y permiten administrar los miembros del equipo asociados a cada contribuyente. ## Perfil del usuario actual [#perfil-del-usuario-actual] | Método | Path | Descripción | | ------- | --------------------- | ------------------------------ | | `GET` | `/api/v1/usuarios/me` | Perfil del usuario autenticado | | `PATCH` | `/api/v1/usuarios/me` | Actualizar nombre y apellido | ### UpdateUsuarioRequest [#updateusuariorequest] | Campo | Tipo | Requerido | Descripción | | ---------- | --------------- | --------- | -------------------- | | `nombre` | `string` (≤255) | ✅ | Nombre del usuario | | `apellido` | `string` (≤255) | ✅ | Apellido del usuario | `username` y `correoElectronico` son administrados por Keycloak y no se editan desde este endpoint. Para cambiar el email o la contraseña, usá la cuenta de Keycloak (`/auth/realms/sifende/account`). ### UsuarioDTO [#usuariodto] | Campo | Tipo | Descripción | | ------------------- | -------------------- | ----------------------------------- | | `usuarioId` | `long` | ID interno | | `keycloakSub` | `string` | Sub claim del JWT | | `username` | `string` | Username Keycloak | | `correoElectronico` | `string` | Email Keycloak | | `nombre` | `string` | Nombre | | `apellido` | `string` | Apellido | | `estaActivo` | `boolean` | Si la cuenta está activa | | `ultimoLogin` | `datetime?` | Último login registrado | | `fechaCreacion` | `datetime` | Alta en Sifende | | `fechaEdicion` | `datetime` | Última modificación | | `contribuyentes` | `ContribuyenteDTO[]` | Contribuyentes asociados al usuario | ### Ejemplo [#ejemplo] ```bash curl https://api.sifende.com.py/api/v1/usuarios/me \ -H "Authorization: Bearer $KEYCLOAK_JWT" ``` Respuesta: ```json { "status": "success", "payload": { "usuarioId": 4, "keycloakSub": "f8c4a2d1-9b3e-4f7a-b6d2-1c8e5f0a3d7b", "username": "sebastian@guarani.com.py", "correoElectronico": "sebastian@guarani.com.py", "nombre": "Sebastián", "apellido": "Álvarez", "estaActivo": true, "ultimoLogin": "2026-04-27T08:14:33", "fechaCreacion": "2026-01-15T09:00:00", "fechaEdicion": "2026-04-02T11:20:00", "contribuyentes": [ { "contribuyenteId": 12, "ruc": "80012345", "dv": "1", "razonSocial": "Comercial Guaraní S.A.", "tieneCertificado": true } ] } } ``` ## Miembros del equipo [#miembros-del-equipo] Cada contribuyente puede tener múltiples usuarios miembros (operadores) además del propietario. | Método | Path | Descripción | | -------- | ------------------------------------------------ | -------------------------------- | | `GET` | `/api/v1/contribuyentes/:id/miembros` | Lista miembros del contribuyente | | `POST` | `/api/v1/contribuyentes/:id/miembros/invitar` | Envía una invitación por email | | `DELETE` | `/api/v1/contribuyentes/:id/miembros/:usuarioId` | Quita un miembro | ### MiembroDTO [#miembrodto] | Campo | Tipo | Descripción | | ------------------- | ----------- | ----------------------------------------- | | `usuarioId` | `long` | ID del usuario | | `nombre` | `string` | Nombre | | `apellido` | `string` | Apellido | | `correoElectronico` | `string` | Email | | `esPropietario` | `boolean` | `true` si es el creador del contribuyente | | `fechaCreacion` | `datetime` | Alta como miembro | | `ultimoLogin` | `datetime?` | Último login | ### Invitar a un miembro [#invitar-a-un-miembro] ```bash curl -X POST https://api.sifende.com.py/api/v1/contribuyentes/12/miembros/invitar \ -H "Authorization: Bearer $KEYCLOAK_JWT" \ -H "Content-Type: application/json" \ -d '{ "email": "operador@guarani.com.py" }' ``` Respuesta `202 Accepted`: la invitación se envía por email. Si el destinatario no tiene cuenta, recibe un link para registrarse y aceptar. ### Quitar un miembro [#quitar-un-miembro] ```bash curl -X DELETE https://api.sifende.com.py/api/v1/contribuyentes/12/miembros/27 \ -H "Authorization: Bearer $KEYCLOAK_JWT" ``` Respuesta `204 No Content`. Solo el propietario puede quitar miembros. No podés quitar al propietario del contribuyente. Para transferir la propiedad contactá a soporte.