Webhooks
Overview
Sistema de webhooks para notificar eventos del sistema a clientes externos mediante HTTP requests asíncronos.
La confiugracion del webhook se puede hacer por el Dashboard -> Integraciones en https://dashboard.cachicamo.app/store/integrations
- Configuración por acción: Cada acción puede tener hasta 3 webhooks configurados
- Reintentos automáticos: Hasta 5 intentos con 1 hora de espera entre cada uno
Documentación para Clientes (Webhooks que Recibirán)
Eventos Disponibles
Los siguientes eventos pueden ser configurados para recibir notificaciones:
1. document.created
Se dispara cuando se crea un documento (factura, nota de débito, nota de crédito, etc.).
Payload:
{
"id": "invoice-uuid-here",
"document_type": "INVOICE",
"action": "document.created",
"event_at": "2024-01-15T10:30:00Z"
}
Campos:
-
id(string): UUID del documento creado -
document_type(string): Tipo de documento. Valores posibles:-
INVOICE: Factura -
DEBIT_NOTE: Nota de débito -
CREDIT_NOTE: Nota de crédito (refund) -
PROVIDER_INVOICE: Factura de proveedor -
PROVIDER_DEBIT_NOTE: Nota de débito de proveedor -
PROVIDER_CREDIT_NOTE: Nota de crédito de proveedor -
QUOTE: Cotización
-
-
action(string): Nombre del evento -
event_at(string): Fecha y hora del evento en formato ISO 8601
Nota: Solo se envía el ID del documento, no el objeto completo. El cliente debe consultar el documento usando el ID si necesita más información.
2. document.updated
Se dispara cuando un documento cambia después de haber sido creado. El campo change dice qué fue lo que cambió, para poder filtrar el aviso sin consultar el documento en cada notificación.
Payload:
{
"id": "invoice-uuid-here",
"document_type": "INVOICE",
"action": "document.updated",
"change": "control_number_assigned",
"event_at": "2024-01-15T10:35:00Z"
}
Campos:
-
id,document_type,actionyevent_at: iguales a los dedocument.created -
change(string): qué cambió en el documento. Valores posibles:-
emitted: el documento salió deTO_EMITy quedó emitido con su número. Ocurre cuando la máquina fiscal imprime después del intento inicial (el sistema reintenta cada 10 segundos) y cuando se confirma un borrador -
control_number_assigned: la imprenta digital entregó el número de control después de emitir. El documento ya existía y ya tenía su número; lo que llega ahora es elmanual_control_number -
cancelled: el documento fue anulado -
header_edited: se corrigió el encabezado de un documento de compra o de un documento de venta escrito sobre talonario (número, número de control, fecha de emisión o contraparte)
-
Nota: change sólo viaja cuando el sistema sabe qué cambió. Un aviso sin change significa "algo cambió, consulta el documento".
Caso típico: cuando se emite por API contra una imprenta digital, la respuesta de POST /invoices/save_preview trae el documento con su número, pero el número de control puede llegar minutos después. En vez de reconsultar la factura cada cierto tiempo, hay que suscribirse a document.updated y atender los avisos con change igual a control_number_assigned.
3. product.created
Se dispara cuando se crea un nuevo producto.
Payload:
{
"payload": {
"uuid": "product-uuid-here",
"name": "Producto Ejemplo",
// ....
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
},
"action": "product.created",
"event_at": "2024-01-15T10:30:00Z"
}
Nota: Se envía el objeto completo del producto con todos sus campos.
4. product.updated
Se dispara cuando se actualiza un producto o cuando se crea/actualiza un precio del producto.
Payload:
{
"payload": {
"uuid": "product-uuid-here",
"name": "Producto Actualizado",
// ....
},
"action": "product.updated",
"event_at": "2024-01-15T10:35:00Z"
}
Nota: Se envía el objeto completo del producto actualizado.
5. stock.updated
Se dispara cuando se actualiza el stock de un producto (inventario).
Payload:
{
"payload": {
"user_uuid": "user-uuid-here",
"store_uuid": "store-uuid-here",
"product_uuid": "product-uuid-here",
"quantity_billable": 100.0,
"quantity_available": 95.0,
"quantity_locked": 5.0,
"quantity_transit": 0.0,
"warehouse_location": "A-1-2",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:35:00Z"
},
"action": "stock.updated",
"event_at": "2024-01-15T10:35:00Z"
}
6. document_to_pay.created
Se dispara cuando se crea un documento por pagar (cuenta por cobrar o por pagar).
Payload:
{
"payload": {
"uuid": "document-to-pay-uuid",
"document_uuid": "invoice-uuid-here",
"document_type": "INVOICE",
"status": "PENDING",
"user_uuid": "user-uuid-here",
"store_uuid": "store-uuid-here",
"customer_uuid": "customer-uuid-here",
"description": "Factura pendiente de pago",
"currency_uuid": "currency-uuid-here",
"amount_pending": 100000,
"original_amount": 100000,
"amount_payed": 0,
"is_collect": true,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
},
"action": "document_to_pay.created",
"event_at": "2024-01-15T10:30:00Z"
}
7. document_to_pay.updated
Se dispara cuando cambia el monto pendiente de un documento por pagar ya existente, porque el documento que lo originó se modificó.
Payload: el mismo objeto de document_to_pay.created, con los montos ya recalculados.
{
"payload": {
"uuid": "document-to-pay-uuid",
"document_uuid": "invoice-uuid-here",
"document_type": "INVOICE",
"status": "PENDING",
"amount_pending": 80000,
"original_amount": 100000,
"amount_payed": 20000,
"updated_at": "2024-01-15T10:32:00Z"
},
"action": "document_to_pay.updated",
"event_at": "2024-01-15T10:32:00Z"
}
8. document_to_pay.payment
Se dispara cuando se registra un pago parcial o total de un documento por pagar.
Payload:
{
"payload": {
"uuid": "payment-uuid-here",
"document_to_pay_uuid": "document-to-pay-uuid",
"payment_method_uuid": "payment-method-uuid",
"currency_uuid": "currency-uuid-here",
"document_total_payment": 50000,
"origin_total_payment": 50000,
"created_at": "2024-01-15T10:35:00Z"
},
"action": "document_to_pay.payment",
"event_at": "2024-01-15T10:35:00Z"
}
9. document_to_pay.payment_cancelled
Se dispara cuando se anula un pago ya registrado de un documento por pagar. El monto vuelve a quedar pendiente.
Payload: el mismo objeto de document_to_pay.payment, el del pago que se anuló.
{
"payload": {
"uuid": "payment-uuid-here",
"document_to_pay_uuid": "document-to-pay-uuid",
"payment_method_uuid": "payment-method-uuid",
"currency_uuid": "currency-uuid-here",
"document_total_payment": 50000,
"origin_total_payment": 50000,
"created_at": "2024-01-15T10:35:00Z"
},
"action": "document_to_pay.payment_cancelled",
"event_at": "2024-01-15T11:05:00Z"
}
10. document_to_pay.payed
Se dispara cuando un documento por pagar se marca como completamente pagado.
Payload:
{
"payload": {
"uuid": "document-to-pay-uuid",
"document_uuid": "invoice-uuid-here",
"document_type": "INVOICE",
"status": "PAID",
"amount_pending": 0,
"amount_payed": 100000,
"payed_at": "2024-01-15T10:40:00Z"
},
"action": "document_to_pay.payed",
"event_at": "2024-01-15T10:40:00Z"
}
11. retained.reference_assigned
Se dispara cuando se asigna un reference a una retención de factura de venta.
Payload:
{
"payload": {
"uuid": "retained-uuid-here",
"tax_uuid": "tax-uuid-here",
"invoice_uuid": "invoice-uuid-here",
"reference": "REF-001",
"status": "ASSIGNED",
"retained_type": "WITHHELD_BY_SUPPLIER",
"retained_amount": 10000,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:35:00Z"
},
"action": "retained.reference_assigned",
"event_at": "2024-01-15T10:35:00Z"
}
12. retained.group_created
Se dispara cuando se crea un comprobante de retención para facturas de compra.
Payload:
{
"payload": {
"uuid": "retained-uuid-here",
"tax_uuid": "tax-uuid-here",
"invoice_uuid": "invoice-uuid-here",
"taxes_retained_group_uuid": "group-uuid-here",
"reference": "GRP-001",
"status": "ASSIGNED",
"retained_type": "WITHHELD_BY_ME",
"retained_amount": 10000,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:35:00Z"
},
"action": "retained.group_created",
"event_at": "2024-01-15T10:35:00Z"
}
13. retained.group_cancelled
Se dispara cuando se anula un comprobante de retención.
Payload:
{
"payload": {
"uuid": "retained-uuid-here",
"tax_uuid": "tax-uuid-here",
"invoice_uuid": "invoice-uuid-here",
"taxes_retained_group_uuid": "group-uuid-here",
"status": "CANCELLED",
"cancel_reason": "Error en el comprobante",
"cancelled_at": "2024-01-15T10:40:00Z",
"retained_amount": 10000
},
"action": "retained.group_cancelled",
"event_at": "2024-01-15T10:40:00Z"
}
14. retained.cancelled
Se dispara cuando se anula una retención individual.
Payload:
{
"payload": {
"uuid": "retained-uuid-here",
"tax_uuid": "tax-uuid-here",
"invoice_uuid": "invoice-uuid-here",
"status": "CANCELLED",
"cancel_reason": "Error en la retención",
"cancelled_at": "2024-01-15T10:40:00Z",
"retained_amount": 10000
},
"action": "retained.cancelled",
"event_at": "2024-01-15T10:40:00Z"
}
15. product.bulk_updated
Se dispara cuando una operación masiva —una importación de Excel, por ejemplo— crea o actualiza productos o sus precios. Es la versión en lote de product.created y product.updated, y es un evento independiente al que hay que suscribirse aparte: quien está suscrito sólo a product.created o product.updated recibe esos envíos por cada producto que se crea o edita de uno en uno, y no recibe éste.
Se emite una sola vez, cuando la operación masiva completa termina, con la lista de todos los productos que afectó.
Payload:
{
"payload": {
"items": [
{
"uuid": "product-uuid-here",
"name": "Producto Ejemplo",
// ....
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}
]
},
"action": "product.bulk_updated",
"event_at": "2024-01-15T10:40:00Z"
}
Campos:
payload.items(array): un elemento por cada producto afectado por la operación masiva. Cada elemento trae el mismo objeto completo del producto que traería su envío individual (product.createdoproduct.updated)action,event_at: iguales a los del resto de los eventos
16. stock.bulk_updated
Se dispara cuando una operación masiva actualiza el stock de varios productos. Es la versión en lote de stock.updated, y es un evento independiente al que hay que suscribirse aparte: quien está suscrito sólo a stock.updated recibe ese envío por cada ajuste de stock hecho de uno en uno, y no recibe éste.
Se emite una sola vez, cuando la operación masiva completa termina, con la lista de todos los productos cuyo stock afectó.
Payload:
{
"payload": {
"items": [
{
"user_uuid": "user-uuid-here",
"store_uuid": "store-uuid-here",
"product_uuid": "product-uuid-here",
"quantity_billable": 100.0,
"quantity_available": 95.0,
"quantity_locked": 5.0,
"quantity_transit": 0.0,
"warehouse_location": "A-1-2",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:35:00Z"
}
]
},
"action": "stock.bulk_updated",
"event_at": "2024-01-15T10:40:00Z"
}
Campos:
payload.items(array): un elemento por cada producto cuyo stock afectó la operación masiva. Cada elemento trae el mismo objeto que traería el envío individualstock.updatedaction,event_at: iguales a los del resto de los eventos
Nota: una misma operación masiva puede afectar productos y stock a la vez —una importación de Excel que trae precios y cantidades, por ejemplo—. En ese caso se emiten los dos eventos por separado, cada uno con su propia lista: product.bulk_updated con los productos y stock.bulk_updated con las existencias.
17. inventory_transfer.created
Se dispara cuando se crea un traslado de inventario: un traslado entre almacenes, una guía de despacho, una nota de entrega o una entrega a cliente.
Llega cuando el documento ya está en firme. Si es una guía de despacho que va a una imprenta digital y la imprenta la rechaza, el traslado se revierte y el aviso no se envía: recibirlo significa que el traslado existe de verdad.
Payload:
{
"id": "ffffffff-0000-0000-0000-000000000001",
"action": "inventory_transfer.created",
"transfer_type": "dispatch_guide",
"status": "accepted",
"reference": "00001234",
"from_store_uuid": "ffffffff-0000-0000-0000-000000000010",
"customer_uuid": "ffffffff-0000-0000-0000-000000000020",
"affects_inventory": "true",
"event_at": "2026-08-28T14:22:31-04:00"
}
Campos:
-
id(string): UUID del traslado -
transfer_type(string): tipo de traslado. Valores posibles:-
depot: traslado entre almacenes propios -
dispatch_guide: guía de despacho -
shipping_note: nota de entrega -
delivery: entrega a cliente
-
-
status(string): estado del traslado. Valores posibles:created,in-transit,received,accepted,in-transit-returned,returned,cancelled -
reference,from_store_uuid,to_store_uuid,customer_uuid(string, opcionales): son opcionales y la clave se omite del cuerpo cuando el traslado no la tiene. Un traslado entre almacenes propios no llevacustomer_uuid; una entrega a un cliente no llevato_store_uuid -
affects_inventory(string):"true"o"false", según si el traslado mueve existencias -
action,event_at: iguales a los del resto de los eventos
Nota: el status llega ya resuelto. Un traslado entre almacenes propios nace in-transit y una guía a un tercero nace accepted, así que no hay que consultar el traslado para saber en qué punto arrancó. Sólo se envía la identificación del traslado, no la lista de productos: para las líneas hay que consultarlo con el id.
18. inventory_transfer.updated
Se dispara cada vez que el traslado cambia de estado: cuando se envía, se recibe, se acepta, se devuelve o se anula.
Payload: el mismo de inventory_transfer.created, más previous_status.
{
"id": "ffffffff-0000-0000-0000-000000000001",
"action": "inventory_transfer.updated",
"transfer_type": "depot",
"status": "received",
"previous_status": "in-transit",
"reference": "00001234",
"from_store_uuid": "ffffffff-0000-0000-0000-000000000010",
"to_store_uuid": "ffffffff-0000-0000-0000-000000000011",
"affects_inventory": "true",
"event_at": "2026-08-28T15:40:02-04:00"
}
Campos:
-
previous_status(string): el estado que tenía el traslado antes de este cambio. Sólo viaja eninventory_transfer.updated; eninventory_transfer.createdno existe -
El resto: iguales a los de
inventory_transfer.created
Nota: el ciclo del traslado se reconstruye con la secuencia de avisos, no consultando. El envío es asíncrono, y entre el aviso y la consulta el traslado puede haber avanzado otro paso: la consulta devuelve el estado final y se pierden los intermedios. Con status y previous_status en el cuerpo, la secuencia de avisos reconstruye el recorrido completo.
Nota sobre stock.updated: un traslado que mueve existencias emite además stock.updated por cada producto y almacén afectados, tanto al crearse como en cada cambio de estado que mueve inventario. Quien esté suscrito a stock.updated recibe esos avisos aunque no se suscriba a los eventos de traslado.
19. inventory_manufacturing.created
Se dispara cuando se crea una orden de fabricación, con sus materias primas y su producto final ya cargados.
Payload:
{
"id": "ffffffff-0000-0000-0000-000000000001",
"action": "inventory_manufacturing.created",
"status": "manufacturing",
"store_uuid": "ffffffff-0000-0000-0000-000000000010",
"event_at": "2026-08-28T14:22:31-04:00"
}
Campos:
-
id(string): UUID de la orden de fabricación -
status(string): estado de la orden. Valores posibles:manufacturing,release,cancelled -
store_uuid(string): almacén donde se descuenta la materia prima y se acredita el producto final -
action,event_at: iguales a los del resto de los eventos
Nota: sólo se envía la identificación de la orden, no las materias primas ni el producto final: para las líneas hay que consultarla con el id.
20. inventory_manufacturing.updated
Se dispara cuando la orden libera su producto final (release) o se cancela (cancelled).
Payload: el mismo de inventory_manufacturing.created, más previous_status.
{
"id": "ffffffff-0000-0000-0000-000000000001",
"action": "inventory_manufacturing.updated",
"status": "release",
"previous_status": "manufacturing",
"store_uuid": "ffffffff-0000-0000-0000-000000000010",
"event_at": "2026-08-28T15:40:02-04:00"
}
Campos:
-
previous_status(string): el estado que tenía la orden antes de este cambio. Sólo viaja eninventory_manufacturing.updated; eninventory_manufacturing.createdno existe -
El resto: iguales a los de
inventory_manufacturing.created
Nota sobre stock.updated: la fabricación emite además stock.updated por cada producto afectado, tanto al crearse (la materia prima se descuenta y el producto final se bloquea en el mismo acto) como al liberarse o cancelarse. Quien esté suscrito a stock.updated recibe esos avisos aunque no se suscriba a los eventos de fabricación.
Formato del Mensaje HTTP
Cuando se recibe un webhook, el sistema enviará una petición HTTP con:
-
Método: El configurado en
method(típicamentePOST) -
URL: La configurada en
destination -
Headers: Los headers personalizados configurados. Nota: El sistema siempre establece
Content-Type: application/jsonautomáticamente, ignorando cualquier valor deContent-Typeque se configure en los headers personalizados. -
Body: El payload JSON con
payload,actionyevent_at
Ejemplo de petición HTTP:
POST https://example.com/webhook HTTP/1.1
Content-Type: application/json
X-Custom-Header: value
{
"payload": {
"uuid": "product-uuid-here",
"name": "Producto Ejemplo",
...
},
"action": "product.created",
"event_at": "2024-01-15T10:30:00Z"
}
Características del Sistema
Reintentos Automáticos
-
Si un webhook falla, se reintenta automáticamente hasta 5 veces
-
Intervalo entre reintentos: 1 hora
-
Estados de destino:
-
pending: Pendiente de envío -
retry: Falló y será reintentado -
sent: Enviado exitosamente -
failed: Falló después de 5 intentos
-