Saltar al contenido
oden.tax docs
POST tu URL · Oden-Signature

Webhooks

Te avisamos con un POST firmado a tu URL cuando una factura se timbra o cuando cambia su cancelación. El cuerpo trae la factura completa, así que no tienes que pedirla otra vez para saber qué pasó.

Los que más sirven son los de cancelación. La respuesta del receptor llega horas o días después de tu POST, y con el webhook te enteras sin tener que preguntar.

Eventos

mx.invoice.stamped
La factura quedó timbrada. Sale igual si la timbraste por la API, desde el panel o con la facturación masiva.
mx.invoice.cancellation_requested
El SAT recibió la solicitud de cancelación y el receptor tiene que contestarla. El comprobante sigue vigente: data.status ya dice canceled, y data.cancellation.status dice requested.
mx.invoice.cancellation_accepted
El receptor aceptó la cancelación. El comprobante quedó cancelado.
mx.invoice.cancellation_final
Cancelada sin la aceptación del receptor: no hacía falta, o dejó pasar el plazo. El comprobante quedó cancelado.
mx.invoice.cancellation_rejected
El receptor rechazó la cancelación. La factura vuelve a stamped y se puede cancelar otra vez.

Llegan eventos de todos los emisores de la cuenta. data.issuer.id dice de cuál es cada uno.

Cuáles te sirven

Timbrar es síncrono. Si timbraste por la API, la respuesta de tu POST ya trae el UUID, y mx.invoice.stamped te repite lo mismo. Te sirve para enterarte de las facturas que no salieron de tu integración, como las que alguien timbra desde el panel. Puede llegarte antes de que termine la respuesta de tu propio POST.

Con la cancelación pasa algo parecido al principio. Al cancelar una factura sale un evento con el estado que devolvió el SAT en ese momento, que es el mismo que trae la respuesta: cancellation_requested si el receptor tiene que contestar, o cancellation_final si no hacía falta.

Lo que viene después sí es nuevo. Le preguntamos al SAT cada media hora por las cancelaciones que el receptor no ha contestado, y cuando cambia la respuesta te mandamos cancellation_accepted, cancellation_final o cancellation_rejected. Una respuesta que no cambia nada no manda evento. Si en 30 días no hubo respuesta dejamos de preguntar, y no llega ningún evento: consulta la factura.

En sandbox ninguna cancelación pide la aceptación del receptor. Ahí llegan stamped, cancellation_requested y, en la siguiente consulta, cancellation_final. cancellation_accepted y cancellation_rejected sólo llegan en producción.

Configurar un webhook

En el panel, en Cuenta → API, sección Webhooks. Igual que las llaves de API, están incluidos en los dos planes. Una cuenta puede tener varios, por ejemplo uno para el punto de venta y otro para la contabilidad. Si uno deja de contestar, los demás siguen recibiendo.

La URL tiene que usar https en el puerto 443, sin usuario ni contraseña, y apuntar a una dirección pública. En Eventos de facturas mexicanas marca los que quieres. Ninguno viene marcado: un evento mexicano sólo llega a un webhook que lo pidió.

Al guardarlo te mostramos su secreto, que empieza con whs_. Cópialo en ese momento: no lo volvemos a mostrar. Con él verificas la firma de cada aviso.

Rotar secreto cambia el secreto en ese momento, sin periodo en que valgan los dos. Desde ahí todo se firma con el nuevo, también los reintentos de eventos anteriores.

La lista de eventos no se edita. Para cambiarla, agrega otro webhook y apaga el anterior. Un webhook no se borra, se apaga. El panel muestra sus últimas entregas con el código que contestó tu servidor.

La petición

Oden-Event
El tipo de evento. Es el mismo valor que type en el cuerpo.
Oden-Delivery
El intento: whd_<entrega>_<intento>. Cambia en cada reintento, así que no sirve para detectar duplicados.
Oden-Signature
t=<segundos unix>,v1=<firma en hexadecimal>. Se calcula de nuevo en cada intento.
Content-Type
application/json
User-Agent
OdenTax-Webhooks/1.0
id
El id del evento, evt_…. Es el mismo en cada reintento y si el mismo cambio se anuncia dos veces. Úsalo para descartar duplicados.
type
El tipo de evento.
created_at
Cuándo se generó el evento, en ISO 8601.
data
La factura, con la misma forma que devuelve GET /v1/mx/invoices/:id en ese momento.

data es una foto de la factura cuando ocurrió el evento. Un reintento manda el mismo cuerpo, aunque la factura haya cambiado mientras tanto.

Verificar la firma

La firma es un HMAC-SHA256, con el secreto del webhook, de t, un punto y el cuerpo tal como llegó: "#{t}.#{cuerpo}". Calcúlalo tú y compáralo con v1 en tiempo constante.

Usa los bytes del cuerpo antes de parsear el JSON. Un JSON parseado y vuelto a serializar puede cambiar el orden o los espacios, y la firma ya no coincide.

t va dentro de lo firmado, así que no se puede cambiar sin romper la firma. Rechaza los avisos con un t de hace más de cinco minutos: así una petición interceptada no se puede repetir después. Cada reintento lleva un t nuevo, por lo que esta regla no afecta a los reintentos.

Qué contestar

Cualquier 2xx cuenta como entregado. Todo lo demás se reintenta, un 4xx también. Contesta 2xx a los eventos que no te interesan, o se reintentarán hasta agotar los intentos.

Esperamos 3 segundos para conectar y 5 para la respuesta. Si tu proceso tarda más, contesta primero y procesa después. No seguimos redirecciones: un 301 o un 302 cuenta como fallo. Lo que pongas en el cuerpo de la respuesta no se usa.

Reintentos

Siete intentos en unas 33 horas. Cada uno se cuenta desde el anterior.

1
En cuanto ocurre el evento
2
1 minuto después
3
5 minutos después
4
30 minutos después
5
2 horas después
6
6 horas después
7
24 horas después. Es el último.

Si 20 entregas seguidas agotan sus siete intentos, apagamos el webhook y le escribimos al dueño de la cuenta. Una sola entrega exitosa reinicia el conteo. Para volver a recibir, arregla tu servidor y pulsa Encender en el panel.

Mientras un webhook está apagado no se le genera nada, y lo que tenía pendiente ya no se manda. Al encenderlo no se reenvía lo que pasó en ese tiempo: consulta las facturas que te interesen.

Orden y duplicados

No prometemos orden. Cada entrega va por separado, y un reintento puede llegar después de un evento posterior. La factura no trae una fecha de modificación con la cual quedarte con el más nuevo, así que cuando el orden importe, consulta la factura: la respuesta dice dónde está ahora.

Un mismo evento puede llegarte más de una vez, por ejemplo si tu servidor lo procesó pero tardó más de 5 segundos en contestar. Descarta los repetidos por el id del cuerpo, que no cambia, y no por Oden-Delivery, que cambia en cada intento.