Manual de la API
Introducción y endpoints
Esta API HTTP permite crear facturas electrónicas, notas de crédito y gestionar documentos electrónicos de forma simple. Utiliza autenticación vía API Key de Element.
Características
- Crear facturas al contado y a crédito
- Crear notas de crédito
- Cancelar documentos electrónicos
- Consultar facturas por número
- Registro automático de pagos
- Auto-creación de clientes y productos
Resumen de endpoints
| Método | Endpoint | Descripción |
|---|---|---|
| GET | /api/fe/health | Estado del servicio |
| POST | /api/fe/invoice | Crear factura |
| POST | /api/fe/credit-note | Crear nota de crédito |
| GET | /api/fe/invoice/{numero} | Buscar factura |
| POST | /api/fe/invoice/{numero}/cancel | Cancelar factura |
| POST | /api/fe/invoice/{id}/payment | Registrar pago |
| POST | /api/fe/remision | Crear nota de remisión |
| GET | /api/fe/remision/{numero} | Buscar nota de remisión |
| POST | /api/fe/remision/{numero}/cancel | Cancelar nota de remisión |
| POST | /api/fe/remision/{id}/send-email | Enviar nota de remisión por correo |
| GET | /api/fe/invoice/{de_id}/kude-pdf | Descargar KUDE PDF de factura o nota de crédito |
| GET | /api/fe/remision/{de_id}/kude-pdf | Descargar KUDE PDF de nota de remisión |
| POST | /api/fe/partner/by-ruc/{ruc} | Buscar o crear cliente por RUC |
| POST | /api/fe/partner/by-ruc/{ruc}/update | Actualizar cliente por RUC |
| POST | /api/fe/partner/by-ci/{ci}/update | Actualizar cliente por CI |
Health check
GET /api/fe/health
Verifica que el servicio está disponible. No requiere autenticación.
# Este endpoint no requiere API Key, pero si la cookie de sesion:
# es la que le dice al servidor a que base de datos enrutar.
# Sirve para verificar que el modulo esta instalado y el
# servicio responde antes de enviar peticiones.
resp = session.get(f"{URL}/api/fe/health")
print(resp.json())
# Respuesta esperada:
# {
# "success": true,
# "status": "ok",
# "module": "eterp_de_api",
# "version": "16.0.1.0.0"
# }// Este endpoint no requiere API Key, pero si la cookie de sesion:
// es la que le dice al servidor a que base de datos enrutar.
// Sirve para verificar que el modulo esta instalado y el
// servicio responde antes de enviar peticiones.
const resp = await fetch(`${URL}/api/fe/health`, { headers: { Cookie: cookie } });
console.log(await resp.json());
// Respuesta esperada:
// {
// "success": true,
// "status": "ok",
// "module": "eterp_de_api",
// "version": "16.0.1.0.0"
// }// Este endpoint no requiere API Key, pero si la cookie de sesion:
// es la que le dice al servidor a que base de datos enrutar.
// Sirve para verificar que el modulo esta instalado y el
// servicio responde antes de enviar peticiones.
var resp = await http.GetAsync($"{URL}/api/fe/health");
Console.WriteLine(await resp.Content.ReadAsStringAsync());
// Respuesta esperada:
// {
// "success": true,
// "status": "ok",
// "module": "eterp_de_api",
// "version": "16.0.1.0.0"
// }// Este endpoint no requiere API Key, pero si la cookie de sesion:
// es la que le dice al servidor a que base de datos enrutar.
// Sirve para verificar que el modulo esta instalado y el
// servicio responde antes de enviar peticiones.
HttpResponse<String> resp = http.send(
HttpRequest.newBuilder(URI.create(URL + "/api/fe/health")).build(),
HttpResponse.BodyHandlers.ofString());
System.out.println(resp.body());
// Respuesta esperada:
// {
// "success": true,
// "status": "ok",
// "module": "eterp_de_api",
// "version": "16.0.1.0.0"
// }# Este endpoint no requiere API Key, pero si la cookie de sesion:
# es la que le dice al servidor a que base de datos enrutar.
curl -s -b cookies.txt "$URL/api/fe/health"
# Respuesta esperada:
# {"success": true, "status": "ok", "module": "eterp_de_api", "version": "16.0.1.0.0"}
Autenticación
Toda petición se autentica con la API Key que se generó al aprovisionar su base de datos. No hay que configurar nada: la base ya viene lista para recibir peticiones.
Datos de conexión
Para empezar a usar la API solo necesita tres valores:
- URL base — el subdominio de su base, por ejemplo
https://80200001.elementtech.com.py - Nombre de la base de datos — por ejemplo
80200001_test - API Key — la clave de 40 caracteres que autentica cada petición
Los tres llegan en el correo que se envía cuando la base queda lista, y quedan siempre disponibles en su panel de configuración, en la tabla de credenciales de la base.
Las dos bases comparten la misma URL base y cada una tiene su propia API Key. Lo que
distingue el entorno es el nombre de la base de datos: para
pruebas y para producción.
La API Key
La clave se genera automáticamente durante el aprovisionamiento y pertenece a un usuario de su propia base de datos, que ya tiene asignados los permisos necesarios para operar la API. No hay ningún paso previo de su parte: la clave que figura en su panel de configuración funciona desde el primer momento.
Trate la API Key como una contraseña: otorga acceso a la facturación de su empresa. Guárdela en un lugar seguro de su sistema y no la incluya en código que se publique. Si la extravía, puede volver a consultarla en su panel de configuración.
Proceso de autenticación
import requests
# Los tres valores estan en su panel de configuracion.
URL = "https://80200001.elementtech.com.py" # URL base de su instancia
API_KEY = "tu_api_key" # API Key de la base
DB = "80200001_test" # Base de datos: _test o _prod
# Paso 1: iniciar sesion HTTP. Element necesita una cookie de sesion
# vinculada a la base de datos; sin ella las rutas responden 404.
session = requests.Session()
session.get(f"{URL}/web", params={"db": DB})
# Paso 2: headers de toda peticion autenticada. El servidor valida la
# API Key contra las claves registradas y ejecuta la peticion con los
# permisos del usuario propietario de la clave.
headers = {
"Authorization": f"Bearer {API_KEY}",
"X-Database": DB,
"Content-Type": "application/json"
}// Requiere Node 18 o superior (fetch nativo). Sin dependencias.
// Los tres valores estan en su panel de configuracion.
const URL = "https://80200001.elementtech.com.py"; // URL base de su instancia
const API_KEY = "tu_api_key"; // API Key de la base
const DB = "80200001_test"; // Base de datos: _test o _prod
// Paso 1: iniciar sesion HTTP. Element necesita una cookie de sesion
// vinculada a la base de datos; sin ella las rutas responden 404.
// fetch no guarda cookies, asi que se reenvia a mano.
const inicio = await fetch(`${URL}/web?db=${DB}`, { redirect: "manual" });
const cookie = inicio.headers.get("set-cookie")?.match(/session_id=[^;]+/)?.[0] ?? "";
// Paso 2: headers de toda peticion autenticada. El servidor valida la
// API Key contra las claves registradas y ejecuta la peticion con los
// permisos del usuario propietario de la clave.
const headers = {
"Authorization": `Bearer ${API_KEY}`,
"X-Database": DB,
"Content-Type": "application/json",
"Cookie": cookie
};// Requiere .NET 8 o superior. Sin paquetes externos.
using System.Net;
using System.Text;
using System.Text.Json;
// Los tres valores estan en su panel de configuracion.
const string URL = "https://80200001.elementtech.com.py"; // URL base de su instancia
const string API_KEY = "tu_api_key"; // API Key de la base
const string DB = "80200001_test"; // Base de datos: _test o _prod
// El CookieContainer conserva la cookie de sesion entre peticiones.
var handler = new HttpClientHandler { CookieContainer = new CookieContainer() };
var http = new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(120) };
// El servidor valida la API Key contra las claves registradas y ejecuta
// la peticion con los permisos del usuario propietario de la clave.
http.DefaultRequestHeaders.Add("Authorization", $"Bearer {API_KEY}");
http.DefaultRequestHeaders.Add("X-Database", DB);
// Paso 1: iniciar sesion HTTP. Element necesita una cookie de sesion
// vinculada a la base de datos; sin ella las rutas responden 404.
await http.GetAsync($"{URL}/web?db={DB}");
// Paso 2: helper para el cuerpo JSON de las siguientes peticiones.
StringContent Json(object cuerpo) =>
new(JsonSerializer.Serialize(cuerpo), Encoding.UTF8, "application/json");// Requiere Java 17 o superior. Sin dependencias externas.
import java.net.CookieManager;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.function.Function;
// Los tres valores estan en su panel de configuracion.
String URL = "https://80200001.elementtech.com.py"; // URL base de su instancia
String API_KEY = "tu_api_key"; // API Key de la base
String DB = "80200001_test"; // Base de datos: _test o _prod
// El CookieManager conserva la cookie de sesion entre peticiones.
HttpClient http = HttpClient.newBuilder()
.cookieHandler(new CookieManager())
.connectTimeout(Duration.ofSeconds(120))
.build();
// Paso 1: iniciar sesion HTTP. Element necesita una cookie de sesion
// vinculada a la base de datos; sin ella las rutas responden 404.
http.send(HttpRequest.newBuilder(URI.create(URL + "/web?db=" + DB)).build(),
HttpResponse.BodyHandlers.discarding());
// Paso 2: constructor de peticiones ya autenticadas. El servidor valida
// la API Key contra las claves registradas y ejecuta la peticion con los
// permisos del usuario propietario de la clave.
Function<String, HttpRequest.Builder> pedido = ruta ->
HttpRequest.newBuilder(URI.create(URL + ruta))
.header("Authorization", "Bearer " + API_KEY)
.header("X-Database", DB)
.header("Content-Type", "application/json")
.timeout(Duration.ofSeconds(120));# Los tres valores estan en su panel de configuracion.
URL="https://80200001.elementtech.com.py" # URL base de su instancia
API_KEY="tu_api_key" # API Key de la base
DB="80200001_test" # Base de datos: _test o _prod
# Paso 1: iniciar sesion HTTP y guardar la cookie. Element necesita una
# cookie de sesion vinculada a la base; sin ella las rutas responden 404.
curl -s -c cookies.txt -o /dev/null "$URL/web?db=$DB"
# Paso 2: a partir de aqui cada peticion reenvia la cookie con -b y
# los headers de autenticacion.
Primera factura
POST /api/fe/invoice
Campos del request
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
partner_id | string | Sí | ID externo del cliente |
api_ruc | string | No | RUC para consulta SIFEN |
api_partner_data | object | No | Datos del cliente |
currency | string | No | Código de moneda. Por defecto PYG |
payment_term_id | int | No | ID del término de pago |
journal_id | int | No | ID de un diario específico |
tipo_operacion | string | No | Tipo de operación DE, valores 1 a 4 |
lines | array | Sí | Líneas de la factura |
Campos de api_partner_data
El objeto api_partner_data contiene los datos del cliente. El campo name
siempre es requerido, junto con uno de ruc, ci o tipo_documento.
El resto de los campos son opcionales.
Identificación del cliente
| Campo | Tipo | Req. | Descripción |
|---|---|---|---|
name | string | Sí | Nombre o razón social |
ruc | string | Cond. | RUC del contribuyente, con o sin dígito verificador |
ci | string | Cond. | Número de cédula de identidad |
tipo_documento | string | Cond. | Código del tipo de documento: 1 cédula paraguaya, 2 pasaporte, 3 cédula extranjera, 4 carnet de residencia, 5 innominado, 6 tarjeta diplomática, 9 otro |
documento_numero | string | Cond. | Número del documento cuando tipo_documento no es 1. Máximo 20 caracteres |
tipo_documento_descripcion | string | Cond. | Descripción del documento cuando tipo_documento es 9. Mínimo 9 y máximo 41 caracteres |
company_type | int/string | No | 1 o "person" para personas físicas, 2 o "company" para empresas |
nf | string | No | Nombre de fantasía |
Enviar ruc marca al cliente como contribuyente; enviar ci lo marca como
persona física con cédula paraguaya. Para un cliente contribuyente, company_type
determina si el XML lo informa como persona física o jurídica.
Contacto y dirección
| Campo | Tipo | Req. | Descripción |
|---|---|---|---|
email | string | No | Correo electrónico del cliente |
phone | string | No | Teléfono. De 6 a 15 caracteres; si el cliente es de Paraguay, con el prefijo de la ciudad en vez del código de país, por ejemplo 021445566 |
mobile | string | No | Celular |
street | string | No | Calle y número |
numero_casa | string | No | Número de casa. Máximo 6 caracteres |
departamento_id | int/string | No | Departamento: ID o nombre |
distrito_id | int/string | No | Distrito: ID o nombre |
ciudad_id | int/string | No | Ciudad: ID o nombre |
barrio_id | int/string | No | Barrio: ID o nombre |
Los cuatro campos geográficos aceptan tanto el ID numérico como el nombre de la ubicación en
texto, y se resuelven siguiendo la jerarquía departamento → distrito → ciudad → barrio. Como
existen nombres de distrito y de ciudad repetidos entre departamentos distintos, conviene enviar
siempre la jerarquía completa: el distrito se busca dentro del departamento indicado, y la ciudad
dentro del distrito. Si un nombre resulta ambiguo, la API devuelve 400 INVALID_FIELD
con la lista de IDs que coinciden.
Para que la dirección del cliente se incluya en el XML del DE se deben enviar los cinco campos
street, numero_casa, departamento_id,
distrito_id y ciudad_id. Si falta alguno de ellos, el documento se
genera igual pero sin el bloque de dirección, y no se devuelve ningún error.
El campo barrio_id es opcional y se incluye en el XML cuando se envía.
El campo email se incluye en el XML del DE y es el destinatario del envío automático
del documento por correo electrónico. Para configurar copias por tipo de documento existe el campo
email_cc, descrito en el paso 7, Clientes y KUDE PDF.
Campo tipo_operacion
El campo tipo_operacion define el tipo de operación del Documento Electrónico según
la normativa DNIT. Se serializa al XML del DE como cliente.tipoOperacion.
| Valor | Código | Descripción |
|---|---|---|
1 | B2B | Empresa a Empresa |
2 | B2C | Empresa a Consumidor Final. Valor por defecto |
3 | B2G | Empresa a Entidad del Gobierno |
4 | B2F | Empresa a Empresa o Persona del Exterior |
Si el campo se omite, se usa el valor 2 (B2C). En la interfaz de Element ERP el valor
se auto-ajusta al cambiar el cliente (empresa → B2B, persona → B2C), pero en la creación vía API
ese ajuste automático no ocurre, por lo que el cliente externo debe enviar el valor explícitamente
cuando necesite B2B, B2G o B2F. Valores fuera de 1–4 devuelven
400 INVALID_FIELD.
Campos de línea
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
product_id | string/int | Sí | ID externo o ID Element |
api_product_data | object | No | Datos para crear el producto |
name | string | No | Descripción personalizada |
quantity | number | Sí | Cantidad |
price_unit | number | Sí | Precio unitario |
tax_ids | array | No | IDs de impuestos, sobrescriben los del producto |
discount | number | No | Porcentaje de descuento, de 0 a 100 |
discount_amount | number | No | Monto fijo de descuento por unidad |
Los campos discount y discount_amount son mutuamente excluyentes.
discount es un porcentaje de 0 a 100; discount_amount es un monto fijo
de descuento por unidad, en la misma denominación que price_unit. Enviar ambos en la
misma línea devuelve 400 INVALID_FIELD.
Campos de api_product_data
Cuando se utiliza api_product_data para crear un producto nuevo automáticamente, se
deben proporcionar los siguientes campos obligatorios:
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
name | string | Sí | Nombre del producto |
default_code | string | Sí | Código interno de referencia |
uom_id | int | Sí | ID de la unidad de medida en Element |
tax_id | int | Sí | ID del impuesto en Element |
Qué es uom_id y por qué es obligatorio
El campo uom_id corresponde al identificador de la unidad de medida
del producto en Element ERP. Toda factura electrónica en Paraguay requiere especificar la unidad
de medida del bien o servicio facturado (por ejemplo: unidad, kilogramo, litro, hora, metro), ya
que este dato se incluye en el XML del Documento Electrónico enviado a DNIT.
En Element ERP, las unidades de medida se gestionan en el modelo uom.uom y cada una
tiene un ID numérico único. Al crear un producto vía la API, el sistema necesita saber a qué
unidad de medida asociarlo. Por eso uom_id es un campo obligatorio dentro de
api_product_data: el sistema valida que el ID proporcionado exista en la base de
datos antes de crear el producto.
Para consultar las unidades de medida disponibles en su instancia, navegue a Inventario → Configuración → Unidades de Medida.
| ID típico | Nombre | Uso común |
|---|---|---|
| 1 | Unidad(es) | Productos generales, servicios |
| 2 | Docena(s) | Productos vendidos por docena |
| 3 | kg | Productos pesados |
| 4 | Litro(s) | Líquidos |
Los IDs de unidades de medida pueden variar entre instalaciones de Element ERP. El valor
1 (Unidad) es el más común, pero siempre verifique los IDs
disponibles en su instancia antes de integrar. Si el ID proporcionado no existe, la API
retornará un error de validación.
Ejemplo: factura al contado
resp = session.post(
f"{URL}/api/fe/invoice",
headers=headers,
json={
# --- Identificacion del cliente ---
# "partner_id" es el ID externo del cliente en su sistema.
# Se usa como clave para buscar o crear el partner en Element.
# Si el cliente ya fue creado con este mismo partner_id,
# el sistema lo reutiliza automaticamente.
"partner_id": "48213",
# "api_ruc" activa la consulta automatica al servicio SIFEN.
# El sistema consulta el RUC, obtiene nombre, tipo de
# contribuyente y estado, y crea el partner con esos datos.
# Formato: "numero-dv".
"api_ruc": "80000001-1",
# --- Lineas de la factura ---
# "api_product_data" es OBLIGATORIO en cada linea.
"lines": [{
# ID externo del producto. Si ya existe uno con el mismo
# "default_code", se reutiliza; si no, se crea.
"product_id": "20458",
"api_product_data": {
"name": "Servicio de Consultoria",
"default_code": "SRV-2045", # Codigo interno (unico)
"uom_id": 1, # ID de unidad de medida en Element
"tax_id": 1 # ID de impuesto en Element (ej: IVA 10%)
},
"quantity": 1,
"price_unit": 150000 # Precio en la moneda de la factura
}],
# tipo_operacion: "1"=B2B, "2"=B2C (por omision), "3"=B2G, "4"=B2F.
# El onchange de la interfaz no se dispara al crear por API, asi
# que para B2B u otros tipos hay que enviarlo explicitamente.
"tipo_operacion": "1"
}
)
# La respuesta incluye id, numero, estado, montos y los datos del
# documento electronico (DE_estado, DE_id, etc.)
print(resp.json())const resp = await fetch(`${URL}/api/fe/invoice`, {
method: "POST",
headers,
body: JSON.stringify({
// --- Identificacion del cliente ---
// "partner_id" es el ID externo del cliente en su sistema.
// Se usa como clave para buscar o crear el partner en Element.
// Si el cliente ya fue creado con este mismo partner_id,
// el sistema lo reutiliza automaticamente.
partner_id: "48213",
// "api_ruc" activa la consulta automatica al servicio SIFEN.
// El sistema consulta el RUC, obtiene nombre, tipo de
// contribuyente y estado, y crea el partner con esos datos.
// Formato: "numero-dv".
api_ruc: "80000001-1",
// --- Lineas de la factura ---
// "api_product_data" es OBLIGATORIO en cada linea.
lines: [{
// ID externo del producto. Si ya existe uno con el mismo
// "default_code", se reutiliza; si no, se crea.
product_id: "20458",
api_product_data: {
name: "Servicio de Consultoria",
default_code: "SRV-2045", // Codigo interno (unico)
uom_id: 1, // ID de unidad de medida en Element
tax_id: 1 // ID de impuesto en Element (ej: IVA 10%)
},
quantity: 1,
price_unit: 150000 // Precio en la moneda de la factura
}],
// tipo_operacion: "1"=B2B, "2"=B2C (por omision), "3"=B2G, "4"=B2F.
// El onchange de la interfaz no se dispara al crear por API, asi
// que para B2B u otros tipos hay que enviarlo explicitamente.
tipo_operacion: "1"
})
});
// La respuesta incluye id, numero, estado, montos y los datos del
// documento electronico (DE_estado, DE_id, etc.)
console.log(await resp.json());var resp = await http.PostAsync($"{URL}/api/fe/invoice", Json(new
{
// --- Identificacion del cliente ---
// "partner_id" es el ID externo del cliente en su sistema.
// Se usa como clave para buscar o crear el partner en Element.
// Si el cliente ya fue creado con este mismo partner_id,
// el sistema lo reutiliza automaticamente.
partner_id = "48213",
// "api_ruc" activa la consulta automatica al servicio SIFEN.
// El sistema consulta el RUC, obtiene nombre, tipo de
// contribuyente y estado, y crea el partner con esos datos.
// Formato: "numero-dv".
api_ruc = "80000001-1",
// --- Lineas de la factura ---
// "api_product_data" es OBLIGATORIO en cada linea.
lines = new[]
{
new
{
// ID externo del producto. Si ya existe uno con el mismo
// "default_code", se reutiliza; si no, se crea.
product_id = "20458",
api_product_data = new
{
name = "Servicio de Consultoria",
default_code = "SRV-2045", // Codigo interno (unico)
uom_id = 1, // ID de unidad de medida en Element
tax_id = 1 // ID de impuesto en Element (ej: IVA 10%)
},
quantity = 1,
price_unit = 150000 // Precio en la moneda de la factura
}
},
// tipo_operacion: "1"=B2B, "2"=B2C (por omision), "3"=B2G, "4"=B2F.
// El onchange de la interfaz no se dispara al crear por API, asi
// que para B2B u otros tipos hay que enviarlo explicitamente.
tipo_operacion = "1"
}));
// La respuesta incluye id, numero, estado, montos y los datos del
// documento electronico (DE_estado, DE_id, etc.)
Console.WriteLine(await resp.Content.ReadAsStringAsync());// Sin dependencias externas el cuerpo se arma como texto. Con Jackson
// o Gson puede serializar un objeto en su lugar.
// partner_id ID externo del cliente en su sistema. Si ya existe un
// cliente con ese partner_id, el sistema lo reutiliza.
// api_ruc activa la consulta automatica al servicio SIFEN: trae
// nombre, tipo de contribuyente y estado. Formato "numero-dv".
// lines api_product_data es OBLIGATORIO en cada linea. Si ya existe
// un producto con ese default_code, se reutiliza.
// tipo_operacion "1"=B2B, "2"=B2C (por omision), "3"=B2G, "4"=B2F. El
// onchange de la interfaz no se dispara al crear por API.
String factura = """
{
"partner_id": "48213",
"api_ruc": "80000001-1",
"lines": [{
"product_id": "20458",
"api_product_data": {
"name": "Servicio de Consultoria",
"default_code": "SRV-2045",
"uom_id": 1,
"tax_id": 1
},
"quantity": 1,
"price_unit": 150000
}],
"tipo_operacion": "1"
}
""";
HttpResponse<String> resp = http.send(
pedido.apply("/api/fe/invoice")
.POST(HttpRequest.BodyPublishers.ofString(factura))
.build(),
HttpResponse.BodyHandlers.ofString());
// La respuesta incluye id, numero, estado, montos y los datos del
// documento electronico (DE_estado, DE_id, etc.)
System.out.println(resp.body());# partner_id ID externo del cliente en su sistema. Si ya existe un
# cliente con ese partner_id, el sistema lo reutiliza.
# api_ruc activa la consulta automatica al servicio SIFEN: trae
# nombre, tipo de contribuyente y estado. Formato "numero-dv".
# lines api_product_data es OBLIGATORIO en cada linea. Si ya existe
# un producto con ese default_code, se reutiliza.
# tipo_operacion "1"=B2B, "2"=B2C (por omision), "3"=B2G, "4"=B2F. El
# onchange de la interfaz no se dispara al crear por API.
curl -s -b cookies.txt -X POST "$URL/api/fe/invoice" \
-H "Authorization: Bearer $API_KEY" \
-H "X-Database: $DB" \
-H "Content-Type: application/json" \
-d '{
"partner_id": "48213",
"api_ruc": "80000001-1",
"lines": [{
"product_id": "20458",
"api_product_data": {
"name": "Servicio de Consultoria",
"default_code": "SRV-2045",
"uom_id": 1,
"tax_id": 1
},
"quantity": 1,
"price_unit": 150000
}],
"tipo_operacion": "1"
}'
Ejemplo: factura a crédito
resp = session.post(
f"{URL}/api/fe/invoice",
headers=headers,
json={
# --- Cliente con datos manuales (sin consulta SIFEN) ---
# En lugar de api_ruc se usa api_partner_data. El sistema busca
# primero si ya existe un partner con ese RUC; si lo encuentra,
# lo reutiliza sin crear uno nuevo.
"partner_id": "51742",
"api_partner_data": {
"name": "Empresa Cliente SA",
"ruc": "80012345-6", # RUC con digito verificador
# Al incluir "ruc" el cliente queda marcado como contribuyente.
# "company_type" define si el XML lo informa como persona
# fisica (1) o juridica (2).
"company_type": 2,
# Correo del cliente. Va en el XML del DE y es el destinatario
# del envio automatico del documento.
"email": "[email protected]",
"phone": "021445566",
# Direccion del cliente. Los cinco campos siguientes deben
# enviarse juntos para que la direccion aparezca en el XML.
"street": "Avda. Mariscal Lopez 1234",
"numero_casa": "1234",
# Los campos geograficos aceptan ID numerico o nombre.
"departamento_id": "Central",
"distrito_id": "Luque",
"ciudad_id": "Luque",
"barrio_id": "Centro" # Opcional
},
# Termino de pago: ID del registro payment.term en Element.
# Si el termino tiene dias > 0 la factura es a credito y el
# sistema NO registra pago automatico.
"payment_term_id": 2,
"lines": [{
# Cuando product_id es un entero se interpreta como el ID
# interno de Element y el producto debe existir.
"product_id": 1,
"quantity": 2,
"price_unit": 250000
}]
}
)
print(resp.json())const resp = await fetch(`${URL}/api/fe/invoice`, {
method: "POST",
headers,
body: JSON.stringify({
// --- Cliente con datos manuales (sin consulta SIFEN) ---
// En lugar de api_ruc se usa api_partner_data. El sistema busca
// primero si ya existe un partner con ese RUC; si lo encuentra,
// lo reutiliza sin crear uno nuevo.
partner_id: "51742",
api_partner_data: {
name: "Empresa Cliente SA",
ruc: "80012345-6", // RUC con digito verificador
// Al incluir "ruc" el cliente queda marcado como contribuyente.
// "company_type" define si el XML lo informa como persona
// fisica (1) o juridica (2).
company_type: 2,
// Correo del cliente. Va en el XML del DE y es el destinatario
// del envio automatico del documento.
email: "[email protected]",
phone: "021445566",
// Direccion del cliente. Los cinco campos siguientes deben
// enviarse juntos para que la direccion aparezca en el XML.
street: "Avda. Mariscal Lopez 1234",
numero_casa: "1234",
// Los campos geograficos aceptan ID numerico o nombre.
departamento_id: "Central",
distrito_id: "Luque",
ciudad_id: "Luque",
barrio_id: "Centro" // Opcional
},
// Termino de pago: ID del registro payment.term en Element.
// Si el termino tiene dias > 0 la factura es a credito y el
// sistema NO registra pago automatico.
payment_term_id: 2,
lines: [{
// Cuando product_id es un entero se interpreta como el ID
// interno de Element y el producto debe existir.
product_id: 1,
quantity: 2,
price_unit: 250000
}]
})
});
console.log(await resp.json());var resp = await http.PostAsync($"{URL}/api/fe/invoice", Json(new
{
// --- Cliente con datos manuales (sin consulta SIFEN) ---
// En lugar de api_ruc se usa api_partner_data. El sistema busca
// primero si ya existe un partner con ese RUC; si lo encuentra,
// lo reutiliza sin crear uno nuevo.
partner_id = "51742",
api_partner_data = new
{
name = "Empresa Cliente SA",
ruc = "80012345-6", // RUC con digito verificador
// Al incluir "ruc" el cliente queda marcado como contribuyente.
// "company_type" define si el XML lo informa como persona
// fisica (1) o juridica (2).
company_type = 2,
// Correo del cliente. Va en el XML del DE y es el destinatario
// del envio automatico del documento.
email = "[email protected]",
phone = "021445566",
// Direccion del cliente. Los cinco campos siguientes deben
// enviarse juntos para que la direccion aparezca en el XML.
street = "Avda. Mariscal Lopez 1234",
numero_casa = "1234",
// Los campos geograficos aceptan ID numerico o nombre.
departamento_id = "Central",
distrito_id = "Luque",
ciudad_id = "Luque",
barrio_id = "Centro" // Opcional
},
// Termino de pago: ID del registro payment.term en Element.
// Si el termino tiene dias > 0 la factura es a credito y el
// sistema NO registra pago automatico.
payment_term_id = 2,
lines = new[]
{
new
{
// Cuando product_id es un entero se interpreta como el ID
// interno de Element y el producto debe existir.
product_id = 1,
quantity = 2,
price_unit = 250000
}
}
}));
Console.WriteLine(await resp.Content.ReadAsStringAsync());// api_partner_data reemplaza a api_ruc: los datos del cliente van
// explicitos y no se consulta SIFEN. Si ya existe un partner con ese
// RUC, se reutiliza. Los campos geograficos aceptan ID o nombre, y
// payment_term_id con dias > 0 marca la factura como a credito.
String factura = """
{
"partner_id": "51742",
"api_partner_data": {
"name": "Empresa Cliente SA",
"ruc": "80012345-6",
"company_type": 2,
"email": "[email protected]",
"phone": "021445566",
"street": "Avda. Mariscal Lopez 1234",
"numero_casa": "1234",
"departamento_id": "Central",
"distrito_id": "Luque",
"ciudad_id": "Luque",
"barrio_id": "Centro"
},
"payment_term_id": 2,
"lines": [{
"product_id": 1,
"quantity": 2,
"price_unit": 250000
}]
}
""";
HttpResponse<String> resp = http.send(
pedido.apply("/api/fe/invoice")
.POST(HttpRequest.BodyPublishers.ofString(factura))
.build(),
HttpResponse.BodyHandlers.ofString());
System.out.println(resp.body());# api_partner_data reemplaza a api_ruc: los datos del cliente van
# explicitos y no se consulta SIFEN. Si ya existe un partner con ese
# RUC, se reutiliza. Los campos geograficos aceptan ID o nombre, y
# payment_term_id con dias > 0 marca la factura como a credito.
curl -s -b cookies.txt -X POST "$URL/api/fe/invoice" \
-H "Authorization: Bearer $API_KEY" \
-H "X-Database: $DB" \
-H "Content-Type: application/json" \
-d '{
"partner_id": "51742",
"api_partner_data": {
"name": "Empresa Cliente SA",
"ruc": "80012345-6",
"company_type": 2,
"email": "[email protected]",
"phone": "021445566",
"street": "Avda. Mariscal Lopez 1234",
"numero_casa": "1234",
"departamento_id": "Central",
"distrito_id": "Luque",
"ciudad_id": "Luque",
"barrio_id": "Centro"
},
"payment_term_id": 2,
"lines": [{
"product_id": 1,
"quantity": 2,
"price_unit": 250000
}]
}'
Creación de clientes
Tres formas de especificar el cliente:
- api_ruc: consulta automática en SIFEN por RUC
- api_partner_data con ruc: datos manuales con RUC
- api_partner_data con ci: datos manuales con cédula
Para dar de alta o actualizar un cliente sin emitir un documento, ver el paso 7, Clientes y KUDE PDF.
# Para personas fisicas (no contribuyentes) se usa "ci" en lugar de
# "ruc". El partner se crea con contribuyente=False,
# company_type="person" y el tipo_documento indicado.
resp = session.post(
f"{URL}/api/fe/invoice",
headers=headers,
json={
"partner_id": "63189",
"api_partner_data": {
"name": "Juan Perez",
"ci": "1234567", # Numero de cedula de identidad
"tipo_documento": "1", # Codigo SIFEN del tipo de documento:
# "1" = Cedula de identidad paraguaya
# "2" = Pasaporte
# "3" = Cedula de identidad extranjera
# "4" = Carnet de residencia
# "5" = Innominado
# "6" = Tarjeta diplomatica de exoneracion
# "9" = Otro (requiere
# tipo_documento_descripcion)
"email": "[email protected]"
},
"lines": [{
"product_id": "20458",
"api_product_data": {
"name": "Servicio de Consultoria",
"default_code": "SRV-2045",
"uom_id": 1,
"tax_id": 1
},
"quantity": 1,
"price_unit": 90000
}]
}
)
print(resp.json())// Para personas fisicas (no contribuyentes) se usa "ci" en lugar de
// "ruc". El partner se crea con contribuyente=false,
// company_type="person" y el tipo_documento indicado.
const resp = await fetch(`${URL}/api/fe/invoice`, {
method: "POST",
headers,
body: JSON.stringify({
partner_id: "63189",
api_partner_data: {
name: "Juan Perez",
ci: "1234567", // Numero de cedula de identidad
tipo_documento: "1", // Codigo SIFEN del tipo de documento:
// "1" = Cedula de identidad paraguaya
// "2" = Pasaporte
// "3" = Cedula de identidad extranjera
// "4" = Carnet de residencia
// "5" = Innominado
// "6" = Tarjeta diplomatica de exoneracion
// "9" = Otro (requiere
// tipo_documento_descripcion)
email: "[email protected]"
},
lines: [{
product_id: "20458",
api_product_data: {
name: "Servicio de Consultoria",
default_code: "SRV-2045",
uom_id: 1,
tax_id: 1
},
quantity: 1,
price_unit: 90000
}]
})
});
console.log(await resp.json());// Para personas fisicas (no contribuyentes) se usa "ci" en lugar de
// "ruc". El partner se crea con contribuyente=false,
// company_type="person" y el tipo_documento indicado.
var resp = await http.PostAsync($"{URL}/api/fe/invoice", Json(new
{
partner_id = "63189",
api_partner_data = new
{
name = "Juan Perez",
ci = "1234567", // Numero de cedula de identidad
tipo_documento = "1", // Codigo SIFEN del tipo de documento:
// "1" = Cedula de identidad paraguaya
// "2" = Pasaporte
// "3" = Cedula de identidad extranjera
// "4" = Carnet de residencia
// "5" = Innominado
// "6" = Tarjeta diplomatica de exoneracion
// "9" = Otro (requiere
// tipo_documento_descripcion)
email = "[email protected]"
},
lines = new[]
{
new
{
product_id = "20458",
api_product_data = new
{
name = "Servicio de Consultoria",
default_code = "SRV-2045",
uom_id = 1,
tax_id = 1
},
quantity = 1,
price_unit = 90000
}
}
}));
Console.WriteLine(await resp.Content.ReadAsStringAsync());// Para personas fisicas (no contribuyentes) se usa "ci" en lugar de
// "ruc". El partner se crea con contribuyente=false,
// company_type="person" y el tipo_documento indicado.
// Codigos SIFEN de tipo_documento:
// "1" Cedula de identidad paraguaya "2" Pasaporte
// "3" Cedula de identidad extranjera "4" Carnet de residencia
// "5" Innominado "6" Tarjeta diplomatica de exoneracion
// "9" Otro (requiere tipo_documento_descripcion)
String factura = """
{
"partner_id": "63189",
"api_partner_data": {
"name": "Juan Perez",
"ci": "1234567",
"tipo_documento": "1",
"email": "[email protected]"
},
"lines": [{
"product_id": "20458",
"api_product_data": {
"name": "Servicio de Consultoria",
"default_code": "SRV-2045",
"uom_id": 1,
"tax_id": 1
},
"quantity": 1,
"price_unit": 90000
}]
}
""";
HttpResponse<String> resp = http.send(
pedido.apply("/api/fe/invoice")
.POST(HttpRequest.BodyPublishers.ofString(factura))
.build(),
HttpResponse.BodyHandlers.ofString());
System.out.println(resp.body());# Para personas fisicas (no contribuyentes) se usa "ci" en lugar de
# "ruc". El partner se crea con contribuyente=false,
# company_type="person" y el tipo_documento indicado.
# Codigos SIFEN de tipo_documento:
# "1" Cedula de identidad paraguaya "2" Pasaporte
# "3" Cedula de identidad extranjera "4" Carnet de residencia
# "5" Innominado "6" Tarjeta diplomatica de exoneracion
# "9" Otro (requiere tipo_documento_descripcion)
curl -s -b cookies.txt -X POST "$URL/api/fe/invoice" \
-H "Authorization: Bearer $API_KEY" \
-H "X-Database: $DB" \
-H "Content-Type: application/json" \
-d '{
"partner_id": "63189",
"api_partner_data": {
"name": "Juan Perez",
"ci": "1234567",
"tipo_documento": "1",
"email": "[email protected]"
},
"lines": [{
"product_id": "20458",
"api_product_data": {
"name": "Servicio de Consultoria",
"default_code": "SRV-2045",
"uom_id": 1,
"tax_id": 1
},
"quantity": 1,
"price_unit": 90000
}]
}'
Notas de crédito
POST /api/fe/credit-note
Campos del request
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
original_invoice_number | string | No | Número de la factura original |
original_invoice_id | int | No | ID de la factura original |
motivo | string | No | Código de motivo, valores 1 a 8 |
partner_id | string | Cond. | Si no hay factura original |
lines | array | Cond. | Si no hay factura original |
tipo_operacion | string | No | Tipo de operación DE, valores 1 a 4 |
amount_factor | float | No | Factor de monto. Por defecto 1.0 |
Motivos disponibles
| Código | Descripción |
|---|---|
1 | Devolución y ajuste de precios |
2 | Devolución |
3 | Descuento |
4 | Bonificación |
5 | Crédito incobrable |
6 | Recupero de costo |
7 | Recupero de gasto |
8 | Ajuste de precio |
Ejemplo
resp = session.post(
f"{URL}/api/fe/credit-note",
headers=headers,
json={
# Numero de la factura original a la que aplica la NC. El sistema
# la busca y vincula la NC mediante "reversed_entry_id".
# Alternativa: "original_invoice_id" con el ID numerico de Element.
"original_invoice_number": "001-001-0004661",
# Codigo de motivo segun normativa SIFEN. Se almacena en
# "motivo_nota_credito_debito". Ver la tabla de motivos mas arriba.
"motivo": "1", # 1 = Devolucion y Ajuste de precios
# Lineas propias de la NC. Si se omite "lines" el sistema copia
# las lineas de la factura original; con "amount_factor" (ej: 0.5)
# se genera una NC parcial.
"lines": [{
"product_id": "20458",
"api_product_data": {
"name": "Servicio a devolver",
"default_code": "SRV-2045",
"uom_id": 1,
"tax_id": 1
},
"quantity": 1,
"price_unit": 100000
}]
}
)
print(resp.json())const resp = await fetch(`${URL}/api/fe/credit-note`, {
method: "POST",
headers,
body: JSON.stringify({
// Numero de la factura original a la que aplica la NC. El sistema
// la busca y vincula la NC mediante "reversed_entry_id".
// Alternativa: "original_invoice_id" con el ID numerico de Element.
original_invoice_number: "001-001-0004661",
// Codigo de motivo segun normativa SIFEN. Se almacena en
// "motivo_nota_credito_debito". Ver la tabla de motivos mas arriba.
motivo: "1", // 1 = Devolucion y Ajuste de precios
// Lineas propias de la NC. Si se omite "lines" el sistema copia
// las lineas de la factura original; con "amount_factor" (ej: 0.5)
// se genera una NC parcial.
lines: [{
product_id: "20458",
api_product_data: {
name: "Servicio a devolver",
default_code: "SRV-2045",
uom_id: 1,
tax_id: 1
},
quantity: 1,
price_unit: 100000
}]
})
});
console.log(await resp.json());var resp = await http.PostAsync($"{URL}/api/fe/credit-note", Json(new
{
// Numero de la factura original a la que aplica la NC. El sistema
// la busca y vincula la NC mediante "reversed_entry_id".
// Alternativa: "original_invoice_id" con el ID numerico de Element.
original_invoice_number = "001-001-0004661",
// Codigo de motivo segun normativa SIFEN. Se almacena en
// "motivo_nota_credito_debito". Ver la tabla de motivos mas arriba.
motivo = "1", // 1 = Devolucion y Ajuste de precios
// Lineas propias de la NC. Si se omite "lines" el sistema copia
// las lineas de la factura original; con "amount_factor" (ej: 0.5)
// se genera una NC parcial.
lines = new[]
{
new
{
product_id = "20458",
api_product_data = new
{
name = "Servicio a devolver",
default_code = "SRV-2045",
uom_id = 1,
tax_id = 1
},
quantity = 1,
price_unit = 100000
}
}
}));
Console.WriteLine(await resp.Content.ReadAsStringAsync());// original_invoice_number identifica la factura a la que aplica la NC; el
// sistema la vincula mediante reversed_entry_id. Alternativa:
// original_invoice_id con el ID numerico de Element.
// motivo es el codigo SIFEN (1 = Devolucion y Ajuste de precios).
// Si se omite "lines" se copian las lineas de la factura original; con
// amount_factor (ej: 0.5) se genera una NC parcial.
String notaCredito = """
{
"original_invoice_number": "001-001-0004661",
"motivo": "1",
"lines": [{
"product_id": "20458",
"api_product_data": {
"name": "Servicio a devolver",
"default_code": "SRV-2045",
"uom_id": 1,
"tax_id": 1
},
"quantity": 1,
"price_unit": 100000
}]
}
""";
HttpResponse<String> resp = http.send(
pedido.apply("/api/fe/credit-note")
.POST(HttpRequest.BodyPublishers.ofString(notaCredito))
.build(),
HttpResponse.BodyHandlers.ofString());
System.out.println(resp.body());# original_invoice_number identifica la factura a la que aplica la NC; el
# sistema la vincula mediante reversed_entry_id. Alternativa:
# original_invoice_id con el ID numerico de Element.
# motivo es el codigo SIFEN (1 = Devolucion y Ajuste de precios).
# Si se omite "lines" se copian las lineas de la factura original; con
# amount_factor (ej: 0.5) se genera una NC parcial.
curl -s -b cookies.txt -X POST "$URL/api/fe/credit-note" \
-H "Authorization: Bearer $API_KEY" \
-H "X-Database: $DB" \
-H "Content-Type: application/json" \
-d '{
"original_invoice_number": "001-001-0004661",
"motivo": "1",
"lines": [{
"product_id": "20458",
"api_product_data": {
"name": "Servicio a devolver",
"default_code": "SRV-2045",
"uom_id": 1,
"tax_id": 1
},
"quantity": 1,
"price_unit": 100000
}]
}'
Consultar, cancelar y cobrar
Operaciones sobre documentos ya emitidos: buscarlos por número, cancelarlos ante SIFEN y registrar su pago.
Buscar factura
GET /api/fe/invoice/{numero}?move_type={tipo}
Parámetro de query opcional
| Valor | Descripción |
|---|---|
invoice | Busca solo facturas |
credit_note | Busca solo notas de crédito |
| Omitido | Busca en ambos tipos |
Códigos de respuesta
| HTTP | Código | Descripción |
|---|---|---|
| 200 | — | Documento encontrado |
| 404 | NOT_FOUND | No existe documento con ese número |
| 409 | AMBIGUOUS_NUMBER | El número corresponde a una factura y a una nota de crédito; usar ?move_type= para especificar. La respuesta incluye all_matches. |
resp = session.get(
f"{URL}/api/fe/invoice/001-001-0004707",
headers=headers
)
# Con filtro de tipo, cuando el numero es ambiguo entre factura y NC:
resp = session.get(
f"{URL}/api/fe/invoice/001-001-0004707?move_type=invoice",
headers=headers
)
print(resp.json())let resp = await fetch(`${URL}/api/fe/invoice/001-001-0004707`, { headers });
// Con filtro de tipo, cuando el numero es ambiguo entre factura y NC:
resp = await fetch(`${URL}/api/fe/invoice/001-001-0004707?move_type=invoice`, { headers });
console.log(await resp.json());var resp = await http.GetAsync($"{URL}/api/fe/invoice/001-001-0004707");
// Con filtro de tipo, cuando el numero es ambiguo entre factura y NC:
resp = await http.GetAsync($"{URL}/api/fe/invoice/001-001-0004707?move_type=invoice");
Console.WriteLine(await resp.Content.ReadAsStringAsync());HttpResponse<String> resp = http.send(
pedido.apply("/api/fe/invoice/001-001-0004707").GET().build(),
HttpResponse.BodyHandlers.ofString());
// Con filtro de tipo, cuando el numero es ambiguo entre factura y NC:
resp = http.send(
pedido.apply("/api/fe/invoice/001-001-0004707?move_type=invoice").GET().build(),
HttpResponse.BodyHandlers.ofString());
System.out.println(resp.body());curl -s -b cookies.txt "$URL/api/fe/invoice/001-001-0004707" \
-H "Authorization: Bearer $API_KEY" \
-H "X-Database: $DB"
# Con filtro de tipo, cuando el numero es ambiguo entre factura y NC:
curl -s -b cookies.txt "$URL/api/fe/invoice/001-001-0004707?move_type=invoice" \
-H "Authorization: Bearer $API_KEY" \
-H "X-Database: $DB"
Cancelar factura
POST /api/fe/invoice/{numero}/cancel
La cancelación es irreversible. Solo se admiten documentos con
DE_estado = 'aprobado'.
Campos del request
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
motivo | string | Sí | Motivo de cancelación, de 5 a 500 caracteres. Queda registrado permanentemente en DNIT. |
move_type | string | No | invoice o credit_note. Requerido si el número es ambiguo. |
resp = session.post(
f"{URL}/api/fe/invoice/001-001-0004707/cancel",
headers=headers,
json={
"motivo": "Solicitud de anulacion del cliente",
# "move_type": "invoice" # incluir si el numero es ambiguo
}
)
print(resp.json())const resp = await fetch(`${URL}/api/fe/invoice/001-001-0004707/cancel`, {
method: "POST",
headers,
body: JSON.stringify({
motivo: "Solicitud de anulacion del cliente"
// move_type: "invoice" // incluir si el numero es ambiguo
})
});
console.log(await resp.json());var resp = await http.PostAsync($"{URL}/api/fe/invoice/001-001-0004707/cancel", Json(new
{
motivo = "Solicitud de anulacion del cliente"
// move_type = "invoice" // incluir si el numero es ambiguo
}));
Console.WriteLine(await resp.Content.ReadAsStringAsync());// move_type ("invoice" o "credit_note") solo hace falta si el numero es
// ambiguo entre una factura y una nota de credito.
String cancelacion = """
{"motivo": "Solicitud de anulacion del cliente"}
""";
HttpResponse<String> resp = http.send(
pedido.apply("/api/fe/invoice/001-001-0004707/cancel")
.POST(HttpRequest.BodyPublishers.ofString(cancelacion))
.build(),
HttpResponse.BodyHandlers.ofString());
System.out.println(resp.body());# move_type ("invoice" o "credit_note") solo hace falta si el numero es
# ambiguo entre una factura y una nota de credito.
curl -s -b cookies.txt -X POST "$URL/api/fe/invoice/001-001-0004707/cancel" \
-H "Authorization: Bearer $API_KEY" \
-H "X-Database: $DB" \
-H "Content-Type: application/json" \
-d '{"motivo": "Solicitud de anulacion del cliente"}'
Registrar pago
POST /api/fe/invoice/{id}/payment
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
journal_type | string | No | cash o bank. Por defecto cash |
journal_id | int | No | ID específico de diario |
amount | number | No | Monto a pagar. Por defecto, el total pendiente |
# Registra un pago para una factura ya publicada (posted). Usa el wizard
# "account.payment.register" de Element. La factura debe estar en
# state="posted" y no estar pagada, si no devuelve INVALID_STATE
# o ALREADY_PAID.
resp = session.post(
# El {id} de la URL es el ID numerico de la factura en Element
# (campo "id" que devuelve crearla o buscarla), no su numero.
f"{URL}/api/fe/invoice/123/payment",
headers=headers,
json={
# Tipo de diario de pago: "cash" (caja, por omision) o "bank"
# (transferencia). Con "payment_journal_id" se indica un diario
# concreto en lugar del tipo.
"journal_type": "cash",
# Monto del pago. Si se omite se cobra el total pendiente
# (amount_residual). Un monto menor genera un pago parcial.
"amount": 50000
}
)
# Respuesta: payment_id, payment_name, invoice_payment_state
print(resp.json())// Registra un pago para una factura ya publicada (posted). Usa el wizard
// "account.payment.register" de Element. La factura debe estar en
// state="posted" y no estar pagada, si no devuelve INVALID_STATE
// o ALREADY_PAID.
// El {id} de la URL es el ID numerico de la factura en Element
// (campo "id" que devuelve crearla o buscarla), no su numero.
const resp = await fetch(`${URL}/api/fe/invoice/123/payment`, {
method: "POST",
headers,
body: JSON.stringify({
// Tipo de diario de pago: "cash" (caja, por omision) o "bank"
// (transferencia). Con "payment_journal_id" se indica un diario
// concreto en lugar del tipo.
journal_type: "cash",
// Monto del pago. Si se omite se cobra el total pendiente
// (amount_residual). Un monto menor genera un pago parcial.
amount: 50000
})
});
// Respuesta: payment_id, payment_name, invoice_payment_state
console.log(await resp.json());// Registra un pago para una factura ya publicada (posted). Usa el wizard
// "account.payment.register" de Element. La factura debe estar en
// state="posted" y no estar pagada, si no devuelve INVALID_STATE
// o ALREADY_PAID.
// El {id} de la URL es el ID numerico de la factura en Element
// (campo "id" que devuelve crearla o buscarla), no su numero.
var resp = await http.PostAsync($"{URL}/api/fe/invoice/123/payment", Json(new
{
// Tipo de diario de pago: "cash" (caja, por omision) o "bank"
// (transferencia). Con "payment_journal_id" se indica un diario
// concreto en lugar del tipo.
journal_type = "cash",
// Monto del pago. Si se omite se cobra el total pendiente
// (amount_residual). Un monto menor genera un pago parcial.
amount = 50000
}));
// Respuesta: payment_id, payment_name, invoice_payment_state
Console.WriteLine(await resp.Content.ReadAsStringAsync());// Registra un pago para una factura ya publicada (posted). Usa el wizard
// "account.payment.register" de Element. La factura debe estar en
// state="posted" y no estar pagada, si no devuelve INVALID_STATE
// o ALREADY_PAID.
// El {id} de la URL es el ID numerico de la factura en Element (campo "id"
// que devuelve crearla o buscarla), no su numero.
// journal_type "cash" (caja, por omision) o "bank" (transferencia). Con
// payment_journal_id se indica un diario concreto.
// amount si se omite se cobra el total pendiente (amount_residual).
String pago = """
{"journal_type": "cash", "amount": 50000}
""";
HttpResponse<String> resp = http.send(
pedido.apply("/api/fe/invoice/123/payment")
.POST(HttpRequest.BodyPublishers.ofString(pago))
.build(),
HttpResponse.BodyHandlers.ofString());
// Respuesta: payment_id, payment_name, invoice_payment_state
System.out.println(resp.body());# Registra un pago para una factura ya publicada (posted). La factura debe
# estar en state="posted" y no estar pagada, si no devuelve INVALID_STATE
# o ALREADY_PAID.
# El {id} de la URL es el ID numerico de la factura en Element (campo "id"
# que devuelve crearla o buscarla), no su numero.
# journal_type "cash" (caja, por omision) o "bank" (transferencia). Con
# payment_journal_id se indica un diario concreto.
# amount si se omite se cobra el total pendiente (amount_residual).
curl -s -b cookies.txt -X POST "$URL/api/fe/invoice/123/payment" \
-H "Authorization: Bearer $API_KEY" \
-H "X-Database: $DB" \
-H "Content-Type: application/json" \
-d '{"journal_type": "cash", "amount": 50000}'
Notas de remisión
POST /api/fe/remision
Una nota de remisión electrónica ampara el traslado de mercaderías entre dos puntos. La API crea
el stock.picking, asigna la secuencia, llena los datos del DE de remisión y dispara
la generación del XML automáticamente al validar el picking.
Para crear una nota de remisión el cliente externo debe enviar:
- Un partner destinatario:
partner_idyapi_partner_data. - Un punto de salida:
warehouse_data, con los datos del almacén de salida. - Una secuencia:
secuencia_idoprefijo, por ejemplo"002-001-". - Un transportista:
carrier_data, con datos del vehículo, chofer y empresa. - Los datos del traslado: motivo, fechas, distancia, tipo y modalidad de transporte.
- Las líneas de productos a trasladar.
Campos del request
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
partner_id | string | Sí | ID externo del cliente |
api_partner_data | object | Cond. | Datos del partner |
api_ruc | string | Cond. | Consulta SIFEN |
warehouse_data | object | Sí | Datos del almacén de salida |
prefijo | string | Sí | Prefijo de la secuencia, por ejemplo 002-001- |
tipo_operacion | string | Sí | B2B, B2C, B2G o B2F |
motivo_nota_remision | string | Sí | Código del motivo de traslado |
descripcion_motivo_remision | string | Cond. | Si el motivo es 99, de 5 a 60 caracteres |
responsable_emision | string | Sí | Responsable de emisión |
distancia_estimada | int | Sí | Kilómetros, rango 1 a 99999 |
tipo_transporte | string | Sí | 1 propio, 2 tercero |
modalidad_transporte | string | Sí | Terrestre, fluvial, aéreo o multimodal |
responsable_costo_flete | string | Sí | Responsable del flete |
inicio_estimado_traslado | date | Sí | Formato YYYY-MM-DD |
fin_estimado_traslado | date | Sí | Igual o posterior al inicio |
carrier_data | object | Sí | Datos del transportista |
lines | array | Sí | Al menos una línea con quantity mayor a 0 |
partner_street | string | No | Actualiza la calle del partner |
partner_numero_casa | string | No | Actualiza el número de casa |
Las claves de api_partner_data son las mismas descritas en el paso 3. En la nota de
remisión el destinatario debe tener departamento_id, distrito_id y
ciudad_id: si falta alguno de los tres, la generación del DE de remisión devuelve un
error. La calle y el número de casa también son obligatorios en el XML y pueden enviarse con
partner_street y partner_numero_casa.
- Punto de salida:
warehouse_data. - Secuencia:
prefijo. - Transportista:
carrier_data.
Si falta uno de estos tres campos, la API retorna un error 400.
Catálogo: motivo_nota_remision
| Código | Descripción |
|---|---|
1 | Traslado por venta |
2 | Traslado por consignación |
3 | Exportación |
4 | Traslado por compra |
5 | Importación |
6 | Traslado por devolución |
7 | Traslado entre locales de la empresa |
8 | Traslado de bienes por transformación |
9 | Traslado de bienes por reparación |
10 | Traslado por emisor móvil |
11 | Exhibición o demostración |
12 | Participación en ferias |
13 | Traslado de encomienda |
14 | Decomiso |
99 | Otro. Requiere descripcion_motivo_remision |
Catálogo: tipo_operacion
| Valor | Código | Descripción |
|---|---|---|
1 | B2B | Empresa a Empresa |
2 | B2C | Empresa a Consumidor Final. Valor por defecto |
3 | B2G | Empresa a Entidad del Gobierno |
4 | B2F | Empresa a Empresa o Persona del Exterior |
Si se omite, se usa el valor 2 (B2C). Valores fuera de 1–4
devuelven 400 INVALID_FIELD.
Catálogos auxiliares
responsable_emision
| Código | Descripción |
|---|---|
1 | Emisor de la factura |
2 | Poseedor de la factura y bienes |
3 | Empresa transportista |
4 | Despachante de aduanas |
5 | Agente de transporte o intermediario |
responsable_costo_flete
| Código | Descripción |
|---|---|
1 | Emisor de la factura electrónica |
2 | Receptor de la factura electrónica |
3 | Tercero |
4 | Agente intermediario del transporte |
5 | Transporte propio |
tipo_transporte y modalidad_transporte
| Código | tipo_transporte | modalidad_transporte |
|---|---|---|
1 | Propio | Terrestre |
2 | Tercero | Fluvial |
3 | — | Aéreo |
4 | — | Multimodal |
Campos de carrier_data
El campo carrier_data permite enviar los datos del transportista sin preconfiguración
previa en el sistema. El sistema busca el vehículo por matrícula, el chofer por tipo y número de
documento, y la empresa transportista por RUC o documento; si alguno no existe, lo crea
automáticamente.
Campos del vehículo y del chofer
| Campo | Tipo | Descripción |
|---|---|---|
nombre_transportista | string | Nombre de la empresa transportista |
marca_vehiculo | string | Marca del vehículo |
categoria_vehiculo | string | Categoría |
matricula_vehiculo | string | Matrícula |
tipo_documento_vehiculo | string | 1 número de identificación, 2 número de matrícula |
nombre_chofer | string | Nombre completo del chofer |
tipo_documento_chofer | string | 1 CI, 2 pasaporte, 3 cédula extranjera, 4 carnet de residencia |
numero_documento_chofer | string | Número de documento del chofer |
street_transportista | string | Dirección de la empresa. Obligatoria en el XML |
Campos según contribuyente_transportista
| Campo | Si contribuyente = true | Si contribuyente = false |
|---|---|---|
ruc_transportista | Requerido | — |
tipo_documento_transportista | — | Requerido, valores 1 a 4 |
numero_documento_transportista | — | Requerido |
Ejemplo de nota de remisión
resp = session.post(
f"{URL}/api/fe/remision",
headers=headers,
json={
# --- Cliente destinatario ---
# ID externo. Si ya existe el partner mapeado se reutiliza; en caso
# contrario se crea con los datos de api_partner_data.
"partner_id": "48214",
"api_partner_data": {
"name": "Distribuidora ABC S.A.",
"ruc": "80123456-7", # RUC con digito verificador
"company_type": 2, # 2=empresa, 1=persona
"email": "[email protected]",
# Datos geograficos: aceptan ID numerico o nombre en texto.
# La API resuelve los IDs segun la jerarquia SIFEN.
"departamento_id": "Central",
"distrito_id": "Luque",
"ciudad_id": "Luque"
},
# Calle y numero de casa del destinatario. Obligatorios en el XML.
"partner_street": "Av. Mariscal Lopez 1234",
"partner_numero_casa": "1234",
# --- Punto de salida ---
# warehouse_data crea o reutiliza el almacen por nombre.
"warehouse_data": {
"name": "Almacen Principal",
"code": "MAIN",
"partner_street": "Av. Eusebio Ayala 1234",
"partner_numero_casa": "1234",
"partner_departamento": "Central",
"partner_distrito": "Luque",
"partner_ciudad": "Luque",
"partner_barrio": "Centro",
"location_direccion": "Av. Eusebio Ayala 1234",
"location_numero_casa": "1234",
"location_departamento": "Central",
"location_distrito": "Luque",
"location_ciudad": "Luque"
},
# --- Secuencia NRE ---
# El prefijo identifica la secuencia DE de remision, configurada en
# Facturacion > Configuracion > DE > Secuencias.
# Alternativa: "secuencia_id" con el ID numerico.
"prefijo": "002-001-",
# --- Datos del traslado ---
"tipo_operacion": "1", # 1=B2B
"motivo_nota_remision": "1", # 1=Traslado por venta
"responsable_emision": "1", # 1=Emisor de la factura
"distancia_estimada": 25, # km estimados
"tipo_transporte": "1", # 1=Propio
"modalidad_transporte": "1", # 1=Terrestre
"responsable_costo_flete": "1", # 1=Emisor FE
"inicio_estimado_traslado": "2026-05-05",
"fin_estimado_traslado": "2026-05-06",
# Con motivo "1" (traslado por venta) el DE exige la fecha de la
# factura futura, salvo que el pedido ya tenga factura emitida.
"fecha_factura_futura": "2026-05-10",
# --- Transportista ---
# El sistema crea o reutiliza vehiculo, chofer y empresa
# transportista segun los datos provistos.
"carrier_data": {
"nombre_transportista": "Transportes XYZ S.A.",
"marca_vehiculo": "Toyota",
"categoria_vehiculo": "Camion",
"matricula_vehiculo": "ABC-123",
"tipo_documento_vehiculo": "2", # 2=Nro de matricula
"nombre_chofer": "Juan Perez",
"tipo_documento_chofer": "1", # 1=CI paraguaya
"numero_documento_chofer": "4567890",
# Direccion del chofer: va en el XML del DE. Si el chofer todavia
# no existe en Element, sin este dato el DE no se puede generar.
"street_chofer": "Av. Mariscal Estigarribia 456",
"street_transportista": "Av. Espana 999",
# Empresa transportista contribuyente:
"contribuyente_transportista": True,
"ruc_transportista": "80999888-7"
},
# --- Lineas a trasladar ---
"lines": [{
"product_id": "20458",
"api_product_data": {
"name": "Mercaderia general",
"default_code": "SRV-2045",
"uom_id": 1, # Unidad de medida
"tax_id": 1
},
"quantity": 5
}]
}
)
# La respuesta incluye id, name (numero NRE), DE_estado, DE_id (CDC),
# DE_xml y DE_url cuando fue aprobada por SIFEN.
print(resp.json())const resp = await fetch(`${URL}/api/fe/remision`, {
method: "POST",
headers,
body: JSON.stringify({
// --- Cliente destinatario ---
// ID externo. Si ya existe el partner mapeado se reutiliza; en caso
// contrario se crea con los datos de api_partner_data.
partner_id: "48214",
api_partner_data: {
name: "Distribuidora ABC S.A.",
ruc: "80123456-7", // RUC con digito verificador
company_type: 2, // 2=empresa, 1=persona
email: "[email protected]",
// Datos geograficos: aceptan ID numerico o nombre en texto.
// La API resuelve los IDs segun la jerarquia SIFEN.
departamento_id: "Central",
distrito_id: "Luque",
ciudad_id: "Luque"
},
// Calle y numero de casa del destinatario. Obligatorios en el XML.
partner_street: "Av. Mariscal Lopez 1234",
partner_numero_casa: "1234",
// --- Punto de salida ---
// warehouse_data crea o reutiliza el almacen por nombre.
warehouse_data: {
name: "Almacen Principal",
code: "MAIN",
partner_street: "Av. Eusebio Ayala 1234",
partner_numero_casa: "1234",
partner_departamento: "Central",
partner_distrito: "Luque",
partner_ciudad: "Luque",
partner_barrio: "Centro",
location_direccion: "Av. Eusebio Ayala 1234",
location_numero_casa: "1234",
location_departamento: "Central",
location_distrito: "Luque",
location_ciudad: "Luque"
},
// --- Secuencia NRE ---
// El prefijo identifica la secuencia DE de remision, configurada en
// Facturacion > Configuracion > DE > Secuencias.
// Alternativa: "secuencia_id" con el ID numerico.
prefijo: "002-001-",
// --- Datos del traslado ---
tipo_operacion: "1", // 1=B2B
motivo_nota_remision: "1", // 1=Traslado por venta
responsable_emision: "1", // 1=Emisor de la factura
distancia_estimada: 25, // km estimados
tipo_transporte: "1", // 1=Propio
modalidad_transporte: "1", // 1=Terrestre
responsable_costo_flete: "1", // 1=Emisor FE
inicio_estimado_traslado: "2026-05-05",
fin_estimado_traslado: "2026-05-06",
// Con motivo "1" (traslado por venta) el DE exige la fecha de la
// factura futura, salvo que el pedido ya tenga factura emitida.
fecha_factura_futura: "2026-05-10",
// --- Transportista ---
// El sistema crea o reutiliza vehiculo, chofer y empresa
// transportista segun los datos provistos.
carrier_data: {
nombre_transportista: "Transportes XYZ S.A.",
marca_vehiculo: "Toyota",
categoria_vehiculo: "Camion",
matricula_vehiculo: "ABC-123",
tipo_documento_vehiculo: "2", // 2=Nro de matricula
nombre_chofer: "Juan Perez",
tipo_documento_chofer: "1", // 1=CI paraguaya
numero_documento_chofer: "4567890",
// Direccion del chofer: va en el XML del DE. Si el chofer todavia
// no existe en Element, sin este dato el DE no se puede generar.
street_chofer: "Av. Mariscal Estigarribia 456",
street_transportista: "Av. Espana 999",
// Empresa transportista contribuyente:
contribuyente_transportista: true,
ruc_transportista: "80999888-7"
},
// --- Lineas a trasladar ---
lines: [{
product_id: "20458",
api_product_data: {
name: "Mercaderia general",
default_code: "SRV-2045",
uom_id: 1, // Unidad de medida
tax_id: 1
},
quantity: 5
}]
})
});
// La respuesta incluye id, name (numero NRE), DE_estado, DE_id (CDC),
// DE_xml y DE_url cuando fue aprobada por SIFEN.
console.log(await resp.json());var resp = await http.PostAsync($"{URL}/api/fe/remision", Json(new
{
// --- Cliente destinatario ---
// ID externo. Si ya existe el partner mapeado se reutiliza; en caso
// contrario se crea con los datos de api_partner_data.
partner_id = "48214",
api_partner_data = new
{
name = "Distribuidora ABC S.A.",
ruc = "80123456-7", // RUC con digito verificador
company_type = 2, // 2=empresa, 1=persona
email = "[email protected]",
// Datos geograficos: aceptan ID numerico o nombre en texto.
// La API resuelve los IDs segun la jerarquia SIFEN.
departamento_id = "Central",
distrito_id = "Luque",
ciudad_id = "Luque"
},
// Calle y numero de casa del destinatario. Obligatorios en el XML.
partner_street = "Av. Mariscal Lopez 1234",
partner_numero_casa = "1234",
// --- Punto de salida ---
// warehouse_data crea o reutiliza el almacen por nombre.
warehouse_data = new
{
name = "Almacen Principal",
code = "MAIN",
partner_street = "Av. Eusebio Ayala 1234",
partner_numero_casa = "1234",
partner_departamento = "Central",
partner_distrito = "Luque",
partner_ciudad = "Luque",
partner_barrio = "Centro",
location_direccion = "Av. Eusebio Ayala 1234",
location_numero_casa = "1234",
location_departamento = "Central",
location_distrito = "Luque",
location_ciudad = "Luque"
},
// --- Secuencia NRE ---
// El prefijo identifica la secuencia DE de remision, configurada en
// Facturacion > Configuracion > DE > Secuencias.
// Alternativa: "secuencia_id" con el ID numerico.
prefijo = "002-001-",
// --- Datos del traslado ---
tipo_operacion = "1", // 1=B2B
motivo_nota_remision = "1", // 1=Traslado por venta
responsable_emision = "1", // 1=Emisor de la factura
distancia_estimada = 25, // km estimados
tipo_transporte = "1", // 1=Propio
modalidad_transporte = "1", // 1=Terrestre
responsable_costo_flete = "1", // 1=Emisor FE
inicio_estimado_traslado = "2026-05-05",
fin_estimado_traslado = "2026-05-06",
// Con motivo "1" (traslado por venta) el DE exige la fecha de la
// factura futura, salvo que el pedido ya tenga factura emitida.
fecha_factura_futura = "2026-05-10",
// --- Transportista ---
// El sistema crea o reutiliza vehiculo, chofer y empresa
// transportista segun los datos provistos.
carrier_data = new
{
nombre_transportista = "Transportes XYZ S.A.",
marca_vehiculo = "Toyota",
categoria_vehiculo = "Camion",
matricula_vehiculo = "ABC-123",
tipo_documento_vehiculo = "2", // 2=Nro de matricula
nombre_chofer = "Juan Perez",
tipo_documento_chofer = "1", // 1=CI paraguaya
numero_documento_chofer = "4567890",
// Direccion del chofer: va en el XML del DE. Si el chofer todavia
// no existe en Element, sin este dato el DE no se puede generar.
street_chofer = "Av. Mariscal Estigarribia 456",
street_transportista = "Av. Espana 999",
// Empresa transportista contribuyente:
contribuyente_transportista = true,
ruc_transportista = "80999888-7"
},
// --- Lineas a trasladar ---
lines = new[]
{
new
{
product_id = "20458",
api_product_data = new
{
name = "Mercaderia general",
default_code = "SRV-2045",
uom_id = 1, // Unidad de medida
tax_id = 1
},
quantity = 5
}
}
}));
// La respuesta incluye id, name (numero NRE), DE_estado, DE_id (CDC),
// DE_xml y DE_url cuando fue aprobada por SIFEN.
Console.WriteLine(await resp.Content.ReadAsStringAsync());// Campos del traslado:
// partner_id / api_partner_data cliente destinatario. Si ya existe el
// partner mapeado se reutiliza. Los campos geograficos aceptan ID
// numerico o nombre; la API resuelve los IDs segun la jerarquia SIFEN.
// partner_street, partner_numero_casa obligatorios en el XML del DE.
// warehouse_data punto de salida; crea o reutiliza el almacen por nombre.
// prefijo identifica la secuencia DE de remision (Facturacion >
// Configuracion > DE > Secuencias). Alternativa: secuencia_id.
// fecha_factura_futura con motivo "1" (traslado por venta) el DE la
// exige, salvo que el pedido ya tenga factura emitida.
// carrier_data crea o reutiliza vehiculo, chofer y empresa transportista.
// street_chofer va en el XML del DE: sin el, un chofer que todavia no
// existe en Element no se puede dar de alta.
String remision = """
{
"partner_id": "48214",
"api_partner_data": {
"name": "Distribuidora ABC S.A.",
"ruc": "80123456-7",
"company_type": 2,
"email": "[email protected]",
"departamento_id": "Central",
"distrito_id": "Luque",
"ciudad_id": "Luque"
},
"partner_street": "Av. Mariscal Lopez 1234",
"partner_numero_casa": "1234",
"warehouse_data": {
"name": "Almacen Principal",
"code": "MAIN",
"partner_street": "Av. Eusebio Ayala 1234",
"partner_numero_casa": "1234",
"partner_departamento": "Central",
"partner_distrito": "Luque",
"partner_ciudad": "Luque",
"partner_barrio": "Centro",
"location_direccion": "Av. Eusebio Ayala 1234",
"location_numero_casa": "1234",
"location_departamento": "Central",
"location_distrito": "Luque",
"location_ciudad": "Luque"
},
"prefijo": "002-001-",
"tipo_operacion": "1",
"motivo_nota_remision": "1",
"responsable_emision": "1",
"distancia_estimada": 25,
"tipo_transporte": "1",
"modalidad_transporte": "1",
"responsable_costo_flete": "1",
"inicio_estimado_traslado": "2026-05-05",
"fin_estimado_traslado": "2026-05-06",
"fecha_factura_futura": "2026-05-10",
"carrier_data": {
"nombre_transportista": "Transportes XYZ S.A.",
"marca_vehiculo": "Toyota",
"categoria_vehiculo": "Camion",
"matricula_vehiculo": "ABC-123",
"tipo_documento_vehiculo": "2",
"nombre_chofer": "Juan Perez",
"tipo_documento_chofer": "1",
"numero_documento_chofer": "4567890",
"street_chofer": "Av. Mariscal Estigarribia 456",
"street_transportista": "Av. Espana 999",
"contribuyente_transportista": true,
"ruc_transportista": "80999888-7"
},
"lines": [{
"product_id": "20458",
"api_product_data": {
"name": "Mercaderia general",
"default_code": "SRV-2045",
"uom_id": 1,
"tax_id": 1
},
"quantity": 5
}]
}
""";
HttpResponse<String> resp = http.send(
pedido.apply("/api/fe/remision")
.POST(HttpRequest.BodyPublishers.ofString(remision))
.build(),
HttpResponse.BodyHandlers.ofString());
// La respuesta incluye id, name (numero NRE), DE_estado, DE_id (CDC),
// DE_xml y DE_url cuando fue aprobada por SIFEN.
System.out.println(resp.body());# Campos del traslado:
# partner_id / api_partner_data cliente destinatario. Si ya existe el
# partner mapeado se reutiliza. Los campos geograficos aceptan ID
# numerico o nombre; la API resuelve los IDs segun la jerarquia SIFEN.
# partner_street, partner_numero_casa obligatorios en el XML del DE.
# warehouse_data punto de salida; crea o reutiliza el almacen por nombre.
# prefijo identifica la secuencia DE de remision (Facturacion >
# Configuracion > DE > Secuencias). Alternativa: secuencia_id.
# fecha_factura_futura con motivo "1" (traslado por venta) el DE la
# exige, salvo que el pedido ya tenga factura emitida.
# carrier_data crea o reutiliza vehiculo, chofer y empresa transportista.
# street_chofer va en el XML del DE: sin el, un chofer que todavia no
# existe en Element no se puede dar de alta.
curl -s -b cookies.txt -X POST "$URL/api/fe/remision" \
-H "Authorization: Bearer $API_KEY" \
-H "X-Database: $DB" \
-H "Content-Type: application/json" \
-d '{
"partner_id": "48214",
"api_partner_data": {
"name": "Distribuidora ABC S.A.",
"ruc": "80123456-7",
"company_type": 2,
"email": "[email protected]",
"departamento_id": "Central",
"distrito_id": "Luque",
"ciudad_id": "Luque"
},
"partner_street": "Av. Mariscal Lopez 1234",
"partner_numero_casa": "1234",
"warehouse_data": {
"name": "Almacen Principal",
"code": "MAIN",
"partner_street": "Av. Eusebio Ayala 1234",
"partner_numero_casa": "1234",
"partner_departamento": "Central",
"partner_distrito": "Luque",
"partner_ciudad": "Luque",
"partner_barrio": "Centro",
"location_direccion": "Av. Eusebio Ayala 1234",
"location_numero_casa": "1234",
"location_departamento": "Central",
"location_distrito": "Luque",
"location_ciudad": "Luque"
},
"prefijo": "002-001-",
"tipo_operacion": "1",
"motivo_nota_remision": "1",
"responsable_emision": "1",
"distancia_estimada": 25,
"tipo_transporte": "1",
"modalidad_transporte": "1",
"responsable_costo_flete": "1",
"inicio_estimado_traslado": "2026-05-05",
"fin_estimado_traslado": "2026-05-06",
"fecha_factura_futura": "2026-05-10",
"carrier_data": {
"nombre_transportista": "Transportes XYZ S.A.",
"marca_vehiculo": "Toyota",
"categoria_vehiculo": "Camion",
"matricula_vehiculo": "ABC-123",
"tipo_documento_vehiculo": "2",
"nombre_chofer": "Juan Perez",
"tipo_documento_chofer": "1",
"numero_documento_chofer": "4567890",
"street_chofer": "Av. Mariscal Estigarribia 456",
"street_transportista": "Av. Espana 999",
"contribuyente_transportista": true,
"ruc_transportista": "80999888-7"
},
"lines": [{
"product_id": "20458",
"api_product_data": {
"name": "Mercaderia general",
"default_code": "SRV-2045",
"uom_id": 1,
"tax_id": 1
},
"quantity": 5
}]
}'
La consulta SIFEN (api_ruc) puede fallar si el RUC no está registrado en el
servicio de la DNIT. Para producción, prefiera api_partner_data con los datos
manuales: el ejemplo anterior siempre funciona, mientras que api_ruc depende de
la disponibilidad del servicio externo.
Buscar nota de remisión
GET /api/fe/remision/{numero}
# Busca una NRE por su numero (campo "name" del stock.picking).
# Devuelve 404 NOT_FOUND si no existe.
resp = session.get(
f"{URL}/api/fe/remision/002-001-0000123",
headers=headers
)
data = resp.json()["data"]
print(data["DE_estado"]) # pendiente, aprobado, rechazado, cancelado
print(data["DE_id"]) # CDC de 44 digitos// Busca una NRE por su numero (campo "name" del stock.picking).
// Devuelve 404 NOT_FOUND si no existe.
const resp = await fetch(`${URL}/api/fe/remision/002-001-0000123`, { headers });
const { data } = await resp.json();
console.log(data.DE_estado); // pendiente, aprobado, rechazado, cancelado
console.log(data.DE_id); // CDC de 44 digitosusing System.Text.Json.Nodes;
// Busca una NRE por su numero (campo "name" del stock.picking).
// Devuelve 404 NOT_FOUND si no existe.
var resp = await http.GetAsync($"{URL}/api/fe/remision/002-001-0000123");
var data = JsonNode.Parse(await resp.Content.ReadAsStringAsync())!["data"]!;
Console.WriteLine(data["DE_estado"]); // pendiente, aprobado, rechazado, cancelado
Console.WriteLine(data["DE_id"]); // CDC de 44 digitos// Busca una NRE por su numero (campo "name" del stock.picking).
// Devuelve 404 NOT_FOUND si no existe. La respuesta trae DE_estado
// (pendiente, aprobado, rechazado, cancelado) y DE_id (CDC de 44 digitos).
HttpResponse<String> resp = http.send(
pedido.apply("/api/fe/remision/002-001-0000123").GET().build(),
HttpResponse.BodyHandlers.ofString());
System.out.println(resp.body());# Busca una NRE por su numero (campo "name" del stock.picking).
# Devuelve 404 NOT_FOUND si no existe. La respuesta trae DE_estado
# (pendiente, aprobado, rechazado, cancelado) y DE_id (CDC de 44 digitos).
curl -s -b cookies.txt "$URL/api/fe/remision/002-001-0000123" \
-H "Authorization: Bearer $API_KEY" \
-H "X-Database: $DB"
Cancelar nota de remisión
POST /api/fe/remision/{numero}/cancel
# Cancela una NRE ya aprobada por SIFEN: DE_estado debe ser "aprobado".
# El motivo debe tener al menos 5 caracteres (recomendado 10-200).
resp = session.post(
f"{URL}/api/fe/remision/002-001-0000123/cancel",
headers=headers,
json={"motivo": "El cliente cancelo el pedido antes del despacho"}
)
# En la respuesta DE_estado pasa a "cancelado".
print(resp.json())// Cancela una NRE ya aprobada por SIFEN: DE_estado debe ser "aprobado".
// El motivo debe tener al menos 5 caracteres (recomendado 10-200).
const resp = await fetch(`${URL}/api/fe/remision/002-001-0000123/cancel`, {
method: "POST",
headers,
body: JSON.stringify({ motivo: "El cliente cancelo el pedido antes del despacho" })
});
// En la respuesta DE_estado pasa a "cancelado".
console.log(await resp.json());// Cancela una NRE ya aprobada por SIFEN: DE_estado debe ser "aprobado".
// El motivo debe tener al menos 5 caracteres (recomendado 10-200).
var resp = await http.PostAsync($"{URL}/api/fe/remision/002-001-0000123/cancel", Json(new
{
motivo = "El cliente cancelo el pedido antes del despacho"
}));
// En la respuesta DE_estado pasa a "cancelado".
Console.WriteLine(await resp.Content.ReadAsStringAsync());// Cancela una NRE ya aprobada por SIFEN: DE_estado debe ser "aprobado".
// El motivo debe tener al menos 5 caracteres (recomendado 10-200).
String cancelacion = """
{"motivo": "El cliente cancelo el pedido antes del despacho"}
""";
HttpResponse<String> resp = http.send(
pedido.apply("/api/fe/remision/002-001-0000123/cancel")
.POST(HttpRequest.BodyPublishers.ofString(cancelacion))
.build(),
HttpResponse.BodyHandlers.ofString());
// En la respuesta DE_estado pasa a "cancelado".
System.out.println(resp.body());# Cancela una NRE ya aprobada por SIFEN: DE_estado debe ser "aprobado".
# El motivo debe tener al menos 5 caracteres (recomendado 10-200).
curl -s -b cookies.txt -X POST "$URL/api/fe/remision/002-001-0000123/cancel" \
-H "Authorization: Bearer $API_KEY" \
-H "X-Database: $DB" \
-H "Content-Type: application/json" \
-d '{"motivo": "El cliente cancelo el pedido antes del despacho"}'
Enviar por correo
POST /api/fe/remision/{id}/send-email
Envía la nota de remisión por correo electrónico con el XML y el KUDE en PDF adjuntos. El
parámetro {id} es el ID numérico interno del picking, no el número de la nota.
# El picking_id es el campo "id" que devuelve crear o buscar la NRE:
# es el ID numerico, no el numero del documento.
picking_id = 456
resp = session.post(
f"{URL}/api/fe/remision/{picking_id}/send-email",
headers=headers,
json={"email": "[email protected]"}
)
# La respuesta confirma email_to, remision_name y mail_state.
# Requiere servidor de correo configurado en los ajustes DE.
print(resp.json())// El picking_id es el campo "id" que devuelve crear o buscar la NRE:
// es el ID numerico, no el numero del documento.
const picking_id = 456;
const resp = await fetch(`${URL}/api/fe/remision/${picking_id}/send-email`, {
method: "POST",
headers,
body: JSON.stringify({ email: "[email protected]" })
});
// La respuesta confirma email_to, remision_name y mail_state.
// Requiere servidor de correo configurado en los ajustes DE.
console.log(await resp.json());// El picking_id es el campo "id" que devuelve crear o buscar la NRE:
// es el ID numerico, no el numero del documento.
var picking_id = 456;
var resp = await http.PostAsync($"{URL}/api/fe/remision/{picking_id}/send-email", Json(new
{
email = "[email protected]"
}));
// La respuesta confirma email_to, remision_name y mail_state.
// Requiere servidor de correo configurado en los ajustes DE.
Console.WriteLine(await resp.Content.ReadAsStringAsync());// El picking_id es el campo "id" que devuelve crear o buscar la NRE:
// es el ID numerico, no el numero del documento.
int picking_id = 456;
String correo = """
{"email": "[email protected]"}
""";
HttpResponse<String> resp = http.send(
pedido.apply("/api/fe/remision/" + picking_id + "/send-email")
.POST(HttpRequest.BodyPublishers.ofString(correo))
.build(),
HttpResponse.BodyHandlers.ofString());
// La respuesta confirma email_to, remision_name y mail_state.
// Requiere servidor de correo configurado en los ajustes DE.
System.out.println(resp.body());# El picking_id de la URL es el campo "id" que devuelve crear o buscar la
# NRE: es el ID numerico, no el numero del documento.
# La respuesta confirma email_to, remision_name y mail_state.
# Requiere servidor de correo configurado en los ajustes DE.
curl -s -b cookies.txt -X POST "$URL/api/fe/remision/456/send-email" \
-H "Authorization: Bearer $API_KEY" \
-H "X-Database: $DB" \
-H "Content-Type: application/json" \
-d '{"email": "[email protected]"}'
Clientes y KUDE PDF
Alta y actualización de contactos sin emitir documentos, y descarga de la representación gráfica oficial de un documento electrónico.
Buscar o crear cliente por RUC
POST /api/fe/partner/by-ruc/{ruc}
Consulta el RUC en SIFEN y devuelve el contacto correspondiente en Element ERP. No requiere body.
El RUC en la URL puede enviarse con o sin dígito verificador: 80000001 o
80000001-1.
resp = session.post(
f"{URL}/api/fe/partner/by-ruc/80000001-1",
headers=headers
)
result = resp.json()
print(result["created"]) # true si se creo un contacto nuevo
print(result["data"]["id"]) # ID de Element ERP para usar en otras llamadasconst resp = await fetch(`${URL}/api/fe/partner/by-ruc/80000001-1`, {
method: "POST",
headers
});
const result = await resp.json();
console.log(result.created); // true si se creo un contacto nuevo
console.log(result.data.id); // ID de Element ERP para usar en otras llamadasusing System.Text.Json.Nodes;
var resp = await http.PostAsync($"{URL}/api/fe/partner/by-ruc/80000001-1", null);
var result = JsonNode.Parse(await resp.Content.ReadAsStringAsync())!;
Console.WriteLine(result["created"]); // true si se creo un contacto nuevo
Console.WriteLine(result["data"]!["id"]); // ID de Element ERP para otras llamadas// Este endpoint no lleva cuerpo. La respuesta trae "created" (true si se
// creo un contacto nuevo) y "data.id", el ID de Element ERP que se usa
// en las demas llamadas.
HttpResponse<String> resp = http.send(
pedido.apply("/api/fe/partner/by-ruc/80000001-1")
.POST(HttpRequest.BodyPublishers.noBody())
.build(),
HttpResponse.BodyHandlers.ofString());
System.out.println(resp.body());# Este endpoint no lleva cuerpo. La respuesta trae "created" (true si se
# creo un contacto nuevo) y "data.id", el ID de Element ERP que se usa
# en las demas llamadas.
curl -s -b cookies.txt -X POST "$URL/api/fe/partner/by-ruc/80000001-1" \
-H "Authorization: Bearer $API_KEY" \
-H "X-Database: $DB"
{
"success": true,
"created": true,
"data": {
"id": 123,
"name": "EMPRESA EJEMPLO S.A.",
"ruc": "80000001-1",
"ruc_state": "ACT",
"contribuyente": true,
"company_type": "company"
}
}
created indica si el contacto fue creado (true) o ya existía
(false). ruc_state es el estado del RUC informado por SIFEN, por ejemplo
"ACT".
| HTTP | Código | Descripción |
|---|---|---|
| 200 | — | Contacto encontrado o creado |
| 400 | INVALID_FIELD | RUC no encontrado en SIFEN o con formato inválido |
| 400 | CREATE_ERROR | Error al crear el contacto |
Actualizar cliente por RUC o CI
POST /api/fe/partner/by-ruc/{ruc}/update
POST /api/fe/partner/by-ci/{ci}/update
Actualiza uno o más contactos existentes. Solo se modifican los campos incluidos en el body; los
omitidos no se tocan. Enviar null vacía el campo explícitamente, excepto
name, que no puede quedar vacío.
Si el RUC o la CI coincide con más de un contacto, la actualización se aplica a todos. El
campo matched_count en la respuesta indica cuántos contactos fueron afectados.
Campos estándar y de dirección
| Campo | Tipo | Descripción |
|---|---|---|
name | string | Nombre o razón social |
ref | string | Referencia interna |
company_type | int/string | 1 o "person" para persona física, 2 o "company" para empresa |
lang | string | Código de idioma |
comment | string | Notas internas |
website | string | URL del sitio web |
phone | string | Teléfono. De 6 a 15 caracteres; si el cliente es de Paraguay, con el prefijo de la ciudad en vez del código de país, por ejemplo 021445566 |
mobile | string | Celular |
email | string | Correo electrónico, destinatario principal del envío automático del DE |
vat | string | Identificación fiscal |
street | string | Dirección, línea 1 |
street2 | string | Dirección, línea 2 |
city | string | Ciudad, texto libre |
zip | string | Código postal |
state_id | int/string | Estado o provincia: ID o nombre |
country_id | int/string | País: ID o nombre |
Campos de facturación electrónica
| Campo | Tipo | Descripción |
|---|---|---|
ruc | string | RUC del contribuyente; se reformatea con dígito verificador automáticamente |
ci | string | Número de cédula de identidad |
tipo_documento | string | Código del tipo de documento, ver la tabla del paso 3 |
tipo_documento_descripcion | string | Descripción del documento cuando tipo_documento es 9. Mínimo 9 y máximo 41 caracteres |
documento_numero | string | Número del documento. Máximo 20 caracteres |
contribuyente | bool | Es contribuyente ante la DNIT |
departamento_id | int/string | Departamento: ID o nombre |
distrito_id | int/string | Distrito: ID o nombre |
ciudad_id | int/string | Ciudad: ID o nombre |
barrio_id | int/string | Barrio: ID o nombre |
numero_casa | string | Número de casa. Máximo 6 caracteres |
id_impositivo | string | Identificador impositivo para clientes del exterior |
tax_id | string | Identificador fiscal adicional |
cuit | string | CUIT de Argentina |
Los campos geográficos aceptan ID numérico o nombre, con la misma jerarquía descrita en el paso 3.
Campos especiales
| Campo | Descripción |
|---|---|
api_ruc | Consulta el RUC en SIFEN y sobrescribe name, ruc, contribuyente y ruc_state con los datos oficiales |
external_id | Vincula el contacto al mapeo de IDs externos de la API, para reutilizarlo luego en la creación de facturas o notas de remisión. Solo puede usarse cuando la búsqueda encuentra un único contacto |
email_cc | Configura los correos en copia al enviar cada tipo de documento electrónico del cliente |
El ruc o ci de la URL solo sirve para ubicar el contacto a actualizar.
api_ruc es un campo aparte, del body: si se envía, dispara una nueva consulta a SIFEN
y sobrescribe name, ruc, contribuyente y
ruc_state con los datos oficiales devueltos, incluso si el body ya trae esos mismos
campos con otro valor. En la práctica se envía el mismo RUC que identifica al contacto en la URL,
para refrescar sus datos oficiales sin tocar el resto:
resp = session.post(
f"{URL}/api/fe/partner/by-ruc/80000001-1/update",
headers=headers,
json={"api_ruc": "80000001-1"}
)
print(resp.json())const resp = await fetch(`${URL}/api/fe/partner/by-ruc/80000001-1/update`, {
method: "POST",
headers,
body: JSON.stringify({ api_ruc: "80000001-1" })
});
console.log(await resp.json());var resp = await http.PostAsync($"{URL}/api/fe/partner/by-ruc/80000001-1/update", Json(new
{
api_ruc = "80000001-1"
}));
Console.WriteLine(await resp.Content.ReadAsStringAsync());String cuerpo = """
{"api_ruc": "80000001-1"}
""";
HttpResponse<String> resp = http.send(
pedido.apply("/api/fe/partner/by-ruc/80000001-1/update")
.POST(HttpRequest.BodyPublishers.ofString(cuerpo))
.build(),
HttpResponse.BodyHandlers.ofString());
System.out.println(resp.body());curl -s -b cookies.txt -X POST "$URL/api/fe/partner/by-ruc/80000001-1/update" \
-H "Authorization: Bearer $API_KEY" \
-H "X-Database: $DB" \
-H "Content-Type: application/json" \
-d '{"api_ruc": "80000001-1"}'
external_id vincula el contacto al sistema de mapeo de IDs externos de la API: a
partir de esa llamada, el mismo valor puede usarse como partner_id en
/api/fe/invoice, /api/fe/credit-note o /api/fe/remision, sin
volver a enviar api_ruc ni api_partner_data. Si el RUC o CI de la URL
coincide con más de un contacto, enviar external_id cancela toda la actualización
(ningún campo se modifica) y devuelve 400 INVALID_FIELD.
resp = session.post(
f"{URL}/api/fe/partner/by-ruc/80000001-1/update",
headers=headers,
json={"external_id": "48213"}
)
print(resp.json())const resp = await fetch(`${URL}/api/fe/partner/by-ruc/80000001-1/update`, {
method: "POST",
headers,
body: JSON.stringify({ external_id: "48213" })
});
console.log(await resp.json());var resp = await http.PostAsync($"{URL}/api/fe/partner/by-ruc/80000001-1/update", Json(new
{
external_id = "48213"
}));
Console.WriteLine(await resp.Content.ReadAsStringAsync());String cuerpo = """
{"external_id": "48213"}
""";
HttpResponse<String> resp = http.send(
pedido.apply("/api/fe/partner/by-ruc/80000001-1/update")
.POST(HttpRequest.BodyPublishers.ofString(cuerpo))
.build(),
HttpResponse.BodyHandlers.ofString());
System.out.println(resp.body());curl -s -b cookies.txt -X POST "$URL/api/fe/partner/by-ruc/80000001-1/update" \
-H "Authorization: Bearer $API_KEY" \
-H "X-Database: $DB" \
-H "Content-Type: application/json" \
-d '{"external_id": "48213"}'
Desde entonces, "partner_id": "48213" identifica a este contacto en cualquier otro
endpoint de la API.
email_cc es un objeto: cada clave es un tipo de documento (factura,
nota_credito, nota_debito o nota_remision) y cada valor es
la lista de correos que reciben copia para ese tipo.
"email_cc": {
"factura": ["[email protected]", "[email protected]"],
"nota_credito": ["[email protected]"],
"nota_debito": []
}
La lista enviada para un tipo reemplaza por completo la configuración anterior de ese tipo: en el
ejemplo, nota_debito queda sin copias porque se envió una lista vacía. Un tipo no
incluido en el objeto no se modifica. Enviar email_cc no toca el correo principal
(email) ni viceversa: son dos campos independientes.
El envío automático del documento electrónico solo se dispara si el contacto tiene
email configurado. Sin correo principal, los destinatarios de
email_cc tampoco reciben copia.
resp = session.post(
f"{URL}/api/fe/partner/by-ruc/80000001-1/update",
headers=headers,
json={
"email": "[email protected]",
"phone": "021445566",
"email_cc": {
"factura": ["[email protected]", "[email protected]"]
}
}
)
print(resp.json())const resp = await fetch(`${URL}/api/fe/partner/by-ruc/80000001-1/update`, {
method: "POST",
headers,
body: JSON.stringify({
email: "[email protected]",
phone: "021445566",
email_cc: {
factura: ["[email protected]", "[email protected]"]
}
})
});
console.log(await resp.json());var resp = await http.PostAsync($"{URL}/api/fe/partner/by-ruc/80000001-1/update", Json(new
{
email = "[email protected]",
phone = "021445566",
email_cc = new
{
factura = new[] { "[email protected]", "[email protected]" }
}
}));
Console.WriteLine(await resp.Content.ReadAsStringAsync());String cuerpo = """
{
"email": "[email protected]",
"phone": "021445566",
"email_cc": {
"factura": ["[email protected]", "[email protected]"]
}
}
""";
HttpResponse<String> resp = http.send(
pedido.apply("/api/fe/partner/by-ruc/80000001-1/update")
.POST(HttpRequest.BodyPublishers.ofString(cuerpo))
.build(),
HttpResponse.BodyHandlers.ofString());
System.out.println(resp.body());curl -s -b cookies.txt -X POST "$URL/api/fe/partner/by-ruc/80000001-1/update" \
-H "Authorization: Bearer $API_KEY" \
-H "X-Database: $DB" \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"phone": "021445566",
"email_cc": {
"factura": ["[email protected]", "[email protected]"]
}
}'
Actualizar por cédula sigue el mismo formato, contra
/api/fe/partner/by-ci/{ci}/update.
{
"success": true,
"data": {
"matched_count": 1,
"updated": [
{
"id": 123,
"name": "EMPRESA EJEMPLO S.A.",
"ruc": "80000001-1",
"email": "[email protected]"
}
]
}
}
Si se actualiza el ruc o la ci usados para identificar al contacto
en la URL, quedará inalcanzable por esa clave en llamadas futuras.
| HTTP | Código | Descripción |
|---|---|---|
| 200 | — | Contactos actualizados |
| 404 | NOT_FOUND | Ningún contacto coincide con el RUC o CI indicado |
| 400 | INVALID_JSON | Body ausente o JSON inválido |
| 400 | INVALID_FIELD | Campo no permitido o validación fallida |
| 400 | UPDATE_ERROR | Error genérico durante la actualización |
KUDE de factura o nota de crédito
GET /api/fe/invoice/{de_id}/kude-pdf
El KUDE es la representación gráfica oficial del Documento Electrónico en formato
PDF. El identificador en el path es el CDC (DE_id), el código de 44
dígitos asignado por SIFEN a cada documento electrónico aprobado. Para obtener el
DE_id de un documento, usar GET /api/fe/invoice/{numero}.
Devuelve directamente los bytes del PDF, con Content-Type: application/pdf y
Content-Disposition: attachment.
| Parámetro | Tipo | Descripción |
|---|---|---|
de_id | string | CDC de 44 dígitos del documento |
# Paso 1: obtener el CDC buscando la factura por numero.
resp = session.get(
f"{URL}/api/fe/invoice/001-001-0004707",
headers=headers
)
de_id = resp.json()["data"]["DE_id"] # CDC de 44 digitos
# Paso 2: descargar el KUDE en PDF. Este endpoint devuelve el binario,
# no JSON: hay que escribir resp.content a un archivo.
resp = session.get(
f"{URL}/api/fe/invoice/{de_id}/kude-pdf",
headers=headers
)
with open("kude.pdf", "wb") as f:
f.write(resp.content)import { writeFile } from "node:fs/promises";
// Paso 1: obtener el CDC buscando la factura por numero.
let resp = await fetch(`${URL}/api/fe/invoice/001-001-0004707`, { headers });
const { data } = await resp.json();
const deId = data.DE_id; // CDC de 44 digitos
// Paso 2: descargar el KUDE en PDF. Este endpoint devuelve el binario,
// no JSON: se escribe el arrayBuffer a un archivo.
resp = await fetch(`${URL}/api/fe/invoice/${deId}/kude-pdf`, { headers });
await writeFile("kude.pdf", Buffer.from(await resp.arrayBuffer()));using System.Text.Json.Nodes;
// Paso 1: obtener el CDC buscando la factura por numero.
var resp = await http.GetAsync($"{URL}/api/fe/invoice/001-001-0004707");
var deId = JsonNode.Parse(await resp.Content.ReadAsStringAsync())!["data"]!["DE_id"]!
.GetValue<string>(); // CDC de 44 digitos
// Paso 2: descargar el KUDE en PDF. Este endpoint devuelve el binario,
// no JSON: se escriben los bytes a un archivo.
resp = await http.GetAsync($"{URL}/api/fe/invoice/{deId}/kude-pdf");
await File.WriteAllBytesAsync("kude.pdf", await resp.Content.ReadAsByteArrayAsync());import java.nio.file.Path;
// Paso 1: obtener el CDC buscando la factura por numero.
HttpResponse<String> resp = http.send(
pedido.apply("/api/fe/invoice/001-001-0004707").GET().build(),
HttpResponse.BodyHandlers.ofString());
// CDC de 44 digitos dentro de "data"
String deId = resp.body().replaceAll("(?s).*\"DE_id\":\\s*\"([^\"]*)\".*", "$1");
// Paso 2: descargar el KUDE en PDF. Este endpoint devuelve el binario,
// no JSON: se guarda directo a un archivo.
http.send(pedido.apply("/api/fe/invoice/" + deId + "/kude-pdf").GET().build(),
HttpResponse.BodyHandlers.ofFile(Path.of("kude.pdf")));# Paso 1: obtener el CDC buscando la factura por numero.
curl -s -b cookies.txt "$URL/api/fe/invoice/001-001-0004707" \
-H "Authorization: Bearer $API_KEY" \
-H "X-Database: $DB"
# La respuesta incluye: "DE_id": "01800000011001001000000012024010112345678901"
# Paso 2: descargar el KUDE en PDF. Este endpoint devuelve el binario.
curl -s -b cookies.txt \
"$URL/api/fe/invoice/01800000011001001000000012024010112345678901/kude-pdf" \
-H "Authorization: Bearer $API_KEY" \
-H "X-Database: $DB" \
--output kude.pdf
| HTTP | Código | Descripción |
|---|---|---|
| 200 | — | PDF descargado correctamente |
| 404 | NOT_FOUND | No existe documento con ese CDC |
| 400 | DE_NO_XML | El documento existe pero aún no tiene XML generado |
| 500 | PDF_ERROR | Error al renderizar el PDF |
| 401 | UNAUTHORIZED | API Key inválida o ausente |
KUDE de nota de remisión
GET /api/fe/remision/{de_id}/kude-pdf
Idéntico al anterior, pero para notas de remisión electrónicas. El de_id se obtiene
desde GET /api/fe/remision/{numero}.
# El de_id se obtiene desde GET /api/fe/remision/{numero}.
resp = session.get(
f"{URL}/api/fe/remision/04800000011001001000000012024010112345678901/kude-pdf",
headers=headers
)
with open("kude_remision.pdf", "wb") as f:
f.write(resp.content)import { writeFile } from "node:fs/promises";
// El de_id se obtiene desde GET /api/fe/remision/{numero}.
const resp = await fetch(
`${URL}/api/fe/remision/04800000011001001000000012024010112345678901/kude-pdf`,
{ headers });
await writeFile("kude_remision.pdf", Buffer.from(await resp.arrayBuffer()));// El de_id se obtiene desde GET /api/fe/remision/{numero}.
var resp = await http.GetAsync(
$"{URL}/api/fe/remision/04800000011001001000000012024010112345678901/kude-pdf");
await File.WriteAllBytesAsync("kude_remision.pdf",
await resp.Content.ReadAsByteArrayAsync());import java.nio.file.Path;
// El de_id se obtiene desde GET /api/fe/remision/{numero}.
HttpResponse<Path> resp = http.send(
pedido.apply("/api/fe/remision/04800000011001001000000012024010112345678901/kude-pdf")
.GET().build(),
HttpResponse.BodyHandlers.ofFile(Path.of("kude_remision.pdf")));
System.out.println(resp.statusCode());# El de_id se obtiene desde GET /api/fe/remision/{numero}.
curl -s -b cookies.txt \
"$URL/api/fe/remision/04800000011001001000000012024010112345678901/kude-pdf" \
-H "Authorization: Bearer $API_KEY" \
-H "X-Database: $DB" \
--output kude_remision.pdf
En instalaciones con múltiples bases de datos, establecer la cookie de sesión antes de llamar al endpoint:
curl -c cookies.txt "$URL/web?db=$DB"
Usar esa cookie (-b cookies.txt) en todas las llamadas siguientes. No requiere
credenciales.
Respuestas y errores
Todas las respuestas comparten la misma envoltura: un campo success y, según el
caso, data o error.
Respuesta exitosa
{
"success": true, # Siempre true en respuestas exitosas
"data": {
# --- Identificacion ---
"id": 123, # ID interno de Element
"name": "001-001-0004707", # Numero de factura
# --- Estado contable ---
"state": "posted", # "draft", "posted" o "cancel"
# --- Estado electronico (campos DE del modulo eterp_de) ---
"DE_estado": "aprobado", # "pendiente", "aprobado",
# "rechazado" o "cancelado"
"DE_id": "01800...", # CDC: Codigo de Control
# (44 digitos, identificador
# unico del DE ante SIFEN)
"DE_xml": "base64_encoded_xml...",# XML completo del DE en
# Base64. Decodificar para
# obtener el XML original.
# Util para generar tickets.
"DE_url": "https://ekuatia.set.gov.py/...",
# URL de verificacion publica
"DE_mensaje": "Aprobado",
# Mensaje de respuesta SIFEN
# --- Montos ---
"amount_total": 150000, # Total con impuestos
"amount_residual": 0, # Saldo pendiente de cobro
"currency": "PYG", # Codigo de moneda
# --- Pago ---
"payment_state": "paid", # "not_paid", "partial",
# "paid", "in_payment"
"payment_term_id": 1, # ID del termino de pago
"payment_term_name": "Pago inmediato",
"payment_term_days": 0 # Total de dias del termino.
# 0 = contado, >0 = credito
}
}
Descripción de los campos más relevantes:
id/name: ID interno de Element y número de facturastate: estado contable (draft,posted,cancel)DE_estado: estado electrónico (pendiente,aprobado,rechazado,cancelado)DE_id: Código de Control (CDC) de 44 dígitos, identificador único del DE ante SIFENDE_xml: XML completo del DE codificado en Base64, decodificable para generar tickets impresosDE_url: URL pública de verificación en el portal ekuatia de la DNIT; solo tiene valor cuandoDE_estado = "aprobado"payment_term_days: 0 es contado, mayor a 0 es crédito
Respuesta de error
{
"success": false, # Siempre false en respuestas de error
"error": {
# Codigo de error programatico. Usar este campo para
# manejar errores en el codigo (switch/if).
# Ver tabla de codigos comunes mas abajo.
"code": "CREATE_ERROR",
# Mensaje legible describiendo el error.
# Puede contener detalles tecnicos de Element
# (ej: "El campo 'uom_id' con valor 99 no existe").
"message": "Descripcion del error"
}
}
Códigos de error comunes
| Código | HTTP | Descripción |
|---|---|---|
AUTH_MISSING | 401 | Falta el header Authorization |
AUTH_INVALID | 401 | API Key inválida |
API_MODE_DISABLED | 403 | Modo API no activo |
NOT_FOUND | 404 | Factura o nota de remisión no encontrada |
INVALID_JSON | 400 | Body JSON inválido |
INVALID_FIELD | 400 | Campo o valor inválido en el body |
CREATE_ERROR | 400 | Error al crear el documento |
UPDATE_ERROR | 400 | Error al actualizar un contacto |
INVALID_MOTIVO | 400 | Motivo de remisión inválido o falta descripción |
FALTA_DESCRIPCION_MOTIVO | 400 | motivo=99 sin descripción válida, de 5 a 60 caracteres |
INVALID_DISTANCIA | 400 | distancia_estimada fuera del rango 1 a 99999 |
FECHA_FIN_ANTES_INICIO | 400 | fin_estimado_traslado anterior al inicio |
INVALID_PICKING_TYPE | 400 | Falta warehouse_data |
INVALID_WAREHOUSE | 400 | Almacén sin tipo de salida configurado |
NO_SEQ | 400 | Falta secuencia_id o prefijo |
INVALID_PREFIX | 400 | Prefijo no encontrado en la configuración |
NO_CONFIG | 400 | No hay configuración DE activa para la compañía |
FALTA_CARRIER | 400 | Falta el campo carrier_data |
NO_LINES | 400 | lines vacío |
INVALID_LINE | 400 | quantity faltante o menor o igual a 0 |
NOT_NRE | 400 | El picking no es una nota de remisión |
DE_NO_XML | 400 | Nota de remisión sin XML generado |
INVALID_EMAIL | 400 | Correo electrónico inválido |
EMAIL_CONFIG_ERROR | 500 | Sin servidor de correo configurado |
EMAIL_ERROR | 500 | Error generando el PDF o enviando el correo |
CANCEL_ERROR | 400 | Nota de remisión no aprobada o motivo inválido |
Ejemplo completo
#!/usr/bin/env python3
"""Script minimo para emitir una factura electronica via API.
Requiere: pip install requests, y la URL base, la base de datos y la
API Key de su panel de configuracion."""
import requests
URL = "https://80200001.elementtech.com.py"
API_KEY = "tu_api_key"
DB = "80200001_test"
# Element vincula la cookie de sesion con la base de datos indicada.
s = requests.Session()
s.get(f"{URL}/web", params={"db": DB})
# Al recibir el JSON el servidor valida la API Key, resuelve el cliente
# (consultando SIFEN por RUC si hace falta), resuelve o crea el producto
# por default_code, crea la factura, la publica y devuelve sus datos.
resp = s.post(
f"{URL}/api/fe/invoice",
headers={
"Authorization": f"Bearer {API_KEY}",
"X-Database": DB,
"Content-Type": "application/json"
},
json={
"partner_id": "48213", # ID externo del cliente
"api_ruc": "80000001-1", # RUC para consulta SIFEN
"lines": [{
"product_id": "20458",
"api_product_data": {
"name": "Servicio",
"default_code": "SRV-2045",
"uom_id": 1, # Unidad de medida (Element ID)
"tax_id": 1 # Impuesto (Element ID)
},
"quantity": 1,
"price_unit": 100000 # Precio en guaranies
}]
}
)
result = resp.json()
if result.get("success"):
data = result["data"]
print(f"Factura creada: {data['name']}")
print(f"Estado DE: {data.get('DE_estado', 'N/A')}")
print(f"Total: {data['amount_total']} {data['currency']}")
else:
error = result["error"]
print(f"Error [{error['code']}]: {error['message']}")#!/usr/bin/env node
// Script minimo para emitir una factura electronica via API.
// Requiere Node 18 o superior, y la URL base, la base de datos y la
// API Key de su panel de configuracion.
const URL = "https://80200001.elementtech.com.py";
const API_KEY = "tu_api_key";
const DB = "80200001_test";
// Element vincula la cookie de sesion con la base de datos indicada.
const inicio = await fetch(`${URL}/web?db=${DB}`, { redirect: "manual" });
const cookie = inicio.headers.get("set-cookie")?.match(/session_id=[^;]+/)?.[0] ?? "";
// Al recibir el JSON el servidor valida la API Key, resuelve el cliente
// (consultando SIFEN por RUC si hace falta), resuelve o crea el producto
// por default_code, crea la factura, la publica y devuelve sus datos.
const resp = await fetch(`${URL}/api/fe/invoice`, {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"X-Database": DB,
"Content-Type": "application/json",
"Cookie": cookie
},
body: JSON.stringify({
partner_id: "48213", // ID externo del cliente
api_ruc: "80000001-1", // RUC para consulta SIFEN
lines: [{
product_id: "20458",
api_product_data: {
name: "Servicio",
default_code: "SRV-2045",
uom_id: 1, // Unidad de medida (Element ID)
tax_id: 1 // Impuesto (Element ID)
},
quantity: 1,
price_unit: 100000 // Precio en guaranies
}]
})
});
const result = await resp.json();
if (result.success) {
const data = result.data;
console.log(`Factura creada: ${data.name}`);
console.log(`Estado DE: ${data.DE_estado ?? "N/A"}`);
console.log(`Total: ${data.amount_total} ${data.currency}`);
} else {
console.log(`Error [${result.error.code}]: ${result.error.message}`);
}// Script minimo para emitir una factura electronica via API.
// Requiere .NET 8 o superior, y la URL base, la base de datos y la
// API Key de su panel de configuracion.
using System.Net;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
const string URL = "https://80200001.elementtech.com.py";
const string API_KEY = "tu_api_key";
const string DB = "80200001_test";
// Element vincula la cookie de sesion con la base de datos indicada.
var handler = new HttpClientHandler { CookieContainer = new CookieContainer() };
var http = new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(120) };
http.DefaultRequestHeaders.Add("Authorization", $"Bearer {API_KEY}");
http.DefaultRequestHeaders.Add("X-Database", DB);
await http.GetAsync($"{URL}/web?db={DB}");
// Al recibir el JSON el servidor valida la API Key, resuelve el cliente
// (consultando SIFEN por RUC si hace falta), resuelve o crea el producto
// por default_code, crea la factura, la publica y devuelve sus datos.
var factura = new
{
partner_id = "48213", // ID externo del cliente
api_ruc = "80000001-1", // RUC para consulta SIFEN
lines = new[]
{
new
{
product_id = "20458",
api_product_data = new
{
name = "Servicio",
default_code = "SRV-2045",
uom_id = 1, // Unidad de medida (Element ID)
tax_id = 1 // Impuesto (Element ID)
},
quantity = 1,
price_unit = 100000 // Precio en guaranies
}
}
};
var cuerpo = new StringContent(JsonSerializer.Serialize(factura), Encoding.UTF8,
"application/json");
var resp = await http.PostAsync($"{URL}/api/fe/invoice", cuerpo);
var result = JsonNode.Parse(await resp.Content.ReadAsStringAsync())!;
if (result["success"]!.GetValue<bool>())
{
var data = result["data"]!;
Console.WriteLine($"Factura creada: {data["name"]}");
Console.WriteLine($"Estado DE: {data["DE_estado"]}");
Console.WriteLine($"Total: {data["amount_total"]} {data["currency"]}");
}
else
{
var error = result["error"]!;
Console.WriteLine($"Error [{error["code"]}]: {error["message"]}");
}// Script minimo para emitir una factura electronica via API.
// Requiere Java 17 o superior, sin dependencias externas. Necesita la URL
// base, la base de datos y la API Key de su panel de configuracion.
import java.net.CookieManager;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
String URL = "https://80200001.elementtech.com.py";
String API_KEY = "tu_api_key";
String DB = "80200001_test";
// Element vincula la cookie de sesion con la base de datos indicada.
HttpClient http = HttpClient.newBuilder()
.cookieHandler(new CookieManager())
.connectTimeout(Duration.ofSeconds(120))
.build();
http.send(HttpRequest.newBuilder(URI.create(URL + "/web?db=" + DB)).build(),
HttpResponse.BodyHandlers.discarding());
// Al recibir el JSON el servidor valida la API Key, resuelve el cliente
// (consultando SIFEN por RUC si hace falta), resuelve o crea el producto
// por default_code, crea la factura, la publica y devuelve sus datos.
String factura = """
{
"partner_id": "48213",
"api_ruc": "80000001-1",
"lines": [{
"product_id": "20458",
"api_product_data": {
"name": "Servicio",
"default_code": "SRV-2045",
"uom_id": 1,
"tax_id": 1
},
"quantity": 1,
"price_unit": 100000
}]
}
""";
HttpResponse<String> resp = http.send(
HttpRequest.newBuilder(URI.create(URL + "/api/fe/invoice"))
.header("Authorization", "Bearer " + API_KEY)
.header("X-Database", DB)
.header("Content-Type", "application/json")
.timeout(Duration.ofSeconds(120))
.POST(HttpRequest.BodyPublishers.ofString(factura))
.build(),
HttpResponse.BodyHandlers.ofString());
System.out.println(resp.body());#!/usr/bin/env bash
# Script minimo para emitir una factura electronica via API.
# Necesita la URL base, la base de datos y la API Key de su panel.
set -eu
URL="https://80200001.elementtech.com.py"
API_KEY="tu_api_key"
DB="80200001_test"
# Element vincula la cookie de sesion con la base de datos indicada.
curl -s -c cookies.txt -o /dev/null "$URL/web?db=$DB"
# Al recibir el JSON el servidor valida la API Key, resuelve el cliente
# (consultando SIFEN por RUC si hace falta), resuelve o crea el producto
# por default_code, crea la factura, la publica y devuelve sus datos.
curl -s -b cookies.txt -X POST "$URL/api/fe/invoice" \
-H "Authorization: Bearer $API_KEY" \
-H "X-Database: $DB" \
-H "Content-Type: application/json" \
-d '{
"partner_id": "48213",
"api_ruc": "80000001-1",
"lines": [{
"product_id": "20458",
"api_product_data": {
"name": "Servicio",
"default_code": "SRV-2045",
"uom_id": 1,
"tax_id": 1
},
"quantity": 1,
"price_unit": 100000
}]
}'
Ver los resultados
Una vez ejecutado el script de creación de facturas, conviene verificar que la factura se haya generado correctamente en Element ERP. A continuación se describe el proceso para acceder a la factura creada y revisar sus elementos electrónicos.
Acceso al módulo de facturación
Para visualizar las facturas generadas mediante la API, siga estos pasos:
- Inicie sesión en Element ERP con sus credenciales.
- Navegue al menú principal y seleccione el módulo Facturación.
- En el submenú, haga clic en Clientes y luego Facturas.
Localización de la factura
Una vez en el listado de facturas:
- Busque la factura recién creada por su número, retornado por la API.
- Utilice los filtros para localizar facturas por fecha, cliente o estado.
- Haga clic en la factura para abrirla en modo de visualización.
Las facturas generadas vía API aparecerán con estado Publicado y el término de pago configurado será visible en el campo correspondiente.
Código QR y XML
Dentro de la vista detallada de la factura podrá acceder a los elementos de facturación electrónica.
Visualización del código QR
- En la vista de la factura, busque la sección Facturación Electrónica.
- El código QR se mostrará automáticamente en la factura.
- Puede hacer clic en el QR para ampliar o descargar la imagen.
Acceso al XML generado
- Localice el campo DE_xml en la vista de la factura.
- Haga clic en el botón Ver XML o el enlace correspondiente.
- El XML se abrirá en una nueva ventana o se descargará automáticamente.
El XML generado por el sistema contiene todos los datos fiscales y comerciales del documento electrónico: emisor, receptor, ítems, totales, CDC. Este XML puede utilizarse como fuente de datos para generar el ticket o comprobante impreso de la factura electrónica desde su sistema externo, extrayendo los campos necesarios para armar el formato de impresión deseado (ticket térmico, A4, media carta) sin necesidad de consultar datos adicionales al servidor.
El campo DE_xml se retorna codificado en Base64 tanto en la respuesta de creación
de la factura como al consultar una factura existente mediante el endpoint
GET /api/fe/invoice/{numero}.
El campo DE_url contiene la URL pública del portal ekuatia de la DNIT que permite
verificar la autenticidad del documento electrónico. Este enlace es único por documento y
puede compartirse directamente con el cliente como comprobante de validez ante la autoridad
tributaria.
El campo solo tiene valor cuando DE_estado = "aprobado". Se retorna tanto en la
respuesta de creación de la factura (POST /api/fe/invoice) como al consultar la
factura con GET /api/fe/invoice/{numero}.
Elementos clave a verificar:
- Estado: la factura debe estar en estado Publicado
- Código QR: visible y escaneable
- XML: archivo descargable con estructura correcta
- URL del QR: el campo
DE_urldebe contener un enlace válido
Verificación de integridad
Para asegurar que la factura fue creada correctamente mediante la API, verifique los siguientes elementos:
- Datos del cliente: correctamente asignados según el
partner_id - Líneas de factura: productos, cantidades y precios coinciden
- Término de pago: refleja el
payment_term_idconfigurado - Totales: montos calculados correctamente
- Estado: factura en estado Publicado, no en borrador
- Elementos electrónicos: QR y XML generados exitosamente
# Obtener sesión curl -s -c cookies.txt "$URL/web?db=$DB" > /dev/null # Crear factura curl -X POST "$URL/api/fe/invoice" \ -b cookies.txt \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '
'
{
"partner_id": "", // Código del cliente en tu sistema
"api_partner_data": {
"name": "", // Nombre completo del cliente
"ruc": "" // RUC del cliente (ej: 80012345-6)
},
"lines": [{
"product_id": "", // Código del producto en tu sistema
"api_product_data": {
"name": "", // Nombre del producto o servicio
"default_code": "", // Código interno del producto
"uom_id": 1, // Unidad de medida (1 = Unidad)
"tax_id": 1 // IVA aplicable (1 = 10%)
},
"quantity": , // Cantidad a facturar
"price_unit": // Precio unitario en Guaraníes
}]
}
API de Facturación Electrónica
para Paraguay
Emite documentos conformes a la DNIT desde tu propio sistema.
Completa tus datos en el código de ejemplo y emite tu primera factura en 60 segundos.
Un plan simple y transparente
Sin sorpresas. Sin costos ocultos. Solo facturación ilimitada.
Plan Único
- Documentos ilimitados
- Todos los tipos de documentos
- Facturas, notas de crédito y cancelaciones
- Dashboard completo
- Soporte técnico prioritario
- Actualizaciones automáticas
- Sin costos ocultos
¿Por qué nuestra API?
Cumple con todas las normativas de la DNIT y simplifica tu proceso de facturación
Integración Rápida
Implementa facturación electrónica en tu sistema en menos de 1 hora. Documentación clara y ejemplos prácticos en múltiples lenguajes.
Datos protegidos
Tráfico cifrado y una base de datos por cliente: tus documentos no conviven con los de nadie más.
Conforme a la DNIT
Genera documentos electrónicos: facturas, notas de crédito y eventos de cancelación.
Reportes en Tiempo Real
Consulta el estado de tus documentos, obtén estadísticas y genera reportes completos desde nuestro dashboard.
Infraestructura propia
Servidores en Paraguay, monitoreados por el mismo equipo que te da soporte.
Soporte Local
Equipo de soporte local disponible 24/7 para ayudarte con cualquier consulta o problema técnico.
Dónde estamos hoy
Lo que llevamos emitido y el soporte que acompaña
<1h
Tiempo de integración
24/7
Soporte disponible
+10.000
Facturas emitidas
Preguntas Frecuentes
Es el sistema de emisión de documentos fiscales electrónicos (e-Kuatia) establecido por la DNIT. Nuestra API te permite generar estos documentos de forma automática y enviarlos.
Puedes emitir facturas, notas de crédito y eventos de cancelación.
Nuestra API está diseñada para ser simple e intuitiva. Si sabes hacer peticiones HTTP básicas, puedes integrar nuestra solución.
Contamos con soporte técnico disponible 24/7 para ayudarte.
No. Con nuestro plan único puedes emitir documentos ilimitados sin cargos adicionales.
El alta te pide tres archivos
Los subes y el sistema arma tu entorno solo: base de datos propia, subdominio y las secuencias de tu timbrado. Sin proyecto de implementación y sin esperar a nadie.
Crear mi cuenta¿Primero quieres ver cómo se integra?
- Timbrado
- El PDF
FORM.364-3que emite la DNIT. De ahí salen tu RUC, tu razón social y tus puntos de expedición: no tipeas nada. - Firma electrónica
- Tu certificado
.p12y su contraseña. Te avisamos antes de que venza. - Código CSC
- El código de seguridad del contribuyente. Para facturar de verdad hace falta el de producción, no el de prueba.