Documentación

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

  1. La dirección de la API de la empresa (aparece en Configuración › Integraciones), por ejemplo https://elerp.tech/v1.
  2. Una clave de API (elerp_sk_…) con los permisos que tu integración necesita. La crea la Dueña de la empresa.
Solo desde un servidor

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-Remaining y X-RateLimit-Reset (segundos). Al pasarte recibes 429 con Retry-After.
  • 30 intentos con clave inválida por minuto por IP. Después, 429 demasiados_intentos hasta 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" } }
HTTPcodigoQué hacer
400datos_invalidosCorrige los datos; el mensaje dice cuál falta o no es válido.
401no_autenticadoFalta la clave, o fue revocada o venció. Pide una nueva.
403sin_permisoLa clave no tiene ese permiso o pide una sede que no es la suya.
403modulo_inactivoLa empresa apagó el módulo API (o Delivery, en pedidos).
403empresa_inactiva, empresa_suspendidaLa empresa no está operativa. Contacta al cliente.
404no_encontrado, ruta_inexistenteEl registro o la ruta no existen.
429demasiadas_peticiones, demasiados_intentosEspera lo que indica Retry-After y reintenta.

Recursos

Método y rutaPermisoFiltros
GET /v1/conexioncualquiera—
GET /v1/productosproductos:leerq, sku, codigoBarras, activo=true
GET /v1/productos/{sku}productos:leer—
GET /v1/existenciasproductos:leersede, sku
GET /v1/clientesclientes:leerq, documento, idExterno
GET /v1/clientes/{id}clientes:leer—
POST /v1/clientesclientes:escribir—
GET /v1/pedidospedidos:leersede, estado, referencia, desde, hasta
GET /v1/pedidos/{id}pedidos:leer—
POST /v1/pedidospedidos:escribir—
GET /v1/documentosdocumentos:leersede, tipo, clienteId, desde, hasta
GET /v1/documentos/{id}documentos:leer—
GET /v1/cobroscobros:leersede, 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 nuevo a la pestaña Por revisar de la bandeja, y el local lo confirma o lo rechaza.
  • sedeId es obligatorio si la clave no está limitada a una sede (los ids salen de /v1/conexion).
  • pagado: true marca que ya cobraste en tu tienda; con false se cobra al entregar.
  • Usa tu número de orden como referencia: reintentar con la misma responde 200 con el mismo pedido, sin duplicar.
  • seguimiento es 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 429 o un error de red, espera y reintenta; las altas de clientes y pedidos son idempotentes si mandas idExterno y referencia.
  • 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.