API: guía para desarrolladores
Referencia de la API pública v1 de ElERP para quien conecta otro sistema. Si eres el dueño del negocio y buscas cómo crear la clave, empieza por API e integraciones.
Lo que necesitas
- La dirección de la API de la empresa (aparece en Configuración › Integraciones), por ejemplo
https://elerp.tech/v1. - Una clave de API (
elerp_sk_…) con los permisos que tu integración necesita. La crea la Dueña de la empresa.
Llama a la API desde tu backend, nunca desde JavaScript en el navegador ni desde una app móvil: la clave quedaría a la vista. La API no habilita CORS a propósito. Guárdala en una variable de entorno o en un gestor de secretos, no en el código.
Primera llamada
La clave va en la cabecera Authorization: Bearer <clave>. También se acepta X-API-Key: <clave>. No hay que mandar la empresa: sale de la clave.
GET /v1/conexion confirma que la clave funciona y dice de qué empresa es, qué permisos tiene y a qué sedes accede:
curl -s https://elerp.tech/v1/conexion \
-H "Authorization: Bearer $ELERP_API_KEY"
{
"empresa": { "id": "emp_…", "nombre": "Bodega La Cima, C.A.", "rif": "J-12345678-4" },
"clave": { "nombre": "Tienda web", "permisos": ["productos:leer", "pedidos:leer", "pedidos:escribir"],
"sedeId": "sede_…", "expira": "" },
"sedes": [ { "id": "sede_…", "nombre": "Sede Principal" } ],
"moneda": "VES",
"limite": { "peticionesPorMinuto": 120 },
"version": "v1"
}
Python
import os, requests
API = "https://elerp.tech/v1"
H = {"Authorization": f"Bearer {os.environ['ELERP_API_KEY']}"}
r = requests.get(f"{API}/productos", headers=H, params={"activo": "true", "porPagina": 200})
r.raise_for_status()
for p in r.json()["datos"]:
print(p["sku"], p["nombre"], p["precio"], p["moneda"])
JavaScript (Node.js 18 o superior)
const API = 'https://elerp.tech/v1'
const H = { Authorization: `Bearer ${process.env.ELERP_API_KEY}` }
const r = await fetch(`${API}/existencias?sku=HAR-001`, { headers: H })
if (!r.ok) throw new Error((await r.json()).error.mensaje)
const { datos } = await r.json()
console.log(datos) // [{ sku, nombre, sedeId, cantidad, apartado, disponible }]
Reglas generales
Listas y paginación
Toda lista responde con el mismo sobre. Pide páginas con pagina (desde 1) y porPagina (por defecto 50, máximo 200), y sigue mientras hayMas sea true:
{ "datos": [ … ], "pagina": 1, "porPagina": 50, "total": 132, "hayMas": true }
Fechas
Los filtros desde y hasta van como AAAA-MM-DD, ambos incluidos, y se interpretan en hora de Venezuela: una factura de las 9 de la noche del lunes entra en desde=…lunes. Las fechas de las respuestas son instantes RFC 3339 en UTC.
Límites
- 120 peticiones por minuto por clave. Cada respuesta trae
X-RateLimit-Limit,X-RateLimit-RemainingyX-RateLimit-Reset(segundos). Al pasarte recibes429conRetry-After. - 30 intentos con clave inválida por minuto por IP. Después,
429 demasiados_intentoshasta que pase el minuto.
Errores
Siempre con el mismo formato: un codigo estable para tu programa y un mensaje legible para el log.
{ "error": { "codigo": "sin_permiso", "mensaje": "esta clave no tiene el permiso cobros:leer" } }
| HTTP | codigo | Qué hacer |
|---|---|---|
| 400 | datos_invalidos | Corrige los datos; el mensaje dice cuál falta o no es válido. |
| 401 | no_autenticado | Falta la clave, o fue revocada o venció. Pide una nueva. |
| 403 | sin_permiso | La clave no tiene ese permiso o pide una sede que no es la suya. |
| 403 | modulo_inactivo | La empresa apagó el módulo API (o Delivery, en pedidos). |
| 403 | empresa_inactiva, empresa_suspendida | La empresa no está operativa. Contacta al cliente. |
| 404 | no_encontrado, ruta_inexistente | El registro o la ruta no existen. |
| 429 | demasiadas_peticiones, demasiados_intentos | Espera lo que indica Retry-After y reintenta. |
Recursos
| Método y ruta | Permiso | Filtros |
|---|---|---|
GET /v1/conexion | cualquiera | — |
GET /v1/productos | productos:leer | q, sku, codigoBarras, activo=true |
GET /v1/productos/{sku} | productos:leer | — |
GET /v1/existencias | productos:leer | sede, sku |
GET /v1/clientes | clientes:leer | q, documento, idExterno |
GET /v1/clientes/{id} | clientes:leer | — |
POST /v1/clientes | clientes:escribir | — |
GET /v1/pedidos | pedidos:leer | sede, estado, referencia, desde, hasta |
GET /v1/pedidos/{id} | pedidos:leer | — |
POST /v1/pedidos | pedidos:escribir | — |
GET /v1/documentos | documentos:leer | sede, tipo, clienteId, desde, hasta |
GET /v1/documentos/{id} | documentos:leer | — |
GET /v1/cobros | cobros:leer | sede, clienteId, documentoId, desde, hasta |
Con una clave limitada a una sede, todo se filtra a esa sede automáticamente. Pedir otra con ?sede= responde 403.
Productos y existencias
El precio va en la moneda del producto (VES, USD…). Las existencias traen cantidad, apartado (comprometido en pedidos) y disponible, por sede. Los costos no se exponen. Los servicios, combos y platos que se preparan al venderse no tienen existencia propia y no aparecen.
Crear un cliente
curl -s -X POST https://elerp.tech/v1/clientes \
-H "Authorization: Bearer $ELERP_API_KEY" -H "Content-Type: application/json" \
-d '{
"nombre": "Distribuidora El Sol, C.A.",
"tipoDocumento": "J", "documento": "123456784",
"direccion": "Av. Bolívar, Valencia",
"telefono": "04141234567", "email": "compras@elsol.com",
"idExterno": "crm-7781"
}'
nombre y documento son obligatorios. El documento se valida como en la pantalla, incluido el dígito verificador del RIF para J y G. Manda siempre idExterno (el id del cliente en tu sistema): con él el alta es idempotente. Si ya creaste ese cliente, recibes 200 con el existente en vez de un duplicado. Un cliente nuevo responde 201.
Publicar un pedido
El ejemplo usa una clave limitada a una sede, que es lo recomendado para una tienda. Con una clave de toda la empresa, agrega "sedeId" con el id de la sede (los ids salen de /v1/conexion).
curl -s -X POST https://elerp.tech/v1/pedidos \
-H "Authorization: Bearer $ELERP_API_KEY" -H "Content-Type: application/json" \
-d '{
"referencia": "WEB-1042",
"cliente": "Ana Pérez",
"telefono": "04141234567",
"direccion": "Av. Principal de Los Ruices, edificio Sol, piso 3",
"puntoDeReferencia": "frente a la plaza",
"pagado": true,
"total": 18.5,
"items": [
{ "sku": "HAR-001", "nombre": "Harina de maíz 1kg",
"cantidad": 2, "precioUnitario": 4.25 },
{ "sku": "CAF-250", "nombre": "Café molido 250g",
"cantidad": 1, "precioUnitario": 10 }
]
}'
{ "id": "ped_…", "numero": 113, "estado": "nuevo", "referencia": "WEB-1042",
"seguimiento": "https://elerp.tech/t/22ykg4jpxiesyuta", … }
- Requiere que la empresa tenga activo el módulo Pedidos y delivery.
- El pedido entra como
nuevoa la pestaña Por revisar de la bandeja, y el local lo confirma o lo rechaza. sedeIdes obligatorio si la clave no está limitada a una sede (los ids salen de/v1/conexion).pagado: truemarca que ya cobraste en tu tienda; confalsese cobra al entregar.- Usa tu número de orden como
referencia: reintentar con la misma responde200con el mismo pedido, sin duplicar. seguimientoes un enlace público que puedes mostrarle a tu cliente para que vea el estado de su pedido.
Para saber en qué va, consulta GET /v1/pedidos/{id} o GET /v1/pedidos?referencia=WEB-1042. Los estados son: nuevo, confirmado, en_preparacion, listo, asignado, retirado_canal, en_ruta, entregado, rechazado, cancelado, entrega_fallida y devuelto.
Facturas y documentos fiscales
tipo puede ser factura, nota_credito, nota_debito o anulacion. Vienen de lo más reciente a lo más antiguo, con número, número de control, cliente, moneda y tasa, base imponible, base exenta, IVA, IGTF, total, si es a crédito, lo cobrado, las líneas y los pagos.
Los documentos fiscales son inmutables. Una anulación o una nota es un documento nuevo que apunta al original en documentoOrigenId, y el original queda con anulado: true. Para un libro de ventas, suma facturas y notas de débito y resta notas de crédito.
# Ventas de octubre, 200 por página
curl -s "https://elerp.tech/v1/documentos?desde=2026-10-01&hasta=2026-10-31&porPagina=200" \
-H "Authorization: Bearer $ELERP_API_KEY"
Cobros
Los cobros tampoco se editan. Uno que se deshizo aparece como otro cobro con reverso: true que apunta al original en cobroOrigenId. Cada cobro trae monto en su moneda, su equivalente montoBs, la tasa usada y el IGTF.
Buenas prácticas
- Una clave por sistema. Si compartes una entre dos integraciones, revocarla corta las dos.
- Sincroniza por fechas. Para facturas y cobros, pide solo el período nuevo (
desde) en lugar de leer todo cada vez. - Reintenta con calma. Ante un
429o un error de red, espera y reintenta; las altas de clientes y pedidos son idempotentes si mandasidExternoyreferencia. - Ignora los campos que no conozcas. La API puede sumar campos nuevos sin aviso.
Compromiso de versión
Lo que está en /v1 no se rompe: se pueden agregar campos y recursos, pero no se quitan ni se renombran. Un cambio incompatible saldría en /v2, con aviso previo.