Back to API DocsVolver a Docs API

CRM Relay API Documentation

Documentación API CRM Relay

Simple JWT-authenticated API for lead management

API simple con autenticación JWT para gestión de leads

📋 View cURL Examples 📋 Ver Ejemplos cURL

Quick Reference

Referencia Rápida

The CRM API allows providers to create and view leads, while clients can update lead status. All endpoints use JWT authentication.

La API CRM permite a los proveedores crear y ver leads, mientras que los clientes pueden actualizar el estado. Todos los endpoints usan autenticación JWT.

✨ Key Features

✨ Características Principales

  • 202 Accepted — Immediate responses for lead creation
  • Idempotency — Unique proveedor_lead_id prevents duplicates
  • State Normalization — Auto-converts aliases ("Aprovado" → "APROBADO")
  • JWT Authentication — Secure Bearer token authentication
  • Role-Based Access — Providers, Clients, and Administrators
  • Filtering & Pagination — Easy data retrieval
  • 202 Accepted — Respuestas inmediatas para creación de leads
  • Idempotenciaproveedor_lead_id único previene duplicados
  • Normalización de Estados — Convierte automáticamente aliases ("Aprovado" → "APROBADO")
  • Autenticación JWT — Autenticación segura con Bearer token
  • Acceso por Roles — Proveedores, Clientes y Administradores
  • Filtrado y Paginación — Recuperación fácil de datos

Quickstart Guide

Guía de Inicio Rápido

Get started with the CRM Relay API in 5 simple steps:

Comienza con la API CRM Relay en 5 pasos simples:

  1. Authenticate — Obtain JWT via POST /api/auth/login
  2. Extract Token — Use response.data.token (nested inside data)
  3. Set Header — Add Authorization: Bearer <token> to all requests
  4. Submit Leads — Send to POST /api/crm/leads with required fields
  5. Process Async — System responds 202 and queues for background processing
  1. Autenticar — Obtén JWT via POST /api/auth/login
  2. Extraer Token — Usa response.data.token (anidado dentro de data)
  3. Configurar Header — Agrega Authorization: Bearer <token> a todas las peticiones
  4. Enviar Leads — Envía a POST /api/crm/leads con campos requeridos
  5. Procesamiento Async — El sistema responde 202 y encola para procesamiento en segundo plano

Example: Create Lead

Ejemplo: Crear Lead

curl -X POST "http://localhost/paqueteriacz/api/crm/leads" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <TOKEN>" \
  -d '{"lead":{"proveedor_lead_id":"PR-001","nombre":"Juan Pérez","telefono":"+50512345678","fecha_hora":"2025-01-15 10:00:00"}}'

Response (202 Accepted)

Respuesta (202 Accepted)

{
    "success": true,
    "message": "Lead(s) aceptado(s) para procesamiento",
    "accepted": 1,
    "inbox_id": 123
}

Authentication

Autenticación

All CRM endpoints require JWT authentication. Use the token from POST /api/auth/login.

Todos los endpoints CRM requieren autenticación JWT. Usa el token de POST /api/auth/login.

Step 1: Login to Get JWT Token

Paso 1: Login para Obtener Token JWT

Authenticate with your credentials to receive a JWT token:

Autentícate con tus credenciales para recibir un token JWT:

POST /api/auth/login

Request Body

Cuerpo de la Petición

{
    "email": "proveedor@example.com",
    "password": "your_secure_password"
}

Response — Success 200

{
    "success": true,
    "message": "Login exitoso",
    "data": {
        "id": "123",
        "nombre": "Usuario Proveedor",
        "email": "proveedor@example.com",
        "rol": "Proveedor",
        "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IjEyMyIsIm5vbWJyZSI6IlVzdWFyaW8gUHJvdmVlZG9yIiwiZW1haWwiOiJwcm92ZWVkb3JAZXhhbXBsZS5jb20iLCJyb2wiOiJQcm92ZWVkb3IiLCJleHAiOjE3MDQ4MTI0MDB9.signature_here"
    }
}
Important: The token is nested inside data.token, not at the root level. Extract it as: response.data.token
Importante: El token está anidado dentro de data.token, no en el nivel raíz. Extráelo como: response.data.token

Response — Invalid Credentials 401

{
    "success": false,
    "message": "Credenciales inválidas"
}

Example: Full Login Flow

Ejemplo: Flujo Completo de Login

# Login and extract token
curl -X POST "http://localhost/paqueteriacz/api/auth/login" \
  -H "Content-Type: application/json" \
  -d '{"email":"proveedor@example.com","password":"your_password"}'

# Response contains token at data.token
# Use it in subsequent requests:
curl -X GET "http://localhost/paqueteriacz/api/crm/leads" \
  -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLC..."

Step 2: Use Token in API Requests

Paso 2: Usar Token en Peticiones API

Required Header

Encabezado Requerido

Authorization: Bearer <JWT_TOKEN>

Roles & Permissions

Roles y Permisos

RolePermissions
ProveedorCreate leads, view own leads
ClienteUpdate lead status (ownership required), view assigned leads
AdministradorFull access + system metrics
RolPermisos
ProveedorCrear leads, ver propios leads
ClienteActualizar estado de lead (requiere propiedad), ver leads asignados
AdministradorAcceso completo + métricas del sistema

HTTP Status Codes

Códigos de Estado HTTP

CodeMeaningWhen
200OKSuccessful query, update, or idempotent retry
202AcceptedLead queued for async processing
400Bad RequestValidation error, invalid transition
401UnauthorizedMissing/invalid JWT token
403ForbiddenInsufficient permissions or ownership
404Not FoundLead ID doesn't exist
CódigoSignificadoCuándo
200OKConsulta exitosa, actualización o reintento idempotente
202AceptadoLead encolado para procesamiento asíncrono
400Solicitud IncorrectaError de validación, transición inválida
401No AutorizadoToken JWT faltante/inválido
403ProhibidoPermisos insuficientes o falta de propiedad
404No EncontradoID de lead no existe

Create Leads

Crear Leads

Submit individual leads or batches. Returns 202 Accepted immediately.

Envía leads individuales o en lote. Retorna 202 Accepted inmediatamente.

Endpoint

Endpoint

POST /api/crm/leads

Allowed Roles

Roles Permitidos

Proveedor, Administrador

Request — Individual Lead

Request — Lead Individual

{
    "lead": {
        "proveedor_lead_id": "PR-12345",
        "nombre": "Juan Pérez",
        "telefono": "+50512345678",
        "producto": "Laptop Dell",
        "precio": 500.00,
        "fecha_hora": "2025-01-15 10:30:00",
        "cliente_id": 5
    }
}

Request — Batch

Request — Lote

{
    "leads": [
        {"proveedor_lead_id":"PR-001","nombre":"Lead 1","telefono":"+50511111111","fecha_hora":"2025-01-15 10:00:00"},
        {"proveedor_lead_id":"PR-002","nombre":"Lead 2","telefono":"+50522222222","fecha_hora":"2025-01-15 10:05:00"}
    ]
}

Field Reference

Referencia de Campos

FieldTypeRequiredDescription
proveedor_lead_idstring(120)✅ YesUnique lead ID (per provider)
fecha_horadatetime✅ YesLead timestamp (YYYY-MM-DD HH:MM:SS)
nombrestring(255)NoProspect name
telefonostring(30)NoPhone number
productostring(255)NoProduct of interest
preciodecimal(10,2)NoProduct price
cliente_idintegerNoClient ID (auto-forward if has webhook)
CampoTipoRequeridoDescripción
proveedor_lead_idstring(120)✅ SíID único de lead (por proveedor)
fecha_horadatetime✅ SíFecha y hora del lead (YYYY-MM-DD HH:MM:SS)
nombrestring(255)NoNombre del prospecto
telefonostring(30)NoNúmero de teléfono
productostring(255)NoProducto de interés
preciodecimal(10,2)NoPrecio del producto
cliente_idintegerNoID del cliente (auto-reenvío si tiene webhook)

Response — Success 202

{
    "success": true,
    "message": "Lead(s) aceptado(s) para procesamiento",
    "accepted": 1,
    "inbox_id": 123
}

Response — Duplicate (Idempotent) 200

{
    "success": true,
    "message": "Lead(s) ya procesado(s) previamente",
    "accepted": 1,
    "duplicated": true
}

Assign Client to Leads

Asignar Cliente a Leads

Assign one or multiple leads to a specific client. Providers can only assign their own leads.

Asigna uno o múltiples leads a un cliente específico. Los proveedores solo pueden asignar sus propios leads.

Endpoint

Endpoint

POST /api/crm/leads/assign-client

Allowed Roles

Roles Permitidos

Proveedor (own leads only), Administrador

Proveedor (solo sus propios leads), Administrador

Request Body

Cuerpo de la Petición

{
    "cliente_id": 5,
    "lead_ids": [101, 102, 103],
    "observaciones": "Asignación manual"
}
FieldTypeRequiredDescription
cliente_idinteger✅ YesTarget Client ID (User ID with role 'Cliente')
lead_idsarray|int✅ YesList of Lead IDs or single ID
observacionesstringNoOptional notes
CampoTipoRequeridoDescripción
cliente_idinteger✅ SíID Cliente Destino (ID Usuario con rol 'Cliente')
lead_idsarray|int✅ SíLista de IDs de Leads o ID único
observacionesstringNoNotas opcionales

Response — Success 200

{
    "success": true,
    "message": "Operación completada. 3 asignados a 'Cliente Juan'.",
    "updated": 3,
    "failed": 0,
    "total_processed": 3
}

Response — Partial Failure 200

{
    "success": true,
    "message": "Operación completada. 2 asignados a 'Cliente Juan'. (1 fallos)",
    "updated": 2,
    "failed": 1,
    "total_processed": 3,
    "failed_details": [
        {"lead_id": 103, "error": "No tienes permiso (No eres el proveedor creador)"}
    ]
}

Provider Metrics

Métricas de Proveedor

Retrieve specific lead metrics for the authenticated provider (or admin view of provider data).

Recupera métricas específicas de leads para el proveedor autenticado (o vista de admin de datos de proveedor).

Endpoint

Endpoint

GET /api/crm/provider-metrics

Allowed Roles

Roles Permitidos

Proveedor, Administrador

Response — Success 200

Respuesta — Éxito 200

{
    "success": true,
    "data": {
        "total": 150,
        "procesados": 120,
        "en_espera": 30,
        "porcentaje_procesado": 80.0,
        "por_estado": [
            {"estado": "EN_ESPERA", "cantidad": 30},
            {"estado": "APROBADO", "cantidad": 100},
            {"estado": "CANCELADO", "cantidad": 20}
        ]
    }
}

Update Lead Status

Actualizar Estado de Lead

Update lead state with automatic normalization. Clients have full flexibility to change to any valid state.

Actualiza el estado del lead con normalización automática. Clientes tienen total flexibilidad para cambiar a cualquier estado válido.

Endpoint

Endpoint

POST /api/crm/leads/{id}/estado

Allowed Roles

Roles Permitidos

Cliente (owner only), Administrador

Cliente (solo propietario), Administrador

Request Body

Cuerpo de la Petición

{
    "estado": "Aprovado",
    "observaciones": "Cliente confirmó recepción"
}

Valid States & Aliases

Estados Válidos y Alias

Canonical StateAccepted Aliases
EN_ESPERAESPERA, PENDING, WAITING
APROBADOAPROVADO, APPROVED
CONFIRMADOCONFIRMAR, CONFIRMED
EN_TRANSITOEN TRANSITO, TRANSITO, TRANSIT
EN_BODEGAEN BODEGA, BODEGA, WAREHOUSE
CANCELADOCANCELAR, CANCELLED, CANCELED
Estado CanónicoAlias Aceptados
EN_ESPERAESPERA, PENDING, WAITING
APROBADOAPROVADO, APPROVED
CONFIRMADOCONFIRMAR, CONFIRMED
EN_TRANSITOEN TRANSITO, TRANSITO, TRANSIT
EN_BODEGAEN BODEGA, BODEGA, WAREHOUSE
CANCELADOCANCELAR, CANCELLED, CANCELED
StateDescriptionWhen to Use
EN_ESPERAWaiting for approvalInitial state when order is created
APROBADOApproved and validatedAfter reviewing and approving the order
CONFIRMADOConfirmed with customerCustomer confirmed they want to proceed
EN_TRANSITOPackage on the wayPackage shipped and being transported
EN_BODEGAPackage arrived at warehousePackage received and stored
CANCELADOOrder cancelledOrder will not proceed for any reason
EstadoDescripciónCuándo Usar
EN_ESPERAEsperando aprobaciónEstado inicial cuando se crea el pedido
APROBADOAprobado y validadoDespués de revisar y aprobar el pedido
CONFIRMADOConfirmado con clienteCliente confirmó que procede con el pedido
EN_TRANSITOPaquete en caminoPaquete salió y está siendo transportado
EN_BODEGAPaquete llegó a bodegaPaquete recibido y almacenado
CANCELADOPedido canceladoPedido no procede por cualquier razón
Note: There are no transition restrictions. Clients can change from any state to any other valid state according to their business needs.
Nota: No hay restricciones de transición. Los clientes pueden cambiar de cualquier estado a cualquier otro estado válido según sus necesidades de negocio.

Response — Success 200

{
    "success": true,
    "message": "Estado actualizado a APROBADO",
    "estado_anterior": "EN_ESPERA",
    "estado_nuevo": "APROBADO"
}

Response — Invalid State 400

{
    "success": false,
    "message": "Estado inválido: INVALID_STATE",
    "estados_validos": ["EN_ESPERA", "APROBADO", "CONFIRMADO", "EN_TRANSITO", "EN_BODEGA", "CANCELADO"]
}

Bulk Status Update

Actualización Masiva de Estado

Update the status of multiple leads simultaneously. Clients can only update leads they own (cliente_id), while admins can update any leads.

Actualiza el estado de múltiples leads simultáneamente. Los clientes solo pueden actualizar leads que les pertenecen (cliente_id), mientras que los admins pueden actualizar cualquier lead.

Endpoint

Endpoint

POST /api/crm/leads/bulk-status

Allowed Roles

Roles Permitidos

Cliente (own leads only), Administrador

Cliente (solo sus propios leads), Administrador

Request Body

Cuerpo de la Petición

{
    "lead_ids": [123, 124, 125],
    "estado": "contactado",
    "observaciones": "Contactados vía campaña SMS"
}

Request Fields

Campos de la Petición

  • lead_ids: Array of internal lead IDs.
  • estado: New status (valid state or alias).
  • observaciones (optional): Text note.
  • lead_ids: Arreglo de IDs internos de leads.
  • estado: Nuevo estado (válido o alias).
  • observaciones (opcional): Nota de texto.

Example cURL

Ejemplo cURL

curl -X POST "http://localhost/paqueteriacz/api/crm/leads/bulk-status" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <TOKEN>" \
  -d '{ "lead_ids": [123, 124, 125], "estado": "aprobado", "observaciones": "Procesados el 2026-01-02" }'

Response — All Success 200

Respuesta — Todo Exitoso 200

{
    "success": true,
    "message": "3 de 3 leads actualizados exitosamente",
    "updated": 3,
    "failed": 0,
    "total": 3,
    "estado_nuevo": "APROBADO",
    "results": [
        { "lead_id": 123, "success": true, "estado_anterior": "EN_ESPERA", "estado_nuevo": "APROBADO" },
        { "lead_id": 124, "success": true, "estado_anterior": "EN_PROCESO", "estado_nuevo": "APROBADO" },
        { "lead_id": 125, "success": true, "estado_anterior": "EN_ESPERA", "estado_nuevo": "APROBADO" }
    ]
}

Response — Mixed Results 207 Multi-Status

Respuesta — Resultados Mixtos 207 Multi-Status

{
    "success": true,
    "message": "2 de 3 leads actualizados exitosamente",
    "updated": 2,
    "failed": 1,
    "total": 3,
    "estado_nuevo": "APROBADO",
    "results": [
        { "lead_id": 123, "success": true, "estado_anterior": "EN_ESPERA", "estado_nuevo": "APROBADO" },
        { "lead_id": 124, "success": false, "message": "No tienes permiso para este lead" },
        { "lead_id": 125, "success": true, "estado_anterior": "EN_ESPERA", "estado_nuevo": "APROBADO" }
    ]
}

Response — Limit Exceeded 400

Respuesta — Límite Excedido 400

{
    "success": false,
    "message": "Límite máximo de 100 leads por request",
    "received": 150
}

Response — All Failed 400

Respuesta — Todos Fallaron 400

{
    "success": false,
    "message": "0 de 3 leads actualizados exitosamente",
    "updated": 0,
    "failed": 3,
    "total": 3,
    "estado_nuevo": "APROBADO",
    "results": [
        { "lead_id": 999, "success": false, "message": "Lead no encontrado" },
        { "lead_id": 888, "success": false, "message": "No tienes permiso para este lead" },
        { "lead_id": 777, "success": false, "message": "Lead no encontrado" }
    ]
}

Bulk Status Update (Async) 🚀

Actualización Masiva de Estado (Asíncrona) 🚀

For processing large datasets (>100 leads). Accepts leads, creates a background job, and returns immediately.

Para procesar grandes conjuntos de datos (>100 leads). Acepta leads, crea un trabajo en segundo plano y retorna inmediatamente.

Endpoint

Endpoint

POST /api/crm/leads/bulk-status-async

Allowed Roles

Roles Permitidos

Cliente (own leads only), Administrador

Cliente (solo sus propios leads), Administrador

Request Body

Cuerpo de la Petición

{
    "lead_ids": [1001, 1002, ..., 5000],
    "estado": "CANCELADO",
    "observaciones": "Limpieza mensual automática"
}

Response — Job Created 202

Respuesta — Job Creado 202

{
    "success": true,
    "message": "Proceso de actualización masiva iniciado. Job ID: bulk_6958b3ea2e5ad_1767420906",
    "job_id": "bulk_6958b3ea2e5ad_1767420906",
    "status_url": "http://localhost/paqueteriacz/api/crm/jobs/bulk_6958b3ea2e5ad_1767420906"
}

Check Job Status

Consultar Estado de Job

Poll this endpoint to track the progress of an async bulk update.

Consulta este endpoint para rastrear el progreso de una actualización masiva asíncrona.

Endpoint

Endpoint

GET /api/crm/jobs/{job_id}

Response — In Progress 200

Respuesta — En Progreso 200

{
    "success": true,
    "job_id": "bulk_6958b3ea2e5ad_1767420906",
    "status": "processing",
    "total_leads": 5000,
    "processed_leads": 2350,
    "progress_percent": 47,
    "created_at": "2026-01-03 00:15:06"
}

Response — Completed 200

Respuesta — Completado 200

{
    "success": true,
    "job_id": "bulk_6958b3ea2e5ad_1767420906",
    "status": "completed",
    "total_leads": 191,
    "processed_leads": 191,
    "successful_leads": 3,
    "failed_leads": 188,
    "estado": "CANCELADO",
    "created_at": "2026-01-03 00:15:06",
    "started_at": "2026-01-03 00:15:06",
    "completed_at": "2026-01-03 00:15:08",
    "progress_percent": 100,
    "successful_details": [
        {"lead_id": 1, "estado_anterior": "EN_ESPERA", "estado_nuevo": "CANCELADO"},
        {"lead_id": 5, "estado_anterior": "EN_PROCESO", "estado_nuevo": "CANCELADO"},
        {"lead_id": 12, "estado_anterior": "APROBADO", "estado_nuevo": "CANCELADO"}
    ],
    "failed_details": [
        {"lead_id": 2, "error": "No encontrado"},
        {"lead_id": 3, "error": "Sin permiso"}
    ]
}

List Leads

Listar Leads

Retrieve leads with filtering and pagination. Automatic role-based scoping applies.

Recupera leads con filtrado y paginación. Se aplica alcance automático basado en roles.

Endpoint

Endpoint

GET /api/crm/leads

Allowed Roles

Roles Permitidos

Cliente (own leads only), Administrador

Cliente (solo sus propios leads), Administrador

Query Parameters

Parámetros de Consulta

ParameterTypeDefaultDescription
pageinteger1Page number
limitinteger50Items per page (max 100)
estadostring-Filter by state
fecha_desdedate-From date (YYYY-MM-DD)
fecha_hastadate-To date (YYYY-MM-DD)
ParámetroTipoPor DefectoDescripción
pageinteger1Número de página
limitinteger50Items por página (máx 100)
estadostring-Filtrar por estado
fecha_desdedate-Desde fecha (YYYY-MM-DD)
fecha_hastadate-Hasta fecha (YYYY-MM-DD)

Example cURL

Ejemplo cURL

curl "http://localhost/paqueteriacz/api/crm/leads?page=1&limit=10&estado=APROBADO" \
  -H "Authorization: Bearer <TOKEN>"

View Lead Detail & Timeline

Ver Detalle de Lead y Cronología

Access full lead details or status change history.

Accede a los detalles completos del lead o historial de cambios de estado.

Endpoints

Endpoints

GET /api/crm/leads/{id}
GET /api/crm/leads/{id}/timeline

Allowed Roles

Roles Permitidos

Cliente (own lead only), Administrador

Cliente (solo su propio lead), Administrador

Example cURL

Ejemplo cURL

# View detail
curl "http://localhost/paqueteriacz/api/crm/leads/1" \
  -H "Authorization: Bearer <TOKEN>"

# View timeline
curl "http://localhost/paqueteriacz/api/crm/leads/1/timeline" \
  -H "Authorization: Bearer <TOKEN>"
# Ver detalle
curl "http://localhost/paqueteriacz/api/crm/leads/1" \
  -H "Authorization: Bearer <TOKEN>"

# Ver cronología
curl "http://localhost/paqueteriacz/api/crm/leads/1/timeline" \
  -H "Authorization: Bearer <TOKEN>"

Notifications

Notificaciones

Manage user notifications. Notifications are generated on key events like lead assignment or status changes.

Gestiona las notificaciones del usuario. Las notificaciones se generan en eventos clave como asignación de leads o cambios de estado.

Endpoints

Endpoints

GET /api/crm/notifications
POST /api/crm/notifications/{id}/read
POST /api/crm/notifications/read-all

Allowed Roles

Roles Permitidos

Proveedor, Cliente, Administrador

Response — List Notifications

Respuesta — Listar Notificaciones

{
    "success": true,
    "data": [
        {
            "id": 15,
            "tipo": "nuevo_lead",
            "mensaje": "Nuevo lead asignado: Juan Pérez",
            "leido": 0,
            "fecha_creacion": "2025-01-15 10:30:00",
            "data_adicional": {"lead_id": 123}
        }
    ],
    "unread_count": 1
}

System Metrics

Métricas del Sistema

Monitor CRM health and performance (admin-only).

Monitorea la salud y rendimiento del CRM (solo administrador).

Endpoint

Endpoint

GET /api/crm/metrics

Allowed Roles

Roles Permitidos

Administrador only

Solo Administrador

Example cURL

Ejemplo cURL

curl "http://localhost/paqueteriacz/api/crm/metrics" \
  -H "Authorization: Bearer <ADMIN_TOKEN>"

Webhooks (HMAC Signed)

Webhooks (Firmados con HMAC)

Receive real-time notifications via cryptographically signed webhooks.

Recibe notificaciones en tiempo real vía webhooks firmados criptográficamente.

Events

Eventos

  • SEND_TO_CLIENT — Lead forwarded to client
  • SEND_TO_PROVIDER — Status updated by client
  • SEND_TO_CLIENT — Lead reenviado al cliente
  • SEND_TO_PROVIDER — Estado actualizado por el cliente

Signature Verification (PHP)

Verificación de Firma (PHP)

$payload = file_get_contents("php://input");
$signature = $_SERVER['HTTP_X_SIGNATURE'];
$secret = 'your_shared_secret';

$expected = 'sha256=' . hash_hmac('sha256', $payload, $secret);

if (hash_equals($expected, $signature)) {
    $data = json_decode($payload, true);
    // Process webhook / Procesar webhook
} else {
    http_response_code(401);
    exit;
}

Configuration

Configuración

INSERT INTO crm_integrations (user_id, kind, webhook_url, secret, is_active)
VALUES (5, 'cliente', 'https://app.com/webhook', 'secret_123', 1);

Worker CLI

Worker CLI

Background worker for async processing with 3-second polling interval.

Worker en segundo plano para procesamiento asíncrono con intervalo de sondeo de 3 segundos.

1. General Worker (Webhooks & Retries)

1. Worker General (Webhooks y Reintentos)

# One-time execution (cron)
php cli/crm_worker.php --once

# Daemon mode (systemd)
php cli/crm_worker.php --loop
# Ejecución única (cron)
php cli/crm_worker.php --once

# Modo daemon (systemd)
php cli/crm_worker.php --loop

2. Bulk Update Worker 🚀

2. Worker de Actualización Masiva 🚀

Processes asynchronous bulk update jobs from POST /bulk-status-async.

Procesa jobs de actualización masiva asíncrona de POST /bulk-status-async.

# Start worker (runs continuously)
php cli/crm_bulk_worker.php

3. Maintenance & Cleanup 🧹

3. Mantenimiento y Limpieza 🧹

Removes old jobs and monitors stuck processes. Run daily via Cron/Task Scheduler.

Elimina jobs antiguos y monitorea procesos atascados. Ejecutar diariamente vía Cron/Task Scheduler.

# Run cleanup
php cli/crm_jobs_cleanup.php

Systemd Service

Servicio Systemd

[Unit]
Description=CRM Relay Worker
After=mariadb.service

[Service]
Type=simple
User=www-data
WorkingDirectory=/xampp/htdocs/paqueteriacz
ExecStart=/usr/bin/php cli/crm_worker.php --loop
Restart=always

[Install]
WantedBy=multi-user.target
sudo systemctl enable crm-worker
sudo systemctl start crm-worker
sudo journalctl -u crm-worker -f

Troubleshooting

Solución de Problemas

  • Duplicate lead detection — First: 202, Retry: 200 + "duplicated":true
  • Invalid transition — Check state matrix (can't jump EN_ESPERA → EN_TRANSITO)
  • Worker not running — Verify: ps aux | grep crm_worker
  • Webhook failures — Check crm_outbox.last_error column
  • Queue backup — Monitor: SELECT COUNT(*) FROM crm_inbox WHERE status='pending'
  • Detección de duplicados — Primero: 202, Reintento: 200 + "duplicated":true
  • Transición inválida — Verifica matriz de estados (no puede saltar EN_ESPERA → EN_TRANSITO)
  • Worker no ejecutándose — Verifica: ps aux | grep crm_worker
  • Fallos de webhook — Revisa columna crm_outbox.last_error
  • Respaldo de cola — Monitorea: SELECT COUNT(*) FROM crm_inbox WHERE status='pending'

Monitoring Queries

Consultas de Monitoreo

-- Pending inbox count
SELECT COUNT(*) FROM crm_inbox WHERE status='pending';

-- Failed webhooks
SELECT id, event_type, attempts, last_error 
FROM crm_outbox 
WHERE status='failed' 
LIMIT 10;

-- Recent leads
SELECT id, proveedor_lead_id, estado_actual, created_at 
FROM crm_leads 
ORDER BY created_at DESC 
LIMIT 20;
-- Conteo de inbox pendientes
SELECT COUNT(*) FROM crm_inbox WHERE status='pending';

-- Webhooks fallidos
SELECT id, event_type, attempts, last_error 
FROM crm_outbox 
WHERE status='failed' 
LIMIT 10;

-- Leads recientes
SELECT id, proveedor_lead_id, estado_actual, created_at 
FROM crm_leads 
ORDER BY created_at DESC 
LIMIT 20;