Manual de integración

API REST de FEPY

Todo lo necesario para que tu ERP, POS o sistema propio emita documentos electrónicos en Paraguay a través de SIFEN. Los ejemplos usan https://www.fepy.nicosdev.com/api/<tenant> y son ejecutables tal cual: reemplazá el tenant y el token por los tuyos.

Parte 0Antes de empezar

URL base y tenant

Cada empresa emisora es un tenant: un identificador propio que va en la ruta de todos los endpoints. El equipo de FEPY te entrega el tenant junto con el token cuando da de alta la empresa.

URL base
https://www.fepy.nicosdev.com/api/<tenant>

Para verificar que un tenant existe hay un ping sin autenticación. Devuelve OK con 200 si el tenant está registrado, y Not Found con 404 si no. Es texto plano, no JSON.

get /api/{tenant} sin token

Ping del tenant. Limitado a 60 peticiones por minuto.

Autenticación

Todos los endpoints salvo el ping requieren un token fijo por tenant en la cabecera Authorization. No hay refresh ni expiración corta: el token vive hasta que se rote.

Header
Authorization: Bearer api_key_<tu-token>

El prefijo api_key_ es opcional: el servidor lo quita si está presente. Ambas formas funcionan, pero conviene usar siempre la misma para que los logs sean uniformes.

El token es una credencial de emisión

Con ese token se emiten documentos tributarios a nombre de tu empresa. No lo pongas en código de front-end, en un repositorio, ni en un archivo de configuración versionado. Va en una variable de entorno del servidor.

Códigos HTTP y errores

Leé esto antes de escribir el cliente HTTP

Solo la capa de autenticación devuelve códigos HTTP de error. Todo el resto de la API responde siempre HTTP 200, incluso cuando el documento fue rechazado por SIFEN o el JSON venía mal. El resultado real está en el campo success del cuerpo.

Si tu cliente hace raise_for_status(), .EnsureSuccessStatusCode() o equivalente y nada más, vas a tratar como exitoso un rechazo de SIFEN. Siempre evaluá success.

Códigos que sí devuelve la autenticación

HTTPCuándo
401Falta la cabecera Authorization o no empieza con Bearer
400Falta el parámetro tenant en la ruta
403El token no existe o no corresponde a ese tenant
503Empresa bloqueada, o certificado digital vencido

Forma del error de negocio

HTTP 200
{
  "success": false,
  "error": "Algunos DEs presentan errores de validación previa",
  "errors": [
    { "index": 0, "error": "Faltan datos obligatorios." }
  ]
}

El campo errors aparece solo cuando el fallo es por documento: cada entrada trae el index dentro del array que enviaste, para que puedas mapear el error al documento exacto.

Tu primera llamada

/test confirma tres cosas de una: que el token es válido, en qué ambiente está la empresa, y si está conectada a SIFEN.

get /api/{tenant}/test

Agregá ?text=t para recibir texto plano en vez de JSON.

curl
curl -H "Authorization: Bearer api_key_<tu-token>" \
  https://www.fepy.nicosdev.com/api/<tenant>/test
Respuesta
{
  "success": true,
  "message": "Conectado con FEPY, hora del Servidor: 2026-09-07 17:25:48",
  "data": {
    "empresa": "80012345-0 | EMPRESA EMISORA S.A.",
    "conectado_sifen": false,
    "idCdc": "0001",
    "ambiente": "test"
  }
}
CampoSignificado
empresaRUC y razón social del emisor detrás del token
conectado_sifentrue firma y transmite a SIFEN. false genera sin firmar — ver Parte 5
idCdcIdentificador del CSC configurado para el QR
ambientetest o prod

La versión ?text=t agrega la última factura aprobada de la empresa, útil cuando migrás de servidor y necesitás saber en qué numeración quedaste.

Parte 1Adaptá tu sistema

Antes de escribir la primera llamada conviene revisar qué le falta a tu sistema. La facturación electrónica no agrega un endpoint: agrega campos obligatorios en clientes, productos y comprobantes que probablemente hoy no tengas, o tengas como texto libre.

Numeración de documentos

Cada documento se identifica por la terna establecimiento · punto de expedición · número, con ceros a la izquierda. FEPY no lleva el correlativo por vos: el número lo asigna tu sistema y debe ser único e irrepetible por terna.

CampoLargoEjemplo
establecimiento3 dígitos001
punto3 dígitos001
numero7 dígitos0000001

El resultado visible es 001-001-0000001. Podés mandar los campos sin padding (por ejemplo 1): FEPY completa los ceros. Lo que no puede hacer es adivinar cuál sigue.

Repetir una terna es un rechazo de SIFEN

Dentro de un mismo lote, FEPY detecta la terna duplicada en la validación previa y te devuelve el error antes de firmar. Entre lotes distintos, el rechazo llega de SIFEN. Guardá el correlativo en tu base con un candado de concurrencia.

Clientes y tipoOperacion

El bloque cliente cambia de forma según quién sea el receptor. Las dos decisiones que gobiernan todo lo demás son contribuyente (si tiene RUC o no) y tipoOperacion.

tipoOperacionCasoCuándo usarlo
1B2BEl cliente tiene RUC — persona física o jurídica
2B2CCédula, pasaporte o consumidor final sin documento
3B2GOrganismo del Estado. Exige datos DNCP
4B2FReceptor del exterior con documento extranjero
Tablas de esta sección Tipo de receptor 2 CSV JSON Tipos de documento de identidad 5 CSV JSON

Con contribuyente: true mandás ruc y tipoContribuyente (1 persona física, 2 persona jurídica). Con contribuyente: false mandás documentoTipo y documentoNumero.

Migrar clientes existentes

Si hoy guardás el RUC como texto libre, vas a necesitar normalizarlo y validar el dígito verificador. El endpoint /ruc/{ruc} consulta el padrón de SIFEN y devuelve el RUC con su DV calculado — sirve para depurar la base antes de salir a producción.

Catálogos a mapear

SIFEN trabaja con códigos, no con descripciones. Estos son los catálogos que típicamente hay que agregar a un sistema que nunca facturó electrónicamente:

QuéDónde impactaTabla
Departamento · distrito · ciudadEstablecimiento del emisor y dirección del receptordepartamentos, distritos, ciudades
PaísesReceptores del exteriorpaises
MonedasCabecera del documento y entregasmonedas
Unidades de medidaCada ítemunidades-medida
Tipos de documento de identidadReceptores sin RUCtipos-documentos-identidades
Condiciones de pagoContado / crédito y formas de pagocondiciones-tipos-pagos
Tablas de esta sección Departamentos 18 CSV JSON Distritos 252 CSV JSON Ciudades 6.414 CSV JSON Paises 248 CSV JSON Monedas 200 CSV JSON Unidades de medida 34 CSV JSON Tipos de documento de identidad 5 CSV JSON Condiciones — tipos de pago 22 CSV JSON

Los tres primeros están encadenados: la ciudad pertenece a un distrito y el distrito a un departamento. Si mandás una combinación inconsistente, el generador la rechaza. El apéndice tiene el catálogo completo, y un ZIP con todo.

Qué persistir de la respuesta

La respuesta de emisión trae datos que tu sistema necesita guardar. Si no los guardás, después no podés consultar el estado ni reimprimir el KUDE.

CampoPor qué guardarlo
cdcClave del documento, 44 dígitos. Es la llave para consultar, cancelar, descargar XML y KUDE
loteIdPermite consultar el estado del envío completo
situacionEstado numérico del documento (ver tabla de situaciones)
respuesta_codigo y respuesta_mensajeMotivo del rechazo cuando lo hay — sin esto no sabés qué corregir
qrURL del QR impreso en el KUDE
numeroFacturaTerna formateada, para mostrar al usuario
El XML también viene en la respuesta

El campo xml trae el documento firmado completo. Podés guardarlo o pedirlo después con /de/xml/{cdc}. La conservación del XML por el plazo legal es responsabilidad del emisor.

Parte 2Emisión de documentos

Hay dos caminos para emitir: por lote (asíncrono, el recomendado) y documento a documento (síncrono). Salvo que tengas una razón concreta para el segundo, usá el lote — es el que soporta borradores, el que devuelve loteId para seguimiento y el que se usa en producción.

POST /lote/create

post /api/{tenant}/lote/create

Recibe un array JSON de documentos, todos del mismo tipoDocumento. Query opcional: ?draft=true crea el lote en borrador sin transmitirlo a SIFEN.

Tipos de documento

tipoDocumentoDocumento
1Factura Electrónica
4Autofactura Electrónica
5Nota de Crédito Electrónica
6Nota de Débito Electrónica
7Nota de Remisión Electrónica
Tablas de esta sección Tipos documentos 8 CSV JSON
Un lote, un tipo

Si mezclás tipos en el mismo array, la validación previa devuelve "Tipo de documento inconsistente." con el índice del documento culpable. Armá un lote por tipo.

Números inutilizados

Si un documento usa un número cubierto por una inutilización aprobada (mismo tipo, establecimiento y punto, en el ambiente actual de la empresa), la validación previa lo rechaza sin guardar nada: "Existe número inutilizado en el rango solicitado: numero=15, establecimiento=001, punto=001 [4066]" con el índice del documento. Emití con un número nuevo.

Ejemplo mínimo: una factura

curl
curl -X POST https://www.fepy.nicosdev.com/api/<tenant>/lote/create \
  -H "Authorization: Bearer api_key_<tu-token>" \
  -H "Content-Type: application/json" \
  -d @factura.json
factura.json
[
  {
    "tipoDocumento": 1,
    "establecimiento": "001",
    "punto": "001",
    "numero": "0000001",
    "fecha": "2026-09-07T10:00:00",
    "tipoTransaccion": 2,
    "tipoImpuesto": 1,
    "moneda": "PYG",
    "cliente": {
      "contribuyente": true,
      "ruc": "4303489-6",
      "razonSocial": "CLIENTE DE PRUEBA S.A.",
      "tipoOperacion": 1,
      "pais": "PRY",
      "tipoContribuyente": 2,
      "codigo": "001"
    },
    "factura": { "presencia": 1 },
    "items": [
      {
        "codigo": "SRV001",
        "descripcion": "Servicio de prueba",
        "unidadMedida": 77,
        "cantidad": 1,
        "precioUnitario": 550000,
        "ivaTipo": 1,
        "ivaBase": 100,
        "iva": 10
      }
    ],
    "condicion": {
      "tipo": 1,
      "entregas": [
        { "tipo": 1, "monto": 550000, "moneda": "PYG" }
      ]
    }
  }
]

Estructura del documento

Cabecera

CampoDescripción
tipoDocumentorequeridoTipo del documento (tabla de arriba)
establecimientorequerido3 dígitos
puntorequerido3 dígitos
numerorequerido7 dígitos
fecharequeridoISO 8601 sin zona: 2026-09-07T10:00:00
tipoTransaccionopcionalNaturaleza de la operación
tipoImpuestoopcional1 IVA
monedarequeridoISO 4217: PYG, USD…
cambioopcionalTipo de cambio si la moneda no es PYG
codigoSeguridadAleatorioopcional9 dígitos. Si no lo mandás, FEPY lo genera
Tablas de esta sección Monedas 200 CSV JSON Tipos transacciones 13 CSV JSON Tipos impuestos 5 CSV JSON Tipos emisiones 2 CSV JSON
La fecha va en hora de Paraguay

SIFEN valida la coherencia entre la fecha de emisión y la de firma (validación A004). Mandá la fecha en America/Asuncion, nunca en UTC: un desfasaje de horas puede caer fuera de la ventana aceptada.

Bloque cliente

CampoDescripción
contribuyenterequeridotrue si tiene RUC
tipoOperacionrequeridoB2B / B2C / B2G / B2F — ver Parte 1
razonSocialrequeridoNombre del receptor
paisrequeridoISO 3166: PRY
ruccondicionalCon contribuyente: true. Formato NNNNNNN-D
tipoContribuyentecondicionalCon RUC: 1 física, 2 jurídica
documentoTipocondicionalCon contribuyente: false
documentoNumerocondicionalCon contribuyente: false
correoopcionalDestino del envío automático del KUDE
direccion, numeroCasa, departamento, distrito, ciudadopcionalDomicilio del receptor
Tablas de esta sección Paises 248 CSV JSON Tipos de documento de identidad 5 CSV JSON Departamentos 18 CSV JSON Distritos 252 CSV JSON Ciudades 6.414 CSV JSON

Bloque items

CampoDescripción
codigorequeridoCódigo interno del producto o servicio
descripcionrequeridoDetalle que se imprime en el KUDE
unidadMedidarequeridoCódigo SIFEN — 77 es unidad
cantidadrequeridoAdmite decimales
precioUnitariorequeridoPrecio con IVA incluido
ivaTiporequerido1 gravado, 2 exonerado, 3 exento, 4 gravado parcial
ivaBaserequeridoProporción gravada: 100 salvo gravado parcial
ivarequeridoTasa: 10, 5 o 0
descuentoopcionalDescuento por ítem
Tablas de esta sección Unidades de medida 34 CSV JSON Códigos de afectación 4 CSV JSON

Bloque condicion

tipo: 1 es contado y lleva entregas. tipo: 2 es crédito y lleva el bloque credito con plazo o cuotas.

Crédito a 30 días
"condicion": {
  "tipo": 2,
  "credito": {
    "tipo": 1,
    "plazo": "30 días"
  }
}

Bloques por tipo de documento

BloqueAplica aContenido
facturaFactura (1)presencia: indicador de presencia de la operación
autoFacturaAutofactura (4)Datos del vendedor no contribuyente
notaCreditoDebitoNC (5) y ND (6)motivo de la nota
remisionNota de remisión (7)Motivo, responsable y datos del traslado
documentoAsociadoNC, ND y remisióncdc del documento original y formato
Tablas de esta sección Indicadores presencias 7 CSV JSON Motivos de nota de crédito 8 CSV JSON Motivos de remisión 15 CSV JSON Responsables de remisión 5 CSV JSON Tipos de documento asociado 3 CSV JSON

Respuesta y situaciones

Respuesta
{
  "success": true,
  "result": {
    "loteId": 42,
    "deList": [
      {
        "numeroFactura": "001-001-0000001",
        "cdc": "01800123450001001000000112026090711234567890",
        "serie": "",
        "respuesta_codigo": "0260",
        "respuesta_mensaje": "Aprobado",
        "situacion": 1,
        "estado": "Aprobado",
        "fechaFirma": "2026-09-07T11:47:58",
        "fechaEmision": "2026-09-07T10:00:00",
        "loteId": 42,
        "isEncontrado": false,
        "qr": "https://ekuatia.set.gov.py/consultas/qr?nVersion=150&Id=...",
        "xml": "<?xml version=\"1.0\"?>..."
      }
    ]
  }
}

Situaciones

situacionEstadoQué significa
-1BorradorGenerado y guardado, pendiente de confirmación
0GeneradoXML armado, todavía no transmitido
1EncoladoEnviado a SIFEN, esperando el resultado del lote
2AprobadoDocumento válido
3Aprobado con observaciónVálido, con advertencias de SIFEN
4RechazadoSIFEN lo rechazó. El motivo viene en respuesta_mensaje
99CanceladoSe le aplicó un evento de cancelación
90InutilizadoSu número quedó dentro de un evento de inutilización aprobado. Lo informan las consultas; no se guarda en el documento. No reintentar ese número
Encolado no es aprobado

El lote es asíncrono: SIFEN acusa recibo y procesa después. Un documento en situacion: 1 todavía puede terminar rechazado. Para cerrar el circuito consultá /lote/{id} o /cdc/{cdc} unos segundos más tarde.

Borradores y confirmación

Con ?draft=true el lote se genera, se firma y se guarda, pero no se transmite. Sirve para previsualizar el KUDE antes de emitir en firme — el caso típico es el mostrador, donde el cliente todavía puede cambiar algo.

post /api/{tenant}/lote/create?draft=true

Devuelve loteId y los documentos en situacion: -1.

post /api/{tenant}/lote/draft-confirm/{loteId}

Transmite a SIFEN un lote que está en borrador. Sin cuerpo.

ErrorCausa
Lote no encontradoEl loteId no existe o es de otra empresa
Lote ya procesadoEse lote no está en borrador — confirmar dos veces no duplica la emisión

POST /lote/qr

post /api/{tenant}/lote/qr

Mismo cuerpo que /lote/create, pero no persiste nada: valida, arma el XML y devuelve solo cdc y qr.

Es el endpoint ideal para probar un payload nuevo: si pasa acá, pasa en /lote/create, y no te deja documentos basura en la base.

Respuesta
{
  "success": true,
  "result": {
    "deList": [
      {
        "cdc": "01800123450001001000000112026090711234567890",
        "qr": "https://ekuatia.set.gov.py/consultas/qr?nVersion=150&Id=..."
      }
    ]
  }
}

POST /de/create (síncrono)

post /api/{tenant}/de/create

Emite un documento y espera la respuesta de SIFEN en la misma llamada. Acepta un objeto o un array — si mandás un array, procesa el primer elemento.

La respuesta es el envelope de SIFEN parseado, no el deList del lote. El estado resuelto queda persistido con situacion 2, 3 o 4 según lo que devuelva SIFEN.

Cuándo NO usar el síncrono

Bloquea tu proceso mientras SIFEN responde, no admite borradores, y emite de a un documento. Para volumen o para cualquier flujo donde el usuario esté esperando en pantalla, usá /lote/create.

Parte 3Consultas y descargas

Como la emisión por lote es asíncrona, estas consultas son las que cierran el circuito: confirman si SIFEN aprobó, rechazó o todavía está procesando.

Estado de un lote

get /api/{tenant}/lote/{id}

Devuelve el estado de todos los documentos del lote. Query: ?qr=f y ?xml=f omiten esos campos — útil si solo querés los estados y no querés arrastrar los XML.

curl
curl -H "Authorization: Bearer api_key_<tu-token>" \
  "https://www.fepy.nicosdev.com/api/<tenant>/lote/42?xml=f"

El lote debe pertenecer a la empresa del token. Si no, la respuesta es Lote no encontrado — no se filtra información de otros tenants.

Estado por CDC

get /api/{tenant}/cdc/{cdc}

Estado de un documento propio. Con ?forzar=t además consulta SIFEN en vivo y agrega el bloque consulta_forzada.

Respuesta
{
  "success": true,
  "data": {
    "numeroFactura": "001-001-0000001",
    "cdc": "0180012345000100100000001...",
    "is_conectado": true,
    "ambiente": "t",
    "respuesta_codigo": "0260",
    "respuesta_mensaje": "Aprobado",
    "situacion": 2,
    "estado": "Aprobado",
    "fechaFirma": "2026-09-07T11:47:58",
    "fechaEmision": "2026-09-07T10:00:00",
    "qr": "https://ekuatia.set.gov.py/consultas/qr?...",
    "json": { },
    "xml": "<?xml ...",
    "eventos": [],
    "loteId": 42
  }
}
Documentos rechazados

Cuando situacion es 4, la respuesta agrega un bloque error con el código, el mensaje y el XML de respuesta del lote. Ahí está el motivo exacto del rechazo.

Estados en bloque

post /api/{tenant}/de/estado

Estado de varios CDC en una sola llamada. Query: ?force=t reconsulta cada uno contra SIFEN; ?qr=f y ?xml=f alivianan la respuesta.

Cuerpo
{
  "cdcList": [
    { "cdc": "0180012345000100100000001..." },
    { "cdc": "0180012345000100100000002..." }
  ]
}

La respuesta es {"success": true, "deList": [...]} con un elemento por CDC encontrado. Los CDC inexistentes se omiten en silencio: compará contra la lista que enviaste para detectar faltantes.

force=t es caro

Con force los CDC se consultan a SIFEN uno por uno, en serie. Una lista larga puede tardar. Usalo para reconciliar, no en el camino caliente de tu aplicación.

CDC de terceros

get /api/{tenant}/cdc-terceros/{cdc}

Consulta directa a SIFEN de un documento que no emitiste vos — por ejemplo, para validar una factura de proveedor.

QueryResultado
sin parámetrosXML crudo del documento (text/xml)
?json=tCampos extraídos del XML, en JSON
?detallado=t&json=tJSON con el envelope completo

Si el documento no existe o fue cancelado, la respuesta es {"success": false, "error": "CDC no encontrado o cancelado"}.

Consulta de RUC

get /api/{tenant}/ruc/{ruc}

Consulta el padrón de SIFEN. Podés mandar el RUC con o sin dígito verificador: si lo mandás con guion, se ignora la parte del DV para consultar.

En la respuesta, el campo dRUCCons vuelve con el DV calculado por FEPY (módulo 11). Sirve para normalizar la base de clientes sin implementar el cálculo por tu cuenta.

curl
curl -H "Authorization: Bearer api_key_<tu-token>" \
  https://www.fepy.nicosdev.com/api/<tenant>/ruc/4303489

Descarga del XML

get /api/{tenant}/de/xml/{cdc}

Devuelve el XML firmado como descarga (application/xml, nombre de archivo <cdc>.xml).

Solo entrega documentos en situación aprobado (2), aprobado con observación (3) o cancelado (99), y solo de tu propia empresa. Cualquier otro caso responde 404 sin cuerpo.

Descarga del KUDE

post /api/{tenant}/de/pdf

Genera el PDF de representación gráfica de uno o varios documentos.

Cuerpo
{
  "cdcList": [
    { "cdc": "0180012345000100100000001..." }
  ]
}

Con un solo CDC el archivo se llama <Tipo>_<numeroFactura>.pdf. Con varios, los documentos van en un único PDF llamado <Tipo>_cant_<N>.pdf. El formato de hoja sale de la configuración de la empresa o del establecimiento.

Parámetros de la empresa

get /api/{tenant}/params

Devuelve la configuración del emisor: RUC, razón social, timbrado, establecimientos, actividades económicas y ambiente.

Útil para que tu sistema lea de acá los establecimientos y puntos habilitados en vez de tenerlos duplicados en tu configuración.

El certificado no se expone

El bloque certificado viene recortado a uuid y expiracion. El archivo .pfx y su passphrase nunca salen por la API — un token de tenant no puede extraer la clave de firma.

Parte 4Eventos y anulación

Un documento electrónico aprobado no se borra ni se edita. Lo que se hace es registrar un evento sobre él, o emitir una nota de crédito que lo compense. Todos los eventos van por el mismo endpoint, cambiando el tipo en la ruta.

post /api/{tenant}/evento/{tipo}

{tipo} es cancelacion, inutilizacion o nominacion. Cualquier otro valor devuelve Evento inexistente o no soportado.

Respuesta

La respuesta trae result con el envelope de SIFEN y, sobre todo, isExitoso: el booleano que indica si SIFEN aprobó el evento. Cuando el envío falla, el evento no queda registrado.

Respuesta
{
  "success": true,
  "isExitoso": true,
  "result": {
    "ns2:rRetEnviEventoDe": {
      "ns2:gResProcEVe": {
        "ns2:dEstRes": "Aprobado",
        "ns2:gResProc": {
          "ns2:dCodRes": "0600",
          "ns2:dMsgRes": "Evento registrado exitosamente"
        }
      }
    }
  }
}
success e isExitoso no son lo mismo

success: true significa que la comunicación con SIFEN salió bien. isExitoso es lo que dice SIFEN del evento en sí. Podés recibir success: true con isExitoso: false — el evento fue rechazado. Validá los dos.

Cancelación

Anula un documento aprobado. Al aprobarse, FEPY marca el documento local en situacion: 99 (Cancelado).

POST /evento/cancelacion
{
  "cdc": "01800123450001001000000112026090711234567890",
  "motivo": "Error en los datos del receptor"
}
La cancelación tiene plazo

SIFEN acepta la cancelación dentro de una ventana desde la emisión — 48 horas para facturas, más amplia para otros documentos. Pasado el plazo, el evento se rechaza y el camino que queda es la nota de crédito.

Inutilización

Declara ante SIFEN un rango de numeración que nunca se va a usar. Es para saltos de correlativo: se cortó la luz, se perdió una numeración, se anuló un preimpreso.

POST /evento/inutilizacion
{
  "tipoDocumento": 1,
  "establecimiento": "001",
  "punto": "001",
  "desde": 15,
  "hasta": 18,
  "motivo": "Salto de numeracion por corte de energia",
  "timbrado": "12345678"
}

No lleva cdc: no anula documentos, reserva números. Si el rango incluye un número ya emitido, SIFEN rechaza el evento.

Una vez aprobado, las consultas de estado (cdc, de/estado, lote/{id}) informan con situacion: 90 (Inutilizado), inutilizado: true y fechaInutilizacion todo documento local de ese tipo, establecimiento y punto cuyo número caiga en el rango y que no esté en cola (0/1), aprobado (2/3) ni cancelado (99). El 90 se calcula al responder: no se guarda en el documento y esos números no se consultan en SIFEN.

Si algún documento del rango todavía está en cola (0/1), el evento se rechaza sin registrarse ni enviarse a SIFEN: "Hay documentos en cola dentro del rango a inutilizar. Espere el resultado del lote antes de enviar el evento." Lo mismo vale para cancelación y nominación de un documento en cola: "El documento todavía está en cola en SIFEN. Espere el resultado del lote antes de enviar el evento."

Un lote nuevo que use un número del rango se rechaza en la validación previa con [4066] Existe número inutilizado en el rango solicitado, sin guardar nada.

Nominación

Asigna o corrige los datos del receptor de un documento ya aprobado — el caso clásico es la factura emitida a consumidor final que el cliente después pide a su RUC.

POST /evento/nominacion
{
  "cdc": "01800123450001001000000112026090711234567890",
  "cliente": {
    "contribuyente": true,
    "ruc": "4303489-6",
    "razonSocial": "CLIENTE DE PRUEBA S.A.",
    "tipoOperacion": 1,
    "pais": "PRY",
    "tipoContribuyente": 2
  }
}

Anulación por nota de crédito

post /api/{tenant}/anulacion

Atajo que emite una nota de crédito total contra una factura aprobada, sin que tengas que armar el documento vos.

Cuerpo
{
  "cdcFactura": "01800123450001001000000112026090711234567890",
  "comprobante": "001-001-0000045"
}
CampoReglas
cdcFactura44 dígitos y debe empezar con 01 — solo se anulan facturas
comprobanteNumeración de la nota de crédito, formato XXX-XXX-XXXXXXX, exactamente 15 caracteres

FEPY toma los ítems, el cliente y la moneda de la factura original, arma la nota de crédito con tipoDocumento: 5, motivo 2 y el documento asociado apuntando al CDC, y la emite. La respuesta es la de /lote/create más un bloque info_anulacion.

El número de la NC lo ponés vos

comprobante es la numeración que va a llevar la nota de crédito, no la de la factura anulada. Sale de tu correlativo de notas de crédito.

Cancelación o nota de crédito

Dentro del plazo, cancelar es más limpio: el documento queda anulado y no genera un comprobante nuevo. La nota de crédito es el camino cuando el plazo venció, o cuando la devolución es parcial — en ese caso armá la NC a mano con /lote/create, porque este atajo siempre es total.

Parte 5Desarrollar sin certificado

Conseguir el certificado digital lleva tiempo, y no tiene sentido que tu integración espere. Una empresa puede quedar configurada en modo desconectado: valida todo, arma el XML, calcula el CDC y devuelve documentos aprobados — sin firmar y sin transmitir a SIFEN.

Lo reconocés en /test: el campo conectado_sifen viene en false.

Qué hace el modo desconectado

PasoModo desconectado
Validación previa del lote (terna, tipo consistente, duplicados)se ejecuta
Validación estructural del JSON del documentose ejecuta
Generación del XML y cálculo del CDCse ejecuta
Persistencia del documento en la basese ejecuta
Firma XMLDSig con el certificadose omite
Generación del QRse omite
Transmisión a SIFENse omite

Es decir: todo el trabajo de integración se puede hacer y probar. El armado del payload, el mapeo de catálogos, el manejo de errores de validación y la numeración se ejercitan igual que en producción.

Respuesta de /lote/create
{
  "success": true,
  "message": "Lote creado con éxito - Sin conexión a SIFEN",
  "result": {
    "loteId": 0,
    "deList": [
      {
        "numeroFactura": "001-001-0000001",
        "cdc": "01800123450001001000000112026090711234567890",
        "xml": "<?xml version=\"1.0\"?>..."
      }
    ]
  }
}

Los documentos quedan guardados con respuesta_codigo: "0260", estado: "Aprobado" y situacion: 1. Son aprobaciones simuladas: te sirven para desarrollar el flujo completo de tu lado.

Límites y trampas

Tres cosas que te van a morder
  • loteId viene en 0. El lote sí se creó en la base, pero la respuesta devuelve cero. Si tu sistema guarda ese valor y después consulta /lote/0, no va a encontrar nada.
  • No hay QR ni XML firmado. El campo qr viene vacío y xml trae el documento sin firma. El KUDE que generes en este modo no tiene un QR válido — sirve para maquetar, no para entregar.
  • El ciclo asíncrono no se simula. No hay protocolo de lote, no hay consulta posterior, no hay transición de estados. El documento nace aprobado. La lógica de reintento y reconciliación contra /lote/{id} no se ejercita acá.

Qué probar igual antes de conectar

Probable en desconectadoRequiere certificado
Armado del payload y mapeo de catálogosFirma y validación criptográfica de SIFEN
Errores de validación previa y estructuralRechazos de reglas de negocio de SIFEN
Control de numeración y duplicadosCiclo Encolado → Aprobado / Rechazado
Persistencia del CDC en tu baseQR, KUDE definitivo y eventos
El timbrado de la empresa de pruebas

En una empresa de desarrollo el timbrado y su fecha suelen ser de relleno: alcanzan para generar el XML, pero SIFEN los rechaza. Antes de activar conectado_sifen, cargá el timbrado real junto con el certificado.

Cuando el certificado esté cargado y la empresa se conecte, tu integración no cambia: los mismos endpoints empiezan a devolver qr, XML firmado y situaciones reales de SIFEN.

ApéndiceTablas de códigos SIFEN

Estas tablas salen del mismo catálogo que usa el generador de XML, no de una copia pegada en la documentación. Lo que descargás acá es lo que valida tus documentos.

Descargar las tablas

Cada tabla está en CSV —para abrir en Excel o importar a tu base— y en JSON, para sembrarla desde un script. El CSV lleva BOM, así que Excel en Windows respeta los acentos sin que tengas que tocar nada.

Catálogo completo

Las 40 tablas en CSV y JSON, más un LEEME.txt con el índice y el conteo de filas.

Descargar ZIP ↓
40 tablas
Departamentos 18
departamentos
Distritos 252
distritos
Ciudades 6.414
ciudades
Paises 248
paises
Monedas 200
monedas
Unidades de medida 34
unidades-medida
Tipos documentos 8
tipos-documentos
Tipos de documento de identidad 5
tipos-documentos-identidades
Tipos de documento asociado 3
tipos-documentos-asociados
Tipos de documento impreso 5
tipos-documentos-impresos
Tipos de documento del receptor 7
tipos-documentos-receptor
Tipo de receptor 2
tipo-receptor
Tipos emisiones 2
tipos-emisiones
Tipos impuestos 5
tipos-impuestos
Tipos operaciones 4
tipos-operaciones
Tipos de régimen 8
tipos-regimenes
Tipos transacciones 13
tipos-transacciones
Tipos transportes 2
tipos-transportes
Tipos constancias 2
tipos-constancias
Tipos combustibles 6
tipos-combustibles
Condiciones operaciones 2
condiciones-operaciones
Condiciones — tipos de pago 22
condiciones-tipos-pagos
Condiciones — tipos de crédito 2
condiciones-creditos-tipos
Condiciones negociaciones 11
condiciones-negociaciones
Indicadores presencias 7
indicadores-presencias
Motivos de nota de crédito 8
notas-creditos-motivos
Motivos de remisión 15
remisiones-motivos
Responsables de remisión 5
remisiones-responsables
Responsables de flete 5
responsables-fletes
Modalidades transportes 4
modalidades-transportes
Características de carga 3
caracteristicas-cargas
Relevancias de mercadería 2
relevancias-mercaderias
Códigos de afectación 4
codigos-afectaciones
Categorías ISC 5
categorias-isc
Tasas ISC 12
tasas-isc
Tarjetas de crédito — tipos 7
tarjetas-creditos-tipos
Tarjetas de crédito — formas de procesamiento 3
tarjetas-creditos-formas-procesamiento
Naturaleza del vendedor (autofactura) 2
naturaleza-vendedor-autofactura
Tipo de conformidad del evento 2
evento-conformidad-tipo
Global por ítem 2
global-por-item
Departamento, distrito y ciudad están encadenados

Cada fila de distritos declara su departamento, y cada fila de ciudades su distrito. Importá las tres tablas y respetá la cadena: una combinación inconsistente hace fallar la generación del XML.

Consumo por HTTP

Las mismas tablas se sirven sin autenticación, para que puedas actualizarlas desde un script en vez de bajarlas a mano.

get /manual/codigos sin token

Lista las tablas disponibles con su slug, etiqueta y cantidad de filas.

get /manual/codigos/{tabla} sin token

Devuelve la tabla como JSON en línea, envuelta en {success, tabla, total, data}.

get /manual/codigos/{tabla}.{csv|json} sin token

Entrega la tabla como archivo adjunto. El JSON acá es el array pelado, sin envoltorio.

get /manual/codigos/todo.zip sin token

El catálogo completo en un ZIP, con las carpetas csv/ y json/.

curl
curl https://www.fepy.nicosdev.com/manual/codigos
curl -O https://www.fepy.nicosdev.com/manual/codigos/unidades-medida.csv
curl -O https://www.fepy.nicosdev.com/manual/codigos/todo.zip
Respuesta de /manual/codigos
{
  "success": true,
  "tablas": [
    { "tabla": "departamentos", "etiqueta": "Departamentos", "filas": 18 },
    { "tabla": "distritos",     "etiqueta": "Distritos",     "filas": 252 }
  ]
}

Un slug fuera del catálogo devuelve 404 con {"success": false, "error": "Tabla inexistente"}.