Facturación electrónica (SIFEN) por API — guía para sistemas externos
Para empresas que ya tienen su propio sistema (ERP, facturador, e-commerce, software a medida) y solo necesitan emitir factura electrónica en Paraguay. Su sistema sigue gestionando ventas, stock y cobros; CDEpos se encarga de la parte fiscal: numera con el timbrado electrónico, firma con el certificado del contribuyente, envía al SET (SIFEN), devuelve el CDC + QR + KuDE (PDF) + XML, consulta la aprobación, anula, emite notas de crédito y envía el comprobante al cliente por email.
Base URL: https://app.micdepos.com/connector/api · Formato: JSON · Auth: OAuth2 (Bearer).
Documentación general de la API: https://app.micdepos.com/doc.
1. Cómo funciona (resumen)
Su sistema CDEpos SET / SIFEN
────────── ────── ───────────
POST /sell {cliente, ítems, pago, crea la venta, numera (timbrado
sifen_emit: true} ───► electrónico), arma el XML del DE,
lo firma y lo envía por lote ───► recibe el lote (0300)
◄─── 200 {id, invoice_no,
sifen: {cdc, qr}}
cron cada 10 min consulta el lote ───► aprobado / rechazado
◄── webhook sifen.aprobado (POST firmado a su URL) o GET /sell/{id}/sifen/status
GET /sell/{id}/pdf ───► KuDE (PDF) / XML / email al cliente
- Una sola llamada crea la factura y la emite (
sifen_emit: true). - La aprobación del SET es asíncrona: normalmente en minutos. CDEpos le
avisa por webhook (§5.1) o se consulta con
GET /sell/{id}/sifen/status. El KuDE es válido desde que tiene CDC; el QR apunta aekuatia.set.gov.py. - No hace falta cargar productos ni clientes por adelantado: la venta puede traer el cliente por RUC/CI y los ítems por SKU, y CDEpos los crea la primera vez (ver §4).
- La numeración (
001-001-0000123) la asigna CDEpos según el timbrado electrónico: guardeid,invoice_noycdcen su sistema.
2. Requisitos previos (una sola vez)
Lo configura CDEpos junto con el contribuyente:
- Negocio en CDEpos con el módulo de factura electrónica activo
(
sifen_activeen el negocio y en la sucursal emisora). - Certificado digital (.p12 del contribuyente, emitido por una PSC habilitada) cargado en la sucursal, con su contraseña.
- Timbrado electrónico vigente (esquema de numeración electrónico con timbrado, fecha de inicio/fin, establecimiento y punto de expedición) asignado a la sucursal.
- Ambiente:
testpara homologar,produccioncuando el SET habilita al contribuyente. (Endemoel XML se firma localmente y no se envía.) - Tasas de IVA del negocio: IVA 10%, IVA 5% y Exenta (vienen por defecto).
- Usuario API con permisos:
sell.create,customer.view,customer.create(alta de clientes),product.create(alta automática de ítems por SKU),invoice.view_pdf(emitir / KuDE / XML),sell.view. - Credenciales OAuth (Client ID, Client Secret, usuario y contraseña API): las genera el administrador del negocio en CDEpos → Conector / Clientes API. El secret se muestra una sola vez.
3. Autenticación
curl -X POST https://app.micdepos.com/oauth/token \
-H "Content-Type: application/json" \
-d '{
"grant_type": "password",
"client_id": "<CLIENT_ID>",
"client_secret": "<CLIENT_SECRET>",
"username": "<USUARIO_API>",
"password": "<PASSWORD_API>",
"scope": "*"
}'
Respuesta: {"token_type":"Bearer","expires_in":...,"access_token":"...","refresh_token":"..."}.
Enviar en cada llamada Authorization: Bearer <access_token> y Accept: application/json.
Renovar con grant_type: refresh_token antes de que venza.
4. Emitir una factura electrónica (una llamada)
POST /connector/api/sell — el cuerpo es un arreglo sells (normalmente con una venta).
{
"sells": [
{
"location_id": 1,
"status": "final",
"idempotency_key": "ERP-FAC-2026-000123",
"sifen_emit": true,
"transaction_date": "2026-08-19 10:30:00",
"contact": {
"ruc": "80012345-6",
"name": "Distribuidora Ejemplo SA",
"email": "[email protected]",
"mobile": "0981123456",
"address": "Av. Monseñor Rodríguez 123, Ciudad del Este"
},
"products": [
{ "sku": "SRV-MANT-01", "name": "Servicio de mantenimiento", "quantity": 1,
"unit_price_inc_tax": 550000, "tax_rate": 10 },
{ "sku": "LIB-0001", "name": "Libro técnico", "quantity": 2,
"unit_price_inc_tax": 120000, "tax_rate": 5 },
{ "sku": "EXE-0001", "name": "Ítem exento", "quantity": 1,
"unit_price_inc_tax": 30000, "tax_rate": 0 }
],
"payments": [
{ "method": "cash", "amount": 820000 }
],
"sale_note": "Pedido ERP #98765"
}
]
}
Campos clave
| Campo | Obligatorio | Descripción |
|---|---|---|
location_id |
sí | Sucursal emisora (la que tiene certificado y timbrado). GET /business-location lista las sucursales. |
status |
sí | final. |
idempotency_key |
recomendado | Id único de la factura en su sistema (≤ 64). Si reintenta con la misma key, CDEpos devuelve la misma venta (duplicate: true) en vez de crear otra. La key es de cada comercio: la misma key en otro comercio es otra venta. |
sifen_emit |
sí (FE) | true → emite la factura electrónica en la misma llamada. Implica document_type: "factura_electronica". |
document_type |
opcional | factura_electronica (se asume con sifen_emit) / comprobante (interno, sin SIFEN). |
transaction_date |
opcional | Y-m-d H:i:s; por defecto ahora. |
contact |
sí* | Cliente por documento — ver abajo. Alternativa: contact_id de un cliente ya cargado. |
products[] |
sí | Ítems — ver abajo. |
payments[] |
opcional | Pagos. Si cubren el total → factura contado; si no hay pagos o son parciales → crédito (la condición de pago del DE sale de acá). method: cash, card, bank_transfer, cheque, other. |
sale_note |
opcional | Observación (aparece en el KuDE). |
discount_type / discount_amount |
opcional | Descuento global (fixed o percentage). |
Cabecera Idempotency-Key. Desde el 15/10/2026, si el cuerpo no trae idempotency_key y el pedido trae una sola venta, la cabecera Idempotency-Key también se guarda con la venta como su clave. Deduplica de forma persistente, por comercio, y no vence. Antes la cabecera sólo deduplicaba 24 h y se perdía en cada actualización del servidor. Si manda las dos, manda la del cuerpo.
- Una cabecera de más de 64 caracteres no se guarda con la venta: sólo deduplica 24 h, como antes.
- No reutilice una clave para otra venta:
- dentro de las 24 h, con otro cuerpo, recibe
422("Idempotency-Key reutilizado con body distinto"); - después, si la clave vino de la cabecera, CDEpos compara sucursal, cliente (si lo manda) y cantidad de productos con la venta guardada. Si no coinciden,
422 CLAVE_YA_USADAy la venta no se crea. Si coinciden, la toma como un reintento y devuelve la venta guardada conduplicate: true.
- dentro de las 24 h, con otro cuerpo, recibe
contact — cliente por documento
| Campo | Descripción |
|---|---|
ruc (o doc) |
RUC con DV (80012345-6) o CI (4671379). Si ya existe un cliente con ese documento se reutiliza; si no, se crea con los datos enviados. |
name |
Nombre / razón social. Obligatorio solo si el documento es nuevo. |
email, mobile, address, city |
Opcionales; si el cliente existe y no los tenía, se completan. |
sin_nombre: true |
Consumidor final "Sin nombre" (cliente por defecto del negocio). |
id |
Alternativa: id de un contacto existente. |
Para validar un RUC contra el SET antes de facturar: GET /connector/api/contactapi-lookup?doc=80012345-6.
products[] — ítems
| Campo | Descripción |
|---|---|
sku |
Código del ítem en su sistema. Si existe en el catálogo de CDEpos se usa; si no, se crea automáticamente (ítem sin control de stock) con name, el IVA y el precio enviados. |
name |
Descripción del ítem (sale en el DE y en el KuDE). Obligatoria la primera vez que aparece un sku. |
quantity |
Cantidad (decimal permitido). |
unit_price_inc_tax |
Precio unitario con IVA (el habitual en Paraguay). CDEpos calcula el neto. |
unit_price |
Alternativa: precio unitario sin IVA. |
tax_rate |
10, 5 o 0 (exento). Alternativa: tax_rate_id (ids en GET /tax). Sin tax_rate ni tax_rate_id la línea va exenta. |
discount_amount / discount_type |
Descuento por línea. |
product_id + variation_id |
Alternativa clásica: ítem ya cargado en CDEpos (GET /product). |
Respuesta (200)
[
{
"id": 614885,
"invoice_no": "001-001-0000123",
"transaction_date": "2026-08-19 10:30:00",
"final_total": "820000.0000",
"payment_status": "paid",
"contact_id": 8415,
"sifen_cdc": "01023665700001001000001212026081910119614641",
"sifen_qr": "https://ekuatia.set.gov.py/consultas/qr?nVersion=150&Id=0102366570...",
"sifen_process": "lote_recibido",
"sifen": {
"emitted": true,
"cdc": "01023665700001001000001212026081910119614641",
"qr": "https://ekuatia.set.gov.py/consultas/qr?...",
"process": "lote_recibido",
"links": {
"status": "https://app.micdepos.com/connector/api/sell/614885/sifen/status",
"pdf": "https://app.micdepos.com/connector/api/sell/614885/pdf",
"xml": "https://app.micdepos.com/connector/api/sell/614885/sifen/xml"
}
},
"invoice_url": "https://app.micdepos.com/invoice/…"
}
]
- Si la emisión falla, la venta igual queda creada y
sifen.emitted = falseconsifen.error.code/message(ver §8). Se reintenta conPOST /sell/{id}/sifen/emitsin volver a crear la venta. - Reintento con la misma
idempotency_key→ misma venta,duplicate: true, mismo CDC. En la respuesta repetida sólo se garantizanid,invoice_no,invoice_urlypayment_link(iguales a la primera); para el estado SIFEN consulteGET /sell/{id}/sifen/status. - Si el reintento llega mientras la primera todavía se está guardando, espera
hasta 10 s; si sigue en curso recibe
503OPERACION_EN_CURSO(ver §8).
5. Consultar el estado SIFEN
GET /connector/api/sell/{id}/sifen/status
{ "data": {
"transaction_id": 614885,
"invoice_no": "001-001-0000123",
"sifen_cdc": "0102…",
"sifen_process": "aprobado_set",
"ambiente": "produccion",
"ultimo_envio": { "estado": "APROBADO", "cod_res_envio": "0260",
"msg_res_envio": "Autorización del DE satisfactoria", "intentos": 1 },
"qr_url_set": "https://ekuatia.set.gov.py/consultas/qr?nVersion=150&Id=0102…",
"email": { "state": "sent" }
} }
sifen_process |
Significado | Qué hacer |
|---|---|---|
generado |
XML firmado (ambiente demo) | — |
lote_recibido |
El SET recibió el lote (cód. 0300); procesando | Consultar de nuevo en 1–10 min |
aprobado_set |
Aprobada por el SET | Listo |
rechazado_set |
Rechazada por el SET (msg_res_envio dice por qué) |
Corregir y emitir una nueva; la rechazada no es válida |
error_envio |
No se pudo enviar (red/SET caído) | POST /sell/{id}/sifen/retry |
Sugerencia: consultar cada 1–2 minutos hasta estado final (el proceso interno revisa los lotes cada 10 minutos). Una factura con CDC ya puede imprimirse y entregarse; el SET la valida en ese lapso. Mejor aún: suscribir un webhook (§5.1) y no consultar.
5.1 Webhooks (recomendado): CDEpos le avisa a su sistema
En vez de consultar el estado, registre una URL y CDEpos le hace un POST
cuando el documento cambia de estado:
| Evento | Cuándo |
|---|---|
sifen.aprobado |
El SET aprobó la factura / nota de crédito |
sifen.rechazado |
El SET la rechazó (ultimo_envio.msg_res_envio explica por qué) |
sifen.error |
No se pudo enviar al SET (se reintenta; si persiste, POST /sell/{id}/sifen/retry) |
sifen.anulado |
El evento de anulación fue aceptado por el SET |
ping |
Prueba manual (POST /sifen/webhooks/{id}/test) |
Registrar (usuario administrador del negocio o con business_settings.access):
curl -X POST https://app.micdepos.com/connector/api/sifen/webhooks \
-H "Authorization: Bearer <TOKEN>" -H "Content-Type: application/json" \
-d '{ "url": "https://mi-erp.com/webhooks/cdepos", "events": ["sifen.aprobado","sifen.rechazado","sifen.error","sifen.anulado"], "description": "ERP producción" }'
Respuesta: {"data": {"id": 1, "url": "...", "secret": "whsec_…"}} — guarde el
secret: se muestra una sola vez (se puede rotar con
PUT /sifen/webhooks/{id} { "rotate_secret": true }). Máximo 5 webhooks por
negocio; la URL debe ser https pública.
Lo que recibe su endpoint (POST, Content-Type: application/json):
X-CDEpos-Event: sifen.aprobado
X-CDEpos-Delivery: 3f0c…-uuid (id único de la entrega; úselo para no procesar dos veces)
X-CDEpos-Timestamp: 1755610000
X-CDEpos-Signature: sha256=<HMAC-SHA256 del cuerpo crudo con el secret>
User-Agent: CDEpos-Webhooks/1.0
{
"id": "3f0c…", "event": "sifen.aprobado", "created_at": "2026-08-19T10:42:00-03:00",
"business_id": 12,
"data": {
"transaction_id": 614885, "type": "sell",
"invoice_no": "001-001-0000123", "idempotency_key": "ERP-FAC-2026-000123",
"transaction_date": "2026-08-19 10:30:00", "final_total": 820000, "payment_status": "paid",
"cdc": "0102366570…", "qr": "https://ekuatia.set.gov.py/consultas/qr?…", "sifen_process": "aprobado_set",
"contact": { "id": 8415, "name": "Distribuidora Ejemplo SA", "tax_number": "80012345-6" },
"ultimo_envio": { "tipo": "ASYNC", "estado": "APROBADO", "cod_res_envio": "0260", "msg_res_envio": "Autorización del DE satisfactoria" },
"links": { "status": "…/sell/614885/sifen/status", "pdf": "…/sell/614885/pdf", "xml": "…/sell/614885/sifen/xml" }
}
}
type = sell (factura) o sell_return (nota de crédito). idempotency_key
es la clave que usted mandó al crear la venta: con eso enlaza el aviso con su
registro sin guardar ids ajenos.
Verificar la firma (obligatorio): calcule HMAC-SHA256(secret, cuerpo crudo)
y compárelo con X-CDEpos-Signature (sin sha256=). Ejemplo PHP:
$raw = file_get_contents('php://input');
$ok = hash_equals('sha256='.hash_hmac('sha256', $raw, $SECRET), $_SERVER['HTTP_X_CDEPOS_SIGNATURE'] ?? '');
Responder 2xx rápido (< 8 s). Cualquier otra respuesta o timeout se
reintenta con espera creciente: 1 min, 5 min, 30 min, 2 h, 6 h, 12 h (6 intentos).
Un mismo evento de una misma factura se entrega una sola vez por webhook,
pero diseñe su receptor idempotente por X-CDEpos-Delivery.
Gestión: GET /sifen/webhooks · PUT /sifen/webhooks/{id} (url, events,
is_active, rotate_secret) · DELETE /sifen/webhooks/{id} ·
POST /sifen/webhooks/{id}/test (ping) · GET /sifen/webhooks/{id}/deliveries
(historial con estado, intentos y último error) ·
POST /sifen/webhooks/{id}/deliveries/{deliveryId}/retry.
5.2 Reconciliación
Para cruzar su sistema con CDEpos (cierre del día, reproceso tras una caída):
GET /connector/api/sell?idempotency_key=ERP-FAC-2026-000123→ la venta que corresponde a su clave (devuelveid,invoice_no,sifen_cdc,sifen_process, líneas y pagos).GET /connector/api/sell?sifen_process=rechazado_set,error_envio&start_date=2026-08-01&end_date=2026-08-31→ documentos con problemas en el período. Tambiénsifen_cdc=einvoice_no=.- Todas las ventas devuelven
idempotency_key,sifen_cdc,sifen_qrysifen_processen su JSON.
6. Documentos
| Acción | Endpoint |
|---|---|
| KuDE (PDF) oficial | GET /connector/api/sell/{id}/pdf → application/pdf |
| XML firmado | GET /connector/api/sell/{id}/sifen/xml |
| Enviar por email al cliente (KuDE + XML adjuntos) | POST /connector/api/sell/{id}/sifen/email { "to_email": "...", "subject": "..." } |
| Detalle de la venta (líneas, pagos, ids) | GET /connector/api/sell/{id} |
| Listado | GET /connector/api/sell?start_date=…&end_date=… |
El negocio también puede activar el email automático al aprobarse el DE (sin llamadas de su parte).
7. Anulación y notas de crédito
- Anular (evento de cancelación):
POST /connector/api/sell/{id}/sifen/cancel{ "motivo": "Error en los datos del cliente" }(mínimo 10 caracteres). Reglas del SET: dentro de las 48 h de la aprobación; después corresponde nota de crédito. - Nota de crédito electrónica (devolución total/parcial):
POST /connector/api/sell-return
Los{ "transaction_id": 614885, "products": [ { "sell_line_id": 990001, "quantity": 1, "unit_price_inc_tax": 550000 } ], "sifen_motivo_nc_nd": 1 }sell_line_idsalen deGET /sell/{id}. La NC se emite asociada al CDC de la factura y se consulta igual que una venta. Motivos:GET /sifen-catalogs/motivos-nc.
8. Errores
| HTTP | Caso | Detalle |
|---|---|---|
| 401 | Token vencido / inválido | Renovar token |
| 403 | Permiso faltante | El mensaje dice cuál (sell.create, invoice.view_pdf, …) |
| 422 | Validación | message legible: products[0].sku 'X' no existe… y no vino "name", tax_rate: el negocio no tiene una tasa de IVA 7%, contact: el documento … no está registrado y no vino "name", idempotency_key: máximo 64 caracteres |
422 CLAVE_YA_USADA |
La key ya está registrada: en otra operación del comercio (p. ej. una compra), en una venta fuera del alcance del usuario (otra sucursal, o de otro cajero con "ver sólo propias"), o, con la key de la cabecera, en una venta con otra sucursal, cliente o cantidad de productos | La venta no se creó. Revisar en Ventas antes de cargarla de nuevo |
503 OPERACION_EN_CURSO |
Otro pedido con la misma Idempotency-Key se está procesando todavía |
Trae Retry-After (segundos). Reintentar con la misma key después de ese tiempo: devuelve el resultado del primero, no crea otra venta. Vale para todo POST/PUT/DELETE con la cabecera |
| 429 | Límite de pedidos por minuto | Trae Retry-After. Los reintentos con la misma key también cuentan |
200 + sifen.error |
La venta se creó pero no se emitió | Códigos: BUSINESS_NO_SIFEN, LOCATION_NO_SIFEN (sucursal sin FE), NO_CERT (sin certificado), TIMBRADO_VENCIDO, LAYOUT_NOT_KUDE (la venta no fue ruteada como FE: faltó sifen_emit/document_type o la config SIFEN de la sucursal está incompleta), XML_GEN_FAIL, EMIT_EXCEPTION |
4xx/5xx en POST /sell |
La venta no se creó | Reintentar con la misma idempotency_key es seguro |
9. Buenas prácticas
- Usar siempre
idempotency_key(el número interno de su factura/pedido) y reintentar con la misma ante timeouts. - Guardar
id,invoice_no,sifen_cdcysifen_qren su sistema; mostrar el QR / linkqr_url_seten sus comprobantes. - Enviar el RUC con dígito verificador y el nombre como figura en el SET
(
contactapi-lookuplo consulta); para consumidor final usarsin_nombre. - Mandar precios con IVA (
unit_price_inc_tax) y la tasa (tax_rate): así el DE refleja exactamente lo que cobró. - Homologar primero en ambiente test (mismos endpoints, la sucursal en
sifen_ambiente = test), luego pasar a producción. - Suscribir un webhook (§5.1) para enterarse de
aprobado_set/rechazado_setsin consultar; atender los rechazos el mismo día. Si no puede exponer una URL, consulte el estado hasta el final. - No borrar facturas: si hubo error, anular (≤ 48 h) o nota de crédito.
10. Checklist de salida a producción
- Certificado de producción cargado en la sucursal y contraseña válida.
- Timbrado electrónico vigente con establecimiento/punto de expedición correctos.
-
sifen_ambiente = produccionen la sucursal;sifen_activeen negocio y sucursal. - Usuario API con los permisos del §2 y credenciales OAuth entregadas.
- Prueba real: 1 factura contado + 1 crédito + 1 nota de crédito →
aprobado_set. - KuDE descargado por
GET /sell/{id}/pdfy QR verificado en ekuatia.set.gov.py. - Email automático al cliente activado (opcional).
Referencia rápida de endpoints
| Método | Ruta | Uso |
|---|---|---|
| POST | /oauth/token |
Token OAuth |
| POST | /connector/api/sell |
Crear + emitir factura (sifen_emit) |
| GET | /connector/api/sell/{id} |
Detalle (líneas, pagos, CDC) |
| GET | /connector/api/sell/{id}/sifen/status |
Estado SIFEN |
| POST | /connector/api/sell/{id}/sifen/emit |
Emitir (si no se emitió en la creación) |
| POST | /connector/api/sell/{id}/sifen/retry |
Reintentar envío con error |
| GET | /connector/api/sell/{id}/pdf |
KuDE PDF |
| GET | /connector/api/sell/{id}/sifen/xml |
XML firmado |
| POST | /connector/api/sell/{id}/sifen/email |
Enviar al cliente |
| POST | /connector/api/sell/{id}/sifen/cancel |
Anular (evento SET) |
| POST | /connector/api/sell-return |
Nota de crédito electrónica |
| GET | /connector/api/sell?idempotency_key= |
Reconciliación por su clave (también sifen_cdc, sifen_process, invoice_no) |
| GET/POST | /connector/api/sifen/webhooks |
Listar / registrar webhook (secret una sola vez) |
| PUT/DELETE | /connector/api/sifen/webhooks/{id} |
Editar (url, events, is_active, rotate_secret) / borrar |
| POST | /connector/api/sifen/webhooks/{id}/test |
Ping de prueba |
| GET | /connector/api/sifen/webhooks/{id}/deliveries |
Historial de entregas (+ /{deliveryId}/retry) |
| GET | /connector/api/contactapi-lookup?doc= |
Consultar RUC/CI en el SET |
| GET | /connector/api/tax |
Tasas de IVA (ids) |
| GET | /connector/api/business-location |
Sucursales |
| GET | /connector/api/sifen-catalogs |
Catálogos SIFEN (departamentos, ciudades, motivos NC…) |
CDEpos — CDE Online · Ciudad del Este, Paraguay · soporte: [email protected]