Cada negocio crea sus llaves en el panel, en Ajustes › API y portal. Una llave puede ser de lectura (solo GET) o de escritura (también POST y PUT). Va en la cabecera de cada llamada:
curl http://repuestovip.com/api/v1/productos?q=filtro \ -H "Authorization: Bearer rv_TU_LLAVE"
Las respuestas traen data; las listas además traen pagina, paginas y total. Los errores traen error con el texto y el código HTTP que corresponde: 401 llave inválida, 403 sin alcance o sin módulo, 404 no existe, 422 datos inválidos (con errors por campo). Límite: 120 llamadas por minuto por llave.
/api/v1/yolecturaQuién soy: el negocio al que pertenece la llave.
Respuesta{
"data": {
"negocio": "Repuestos El Volante",
"rnc": "130123456",
"pais": "DO",
"moneda": "DOP",
"impuesto": {
"nombre": "ITBIS",
"pct": 18
},
"llave": "Tienda en línea"
}
}
/api/v1/productoslecturaCatálogo con precio y existencia por sucursal.
q | Busca por nombre, SKU o código de barras |
por_pagina | Hasta 100 (50 por defecto) |
page | Número de página |
{
"data": [
{
"sku": "FIL-001",
"barras": "7501234567890",
"nombre": "Filtro de aceite Toyota",
"categoria": "Filtros",
"precio": 450,
"impuesto_pct": 18,
"maneja_stock": true,
"existencia": 21,
"por_sucursal": {
"1": 21
}
}
],
"pagina": 1,
"paginas": 3,
"total": 120
}
/api/v1/productos/{sku}lecturaUn producto por SKU o código de barras.
Respuesta{
"data": {
"sku": "FIL-001",
"nombre": "Filtro de aceite Toyota",
"precio": 450,
"existencia": 21
}
}
/api/v1/productos/{sku}Cambia precio, precio mayorista, nombre o si está activo. La existencia no se toca por aquí: eso lo hacen las compras y las ventas.
Cuerpo (JSON){
"precio": 475,
"precio_mayorista": 430,
"activo": true
}
Respuesta
{
"data": {
"sku": "FIL-001",
"precio": 475
}
}
/api/v1/clienteslecturaClientes, con el enlace de su portal.
q | Nombre, teléfono, documento o correo |
por_pagina | Hasta 100 |
{
"data": [
{
"id": 12,
"nombre": "Carlos Rodríguez",
"documento": "048-0000001-1",
"telefono": "8095550101",
"whatsapp": "8095550101",
"email": null,
"portal": "https://tu-dominio/mi/abc…"
}
],
"pagina": 1,
"paginas": 3,
"total": 120
}
/api/v1/clientes/{id}lecturaUn cliente con sus vehículos.
Respuesta{
"data": {
"id": 12,
"nombre": "Carlos Rodríguez",
"vehiculos": [
{
"id": 3,
"descripcion": "Toyota Corolla 2018",
"placa": "A123456",
"kilometraje": 145230
}
]
}
}
/api/v1/clientesCrea un cliente. Si el teléfono o el documento ya existen, devuelve ese cliente en vez de duplicarlo.
Cuerpo (JSON){
"nombre": "María Gómez",
"telefono": "8095550202",
"whatsapp": "8095550202",
"email": "maria@ejemplo.com",
"documento_tipo": "cedula",
"documento": "001-0000002-2"
}
Respuesta
{
"data": {
"id": 40,
"nombre": "María Gómez",
"portal": "https://tu-dominio/mi/…"
},
"creado": true
}
/api/v1/facturaslecturaFacturas de un período.
desde | AAAA-MM-DD (inicio de mes por defecto) |
hasta | AAAA-MM-DD (hoy) |
estado | pendiente, parcial, pagada o anulada |
por_pagina | Hasta 100 |
{
"data": [
{
"numero": "FAC-000120",
"ncf": "B0200000120",
"fecha": "2026-09-22",
"cliente": "Carlos Rodríguez",
"condicion": "contado",
"estado": "pagada",
"subtotal": 900,
"impuesto": 162,
"total": 1062,
"pagado": 1062,
"balance": 0
}
],
"pagina": 1,
"paginas": 3,
"total": 120
}
/api/v1/facturas/{numero}lecturaUna factura con sus líneas. También por NCF.
Respuesta{
"data": {
"numero": "FAC-000120",
"total": 1062,
"lineas": [
{
"tipo": "producto",
"descripcion": "Filtro de aceite Toyota",
"cantidad": 2,
"precio": 450,
"descuento": 0,
"total": 1062
}
]
}
}
/api/v1/facturasEmite una factura de mostrador (por ejemplo, una venta de la tienda en línea). Sale del almacén de la sucursal principal y queda pendiente de cobro a nombre de cliente_id (obligatorio); se cobra desde el panel. Un precio por debajo del mínimo del producto se rechaza.
Cuerpo (JSON){
"cliente_id": 12,
"condicion": "contado",
"notas": "Pedido web #5521",
"lineas": [
{
"sku": "FIL-001",
"cantidad": 2
},
{
"sku": "ACE-010",
"cantidad": 1,
"precio": 1200
}
]
}
Respuesta
{
"data": {
"numero": "FAC-000121",
"total": 2478,
"balance": 2478,
"estado": "pendiente"
}
}
/api/v1/ordenes/{numero}lecturaUna orden de trabajo con estado, líneas e historial.
Respuesta{
"data": {
"numero": "OT-000001",
"estado": "en_reparacion",
"estado_texto": "En reparación",
"cliente": "Carlos Rodríguez",
"vehiculo": {
"descripcion": "Toyota Corolla 2018",
"placa": "A123456"
},
"fecha_promesa": "2026-09-24",
"total": 6600,
"lineas": [],
"historial": []
}
}
/api/v1/cotizaciones/{numero}lecturaUna cotización con sus líneas.
Respuesta{
"data": {
"numero": "COT-000012",
"estado": "pendiente",
"total": 14000,
"vence": "2026-10-07",
"lineas": []
}
}
/api/v1/citasAgenda una cita (por ejemplo, desde la web del negocio). Si el teléfono ya es de un cliente, la cita queda a su nombre.
Cuerpo (JSON){
"nombre": "Pedro Web",
"telefono": "8095550000",
"vehiculo": "Hyundai Accent 2019",
"motivo": "Cambio de aceite",
"fecha": "2026-09-25",
"hora": "10:00",
"notas": "Viene con prisa"
}
Respuesta
{
"data": {
"numero": "AG-000015",
"fecha": "2026-09-25",
"hora": "10:00 am",
"estado": "programada"
}
}
En el panel se registran direcciones a las que se les avisa cuando pasa algo. A cada una le llega un POST con JSON y estas cabeceras: X-RepuestoVIP-Evento con el nombre del evento y X-RepuestoVIP-Firma con el HMAC-SHA256 del cuerpo usando el secreto del webhook. Respondan 2xx en menos de 6 segundos; no hay reintentos, el panel muestra el último resultado.
factura.emitida | Factura emitidaCuando nace una factura, de mostrador o de una orden |
orden.estado | Orden cambió de estadoCada paso de una orden de trabajo |
cotizacion.decidida | Cotización aprobada o rechazadaPor el cliente en su portal o por el taller |
cita.creada | Cita agendadaDesde el panel, el portal del cliente o la API |
{
"evento": "factura.emitida",
"negocio": 7,
"fecha": "2026-09-22T14:05:11-04:00",
"datos": {
"numero": "FAC-000121",
"cliente": "Carlos Rodríguez",
"total": 2478,
"condicion": "contado",
"origen": "pos"
}
}
Verificar la firma (PHP)
$cuerpo = file_get_contents('php://input');
$firma = $_SERVER['HTTP_X_REPUESTOVIP_FIRMA'] ?? '';
if (! hash_equals(hash_hmac('sha256', $cuerpo, $secreto), $firma)) {
http_response_code(401); exit;
}