Saltearse al contenido

Registra un endpoint de webhook

POST
/v1/webhooks

Crea la suscripción y devuelve el secret UNA SOLA VEZ: es la clave HMAC con la que se firma cada entrega. No hay endpoint que la recupere — si se pierde, hay que borrar la suscripción y crear otra.

CampoTipoObligatorioNotas
urlhttpssíSe valida contra SSRF antes de guardar; http:// se rechaza
eventosarraysíAl menos uno del catálogo
incluirMetadataboolnoAdjunta los campos personalizados del CFE en datos.metadata
descripcionstringno

sucursalId no se acepta por cuerpo: el scope sale de la credencial y de x-branch-id. Una credencial no fijada a sucursal recibe los eventos de toda la cuenta.

Tope: 20 suscripciones activas por cuenta (409 WEBHOOK_LIMITE_ALCANZADO). Cada suscripción multiplica las entregas de CADA evento.

Catálogo de eventos suscribibles

EventoQué significa
cfe.emitidoCFE emitido
cfe.actualizadoCambió el estado de un CFE en DGI
cfe_recibido.creadoLlegó un CFE de otro emisor
cfe_recibido.actualizadoCambió un CFE recibido
reporte_diario.aceptadoReporte diario aceptado por DGI
reporte_diario.rechazadoReporte diario RECHAZADO por DGI
reporte_diario.errorEl reporte diario falló antes de llegar a DGI
cae.por_vencerUn CAE está por vencer
cae.por_agotarseUn CAE está por agotar su numeración
certificado.por_vencerEl certificado digital está por vencer
recurrente.emitidoSe emitió una factura recurrente
pago.confirmadoEl cobro se confirmó: la plata entró
pago.fallidoEl cobro fue rechazado de forma definitiva
pago.anuladoEl cobro se anuló (total o parcialmente)
pago.facturadoSe emitió el comprobante fiscal del cobro
pago.pendiente_facturacionPlata cobrada SIN comprobante
pago.en_verificacionEstado AMBIGUO del cobro: puede estar cobrado
link.anuladoEl link de cobro se anuló antes de pagarse
link.expiradoEl link de cobro venció sin pagarse
suscripcion.cobro_exitosoSe cobró un período de la suscripción
suscripcion.cobro_fallidoDeclinó el cobro de un período
suscripcion.canceladaSe dio de baja la suscripción
suscripcion.pausadaLa suscripción se pausó
suscripcion.reanudadaLa suscripción volvió a cobrar
tarjeta.enroladaSe enroló una tarjeta del cliente
tarjeta.bajaSe dio de baja una tarjeta

El envelope es { id, tipo, creadoEn, cuentaId, sucursalId, datos } y la firma viaja en X-HostFactura-Signature: t=<epoch>,v1=<hmac_sha256 de "<t>.<cuerpo raw>">. Deduplicá por X-HostFactura-Event-Id: el mismo evento puede entregarse más de una vez.

El payload (datos) de cada tipo está en la extensión x-webhooks de este spec (components/schemas/WebhookEvento_<tipo>), con un ejemplo por evento.

Alcance requerido: webhooks:gestionar. Si la credencial no lo tiene, la respuesta es 403 API_ALCANCE_INSUFICIENTE con la lista en error.requerido. Las credenciales creadas antes del modelo de alcances (alcances: null) conservan acceso total.

La empresa sale de la credencial. No hay ningún parámetro ni campo de cuerpo que la elija: cada API_KEY opera sobre su propia EFacturaCuenta. La ÚNICA excepción es la cabecera x-cuenta-id de una credencial de revendedor, que apunta a una subcuenta de su cartera. Si integrás empresas que no son de tu cartera, usá una API_KEY por empresa.

Revendedores: una credencial de una cuenta proveedora puede operar sobre una subcuenta de su cartera mandando x-cuenta-id: <uuid de la subcuenta>. Si la subcuenta no está en la cartera, la respuesta es 403 API_CUENTA_FUERA_DE_CARTERA.

Idempotency-Key
string
>= 8 characters <= 128 characters /^[A-Za-z0-9._:~-]{8,128}$/
Ejemplo
orden-12345-cobro-1

Clave de idempotencia de la operación (8 a 128 caracteres de A-Za-z0-9._:~-).

Con la misma clave y el mismo cuerpo, un reintento replica la respuesta original con meta.reusada: true y no vuelve a ejecutar nada. La misma clave con otro cuerpo —u otro endpoint— devuelve 409 PAGOS_IDEMPOTENCY_KEY_CONFLICTO; con la operación todavía en curso, 409 PAGOS_IDEMPOTENCY_EN_CURSO.

Sólo se cachean las respuestas 2xx: un 4xx/5xx libera la clave, así que podés corregir el cuerpo y reintentar con la MISMA clave. La ventana es de 24 horas.

x-cuenta-id
string format: uuid
Ejemplo
0199d1b0-0000-7000-8000-000000000000

Sólo para credenciales de cuenta proveedora (revendedor). UUID de la subcuenta de la cartera sobre la que se quiere operar: la cuenta efectiva del request pasa a ser esa.

Una credencial que no es de proveedor, o una subcuenta que no está en su cartera, recibe 403 API_CUENTA_FUERA_DE_CARTERA. Sin la cabecera, la cuenta es siempre la de la credencial.

x-branch-id
string format: uuid

Sucursal contra la que operar, cuando la credencial es global. Si la credencial está fijada a una sucursal y esta cabecera indica otra, la respuesta es 403 API_ACCESS_BRANCH_MISMATCH. Sin la cabecera se asume la casa central.

x-point-id
string format: uuid

Punto de emisión dentro de la sucursal resuelta. Obligatorio cuando la sucursal tiene 2 o más puntos activos (400 PUNTO_EMISION_REQUERIDO si falta); con uno solo se selecciona automáticamente.

url, eventos[], incluirMetadata?, descripcion?

object
Ejemplos
{
"url": "https://integrador.ejemplo.com/hooks/hostfactura",
"eventos": [
"pago.confirmado",
"pago.facturado",
"pago.pendiente_facturacion"
],
"descripcion": "Cobros y su puente fiscal"
}

Suscripción creada. El secret no se vuelve a mostrar.

object
Ejemplos
{
"data": {
"id": "0199d1b0-0000-7000-8000-000000000000",
"url": "https://integrador.ejemplo.com/hooks/hostfactura",
"eventos": [
"pago.confirmado"
],
"activo": true,
"secret": "whsec_0ea1...solo-esta-vez"
}
}

No autenticado. La API_KEY no fue enviada, no es válida o la credencial está desactivada.

Códigos posibles: API_AUTH_HEADER_MISSING, API_AUTH_HEADER_INVALID, API_ACCESS_INVALID.

object
error
required
object
code
required

Identificador estable del error en SCREAMING_SNAKE_CASE

string
message
required

Mensaje legible en español. No parsearlo: puede cambiar

string
details
Any of:
object
requerido

Sólo en 403 API_ALCANCE_INSUFICIENTE: alcances que habilitan la operación

Array<string>
Allowed values: cfe:emitir cfe:leer recibidos:leer consultas clientes:leer clientes:escribir pagos:leer pagos:cobrar pagos:tarjetas pagos:suscripciones pagos:configurar webhooks:gestionar
requestId

UUID de correlación; mismo valor que el header X-Request-ID

string format: uuid
Ejemplos

Falta header Authorization

{
"error": {
"code": "API_AUTH_HEADER_MISSING",
"message": "Se esperaba la cabecera Authorization con esquema Bearer"
},
"requestId": "5b2c7c8a-1f6e-4d29-9a0b-7c3a8d1e2f4c"
}

El módulo de cobros no está habilitado para esta cuenta, o la credencial no tiene el alcance que la operación exige. Son dos cosas distintas y se corrigen distinto:

CodeQué pasóCómo se corrige
PAGOS_NO_HABILITADOLa cuenta no tiene el módulo de cobrosEs una habilitación comercial: la hace administración, no se activa desde la API
API_ALCANCE_INSUFICIENTELa credencial no tiene el alcance (va en error.requerido)Editar la credencial en Integraciones → API
API_CUENTA_FUERA_DE_CARTERAx-cuenta-id apunta a una cuenta ajena a la carteraRevisar el UUID de la subcuenta

Ninguno se arregla reintentando.

object
error
required
object
code
required

Identificador estable del error en SCREAMING_SNAKE_CASE

string
message
required

Mensaje legible en español. No parsearlo: puede cambiar

string
details
Any of:
object
requerido

Sólo en 403 API_ALCANCE_INSUFICIENTE: alcances que habilitan la operación

Array<string>
Allowed values: cfe:emitir cfe:leer recibidos:leer consultas clientes:leer clientes:escribir pagos:leer pagos:cobrar pagos:tarjetas pagos:suscripciones pagos:configurar webhooks:gestionar
requestId

UUID de correlación; mismo valor que el header X-Request-ID

string format: uuid
Ejemplos

La cuenta no tiene el módulo de cobros

{
"error": {
"code": "PAGOS_NO_HABILITADO",
"message": "El módulo de pagos no está habilitado para esta cuenta. Es una habilitación comercial: la hace administración, no se activa desde la API."
},
"requestId": "4c5d6e7f-8a9b-4c0d-1e2f-3a4b5c6d7e8f"
}

Se alcanzó el tope de suscripciones activas

object
error
required
object
code
required

Identificador estable del error en SCREAMING_SNAKE_CASE

string
message
required

Mensaje legible en español. No parsearlo: puede cambiar

string
details
Any of:
object
requerido

Sólo en 403 API_ALCANCE_INSUFICIENTE: alcances que habilitan la operación

Array<string>
Allowed values: cfe:emitir cfe:leer recibidos:leer consultas clientes:leer clientes:escribir pagos:leer pagos:cobrar pagos:tarjetas pagos:suscripciones pagos:configurar webhooks:gestionar
requestId

UUID de correlación; mismo valor que el header X-Request-ID

string format: uuid
Ejemplos
{
"error": {
"code": "WEBHOOK_LIMITE_ALCANZADO",
"message": "Se alcanzó el máximo de 20 suscripciones activas para esta cuenta"
}
}

La URL no es válida o apunta a una red interna

object
error
required
object
code
required

Identificador estable del error en SCREAMING_SNAKE_CASE

string
message
required

Mensaje legible en español. No parsearlo: puede cambiar

string
details
Any of:
object
requerido

Sólo en 403 API_ALCANCE_INSUFICIENTE: alcances que habilitan la operación

Array<string>
Allowed values: cfe:emitir cfe:leer recibidos:leer consultas clientes:leer clientes:escribir pagos:leer pagos:cobrar pagos:tarjetas pagos:suscripciones pagos:configurar webhooks:gestionar
requestId

UUID de correlación; mismo valor que el header X-Request-ID

string format: uuid
Ejemplos
{
"error": {
"code": "WEBHOOK_URL_INVALIDA",
"message": "La URL del webhook debe ser https"
}
}

Rate limit excedido. Esperá los segundos del header Retry-After. La cuota va por credencial: 60 req/min en cuentas estándar, 600 en cuentas proveedor.

object
error
required
object
code
required

Identificador estable del error en SCREAMING_SNAKE_CASE

string
message
required

Mensaje legible en español. No parsearlo: puede cambiar

string
details
Any of:
object
requerido

Sólo en 403 API_ALCANCE_INSUFICIENTE: alcances que habilitan la operación

Array<string>
Allowed values: cfe:emitir cfe:leer recibidos:leer consultas clientes:leer clientes:escribir pagos:leer pagos:cobrar pagos:tarjetas pagos:suscripciones pagos:configurar webhooks:gestionar
requestId

UUID de correlación; mismo valor que el header X-Request-ID

string format: uuid
Ejemplos

Excediste el rate limit

{
"error": {
"code": "API_RATE_LIMITED",
"message": "Se superó el límite de requests para esta API key"
},
"requestId": "5b2c7c8a-1f6e-4d29-9a0b-7c3a8d1e2f4c"
}
Retry-After
integer
Ejemplo
42

Segundos hasta que se libera la ventana de rate limit

No se pudo verificar la habilitación del módulo (PAGOS_ESTADO_INDETERMINADO), o falta la clave maestra de cifrado de secretos (PAGOS_CLAVE_MAESTRA_AUSENTE). Nada que corregir del lado del integrador: reintentar.

object
error
required
object
code
required

Identificador estable del error en SCREAMING_SNAKE_CASE

string
message
required

Mensaje legible en español. No parsearlo: puede cambiar

string
details
Any of:
object
requerido

Sólo en 403 API_ALCANCE_INSUFICIENTE: alcances que habilitan la operación

Array<string>
Allowed values: cfe:emitir cfe:leer recibidos:leer consultas clientes:leer clientes:escribir pagos:leer pagos:cobrar pagos:tarjetas pagos:suscripciones pagos:configurar webhooks:gestionar
requestId

UUID de correlación; mismo valor que el header X-Request-ID

string format: uuid
Ejemplos

No se pudo leer el flag de pagos

{
"error": {
"code": "PAGOS_ESTADO_INDETERMINADO",
"message": "No se pudo verificar si el módulo de pagos está habilitado para esta cuenta. Reintentá."
},
"requestId": "0f4a1c2e-5b6d-4e7f-8a9b-0c1d2e3f4a5b"
}