Avisos: contenido y catálogo de eventos
La forma común de los avisos que UTILIA OS envía a tu aplicación y el catálogo completo de eventos, con ejemplos del contenido por área.
Actualizado el . 14 min de lectura
Incluido en todos los planes. Ver precios
Esta página describe la forma común de todos los avisos que UTILIA OS envía a tu servidor y el catálogo completo de eventos, con ejemplos del contenido de los más usados. Cómo configurarlos y comprobar su firma se explica en Avisos a tu servidor: visión general.
Forma común de los avisos
- eventstringobligatorio
- Nombre del evento.
- timestampstringobligatorio
- Fecha y hora de emisión, en ISO 8601. Es informativa: la firma usa la cabecera X-Utilia-Timestamp.
- app.idstringobligatorio
- Identificador de tu aplicación externa.
- app.slugstringobligatorio
- Identificador legible de tu aplicación.
- dataobjectobligatorio
- Datos propios de cada evento.
- meta.schemaVersionstringobligatorio
- Versión de la forma del contenido (2026-05; 2026-07 en los eventos de chat). Viaja también en la cabecera X-Utilia-Schema-Version.
Enrutado: dirigido o por difusión
Cada evento del catálogo se entrega de una de dos maneras, y la diferencia importa porque decide qué recibe tu aplicación:
- Dirigido: solo llega a la aplicación dueña del recurso. Todos los avisos de suscripciones y de cobros son dirigidos, y también los de facturas, métodos de pago y autorizaciones cuando el recurso pertenece a un usuario de una aplicación externa. Sin aplicación dueña no hay destinatario.
- Por difusión: llega a todas las aplicaciones suscritas a ese evento. Es como viajan los avisos de los recursos propios de la organización, que no pertenecen a ninguna aplicación en concreto.
Un mismo nombre de evento puede viajar de las dos formas según el recurso: una factura emitida por tu aplicación te llega dirigida, y una emitida por el equipo desde el panel viaja por difusión.
Catálogo de eventos
Tickets de soporte
- TICKET_CREATED
- TICKET_REPLIED
- TICKET_RESOLVED
- TICKET_CLOSED
- TICKET_CONVERTED
- TICKET_ASSIGNED
- TICKET_REOPENED
Usuarios externos
- USER_CREATED
Archivos
- FILE_UPLOADED
Errores del sistema
- ERROR_REPORTED
- ERROR_RESOLVED
- ERROR_REOPENED
- ERROR_SILENCED
- ERROR_LINKED_TO_TICKET
- ERROR_LINKED_TO_ISSUE
Los tres últimos se disparan cuando una persona del equipo actúa sobre un grupo de errores desde la ficha de la aplicación: lo silencia hasta una fecha o lo vincula con un ticket de soporte o con una tarea de Gestión de proyectos.
Presupuestos
- BUDGET_CREATED
- BUDGET_UPDATED
- BUDGET_SENT
- BUDGET_APPROVED
- BUDGET_REJECTED
- BUDGET_CANCELLED
- BUDGET_EXPIRED
- BUDGET_DUPLICATED
- BUDGET_CONVERTED_TO_PROJECT
- BUDGET_CONVERTED_TO_INVOICE
- BUDGET_CONVERTED_TO_RECURRING_INVOICE
- BUDGET_COMMENT_ADDED
- BUDGET_COMMENT_UPDATED
- BUDGET_COMMENT_DELETED
- BUDGET_SIGNED
- BUDGET_SIGNING_LINK_GENERATED
- BUDGET_SIGNING_LINK_REVOKED
- BUDGET_VIEWED
- BUDGET_PARTIALLY_SIGNED
- BUDGET_SIGNING_LINK_REGENERATED
- BUDGET_SIGNING_LINK_EXPIRED
- BUDGET_VALIDITY_EXTENDED
- BUDGET_IMAGE_PLACEMENT_CREATED
- BUDGET_IMAGE_PLACEMENT_UPDATED
- BUDGET_IMAGE_PLACEMENT_DELETED
- BUDGET_IMAGE_PLACEMENT_REPOSITIONED
- BUDGET_IMAGE_PLACEMENT_CROPPED
- BUDGET_IMAGE_PLACEMENT_REORDERED
Como el resto, los de firma electrónica y los de imágenes solo llegan a las aplicaciones que los marquen en su configuración de avisos.
Facturación
- INVOICE_CREATED
- INVOICE_SENT
- INVOICE_PAID
- INVOICE_PARTIALLY_PAID
- INVOICE_PAYMENT_FAILED
- INVOICE_OVERDUE
- INVOICE_CANCELLED
- INVOICE_REFUNDED
- INVOICE_REFUND_CANCELLED
- INVOICE_RECTIFIED
- INVOICE_RECTIFICATION_CREATED
- INVOICE_RECTIFICATION_METADATA_UPDATED
Métodos de pago
- PAYMENT_METHOD_ADDED
- PAYMENT_METHOD_REMOVED
- PAYMENT_METHOD_EXPIRING
Autorizaciones de cobro
- PAYMENT_AUTHORIZATION_GRANTED
- PAYMENT_AUTHORIZATION_REVOKED
- PAYMENT_AUTHORIZATION_CONSUMED
Suscripciones
- SUBSCRIPTION_CREATED
- SUBSCRIPTION_UPDATED
- SUBSCRIPTION_PAUSED
- SUBSCRIPTION_RESUMED
- SUBSCRIPTION_CANCELLED
- SUBSCRIPTION_COMPLETED
- SUBSCRIPTION_INVOICE_GENERATED
- SUBSCRIPTION_PAYMENT_AUTHORIZATION_PENDING
Cobros programados
- CHARGE_SCHEDULED
- CHARGE_STARTED
- CHARGE_SUCCEEDED
- CHARGE_FAILED
- CHARGE_REQUIRES_ACTION
- CHARGE_RETRY_SCHEDULED
- CHARGE_RETRIES_EXHAUSTED
- CHARGE_CANCELLED
Contactos del CRM
- CONTACT_CREATED
- CONTACT_UPDATED
- CONTACT_DELETED
- CONTACT_OPTED_OUT
- CONTACT_OPTED_IN
Leads del CRM
- LEAD_CREATED
- LEAD_UPDATED
- LEAD_QUALIFIED
- LEAD_DISQUALIFIED
- LEAD_CONVERTED
- LEAD_SCORE_UPDATED
Clientes del CRM
- CLIENT_CREATED
- CLIENT_UPDATED
- CLIENT_DEACTIVATED
- CLIENT_REACTIVATED
- CLIENT_FISCAL_DATA_UPDATED
- CLIENT_LINKED_TO_GROUP
- CLIENT_UNLINKED_FROM_GROUP
Grupos de empresas. CLIENT_LINKED_TO_GROUP y CLIENT_UNLINKED_FROM_GROUP avisan de que una filial se vincula a su empresa matriz o se desvincula. Viajan solo con identificadores (parentClientId, childClientId, organizationId y, en la desvinculación, reason): si necesitas los datos de los clientes, pídelos a la API. Cambiar el vínculo no dispara CLIENT_UPDATED, y al disolver un grupo llega un CLIENT_UNLINKED_FROM_GROUP por filial con reason=DISSOLVE.
Sin bucles. Los eventos CONTACT_*, LEAD_* y CLIENT_* llegan por difusión a todas las aplicaciones suscritas, salvo a la que provocó el cambio: si tu aplicación registra la baja de un contacto, no recibe el aviso de su propia baja. LEAD_CONVERTED incluye contactId y, si se creó, opportunityId. No existen LEAD_DELETED ni CLIENT_DELETED: la API externa no borra leads ni clientes, los descarta (/disqualify) o los desactiva (/deactivate). El IBAN no viaja nunca en un aviso de cliente. Detalle en Contactos, Leads y Clientes.
Tarjeta guardada no es permiso para cobrar. Los eventos PAYMENT_AUTHORIZATION_* cubren la firma y la retirada del consentimiento con el que la organización cobra suscripciones y cobros programados sin el usuario delante. Son distintos de PAYMENT_METHOD_*: guardar una tarjeta (PAYMENT_METHOD_ADDED) no autoriza cobros automáticos; para eso el usuario firma un texto de consentimiento con su versión.
Facturas dirigidas o por difusión. Los eventos INVOICE_* de una factura emitida por una aplicación externa (source = EXTERNAL_APP) solo llegan a esa aplicación. Los de una factura emitida desde el panel (source = UTILIA_NATIVE) llegan a todas las aplicaciones suscritas.
La divisa llega en minúsculas
Los campos currency de los avisos de factura y de pago llegan en minúsculas (eur, usd), y eur es el valor por defecto cuando no se indicó divisa. Compara siempre sin distinguir mayúsculas y minúsculas (currency.toLowerCase() === 'eur') o tu condición fallará sin avisar.
Plataforma de chat
- CHAT_MESSAGE_CREATED
- CHAT_MESSAGE_UPDATED
- CHAT_MESSAGE_DELETED
- CHAT_CHANNEL_CREATED
- CHAT_CHANNEL_UPDATED
- CHAT_CHANNEL_ARCHIVED
- CHAT_MEMBER_ADDED
- CHAT_MEMBER_REMOVED
- CHAT_APP_MENTIONED
- CHAT_AGENT_TASK_EXECUTED
- CHAT_AGENT_PENDING_APPROVAL
- CHAT_AGENT_APPROVED
- CHAT_AGENT_REJECTED
- CHAT_AGENT_BUDGET_EXCEEDED
Los eventos CHAT_* tienen su propia forma y viajan con meta.schemaVersion = 2026-07 (el resto usa 2026-05). Son dirigidos: solo llegan a las aplicaciones que participan en el canal y tienen el permiso correspondiente, y nunca transportan mensajes internos ni privados. Su contenido y ejemplos de receptor están en Webhooks de chat.
Ejemplos del contenido
TICKET_CREATED
{
"event": "TICKET_CREATED",
"timestamp": "2026-02-08T00:00:00.000Z",
"app": { "id": "app_1", "slug": "mi-aplicacion" },
"data": {
"ticket": {
"id": "t_1",
"ticketKey": "mi-aplicacion-0001",
"title": "No puedo iniciar sesión",
"category": "BUG",
"priority": "MEDIA",
"status": "OPEN",
"createdAt": "2026-02-08T00:00:00.000Z"
},
"user": {
"externalId": "user_01HXYZ",
"email": "cliente@ejemplo.com",
"name": "Ana Cliente"
}
}
}ERROR_REPORTED
{
"event": "ERROR_REPORTED",
"timestamp": "2026-02-27T12:00:00.000Z",
"app": { "id": "app_1", "slug": "mi-aplicacion" },
"data": {
"hash": "a1b2c3d4e5f6...",
"message": "Connection timeout to payment gateway",
"module": "payment-processor",
"severity": "critical",
"deduplicated": false
}
}deduplicated llega siempre con valor false y se conserva solo por compatibilidad del contrato: el webhook se emite una única vez por hash, en la primera ocurrencia del error, y las repeticiones posteriores incrementan el contador interno sin generar entrega. No construyas lógica sobre este campo ni esperes un aviso por cada repetición.
ERROR_RESOLVED
{
"event": "ERROR_RESOLVED",
"timestamp": "2026-02-27T14:00:00.000Z",
"app": { "id": "app_1", "slug": "mi-aplicacion" },
"data": {
"hash": "a1b2c3d4e5f6...",
"resolvedBy": "user-admin-id",
"note": "Reiniciado el servicio de pagos"
}
}ERROR_REOPENED
{
"event": "ERROR_REOPENED",
"timestamp": "2026-02-27T16:00:00.000Z",
"app": { "id": "app_1", "slug": "mi-aplicacion" },
"data": {
"hash": "a1b2c3d4e5f6...",
"reopenedBy": "user-admin-id"
}
}ERROR_SILENCED
until es la fecha hasta la que el grupo de errores queda silenciado. silencedBy es nulo cuando la acción no la origina una persona.
{
"event": "ERROR_SILENCED",
"timestamp": "2026-08-04T09:15:00.000Z",
"app": { "id": "app_1", "slug": "mi-aplicacion" },
"data": {
"hash": "a1b2c3d4e5f6...",
"until": "2026-08-11T09:15:00.000Z",
"silencedBy": "user-admin-id"
}
}ERROR_LINKED_TO_TICKET
{
"event": "ERROR_LINKED_TO_TICKET",
"timestamp": "2026-08-04T09:20:00.000Z",
"app": { "id": "app_1", "slug": "mi-aplicacion" },
"data": {
"hash": "a1b2c3d4e5f6...",
"ticket": { "id": "tkt_123", "ticketKey": "SUP-482" },
"linkedBy": "user-admin-id"
}
}ERROR_LINKED_TO_ISSUE
{
"event": "ERROR_LINKED_TO_ISSUE",
"timestamp": "2026-08-04T09:25:00.000Z",
"app": { "id": "app_1", "slug": "mi-aplicacion" },
"data": {
"hash": "a1b2c3d4e5f6...",
"issue": { "id": "iss_456", "key": "PROJ-123" },
"linkedBy": "user-admin-id"
}
}CLIENT_LINKED_TO_GROUP
{
"event": "CLIENT_LINKED_TO_GROUP",
"timestamp": "2026-07-05T10:15:00.000Z",
"app": { "id": "app_1", "slug": "mi-aplicacion" },
"data": {
"parentClientId": "cli-matriz-uuid",
"childClientId": "cli-filial-uuid",
"organizationId": "org-uuid",
"occurredAt": "2026-07-05T10:15:00.000Z"
}
}CLIENT_UNLINKED_FROM_GROUP
{
"event": "CLIENT_UNLINKED_FROM_GROUP",
"timestamp": "2026-07-05T11:40:00.000Z",
"app": { "id": "app_1", "slug": "mi-aplicacion" },
"data": {
"parentClientId": "cli-matriz-uuid",
"childClientId": "cli-filial-uuid",
"organizationId": "org-uuid",
"reason": "DISSOLVE",
"occurredAt": "2026-07-05T11:40:00.000Z"
}
}INVOICE_CREATED
{
"event": "INVOICE_CREATED",
"timestamp": "2026-04-19T10:32:15.000Z",
"app": { "id": "app_1", "slug": "mi-aplicacion" },
"data": {
"invoice": {
"id": "inv-uuid",
"invoiceNumber": "202600001",
"status": "ISSUED",
"totalAmount": 53.45,
"paidAmount": 0,
"currency": "eur",
"issueDate": "2026-04-19T00:00:00.000Z",
"dueDate": null
},
"externalUser": { "externalId": "user_01HXYZ" },
"metadata": { "orderId": "ORD-4471" }
}
}metadata solo viaja en INVOICE_CREATED: es el objeto metadata que tu aplicación envió al crear la factura por la API externa, devuelto tal cual salvo la clave externalIdempotencyKey, que se quita antes de enviarlo. Llega null si la factura se creó sin metadata.
INVOICE_PAID
{
"event": "INVOICE_PAID",
"timestamp": "2026-04-20T11:22:33.000Z",
"app": { "id": "app_1", "slug": "mi-aplicacion" },
"data": {
"invoice": {
"id": "inv-uuid",
"invoiceNumber": "202600001",
"status": "PAID",
"totalAmount": 53.45,
"paidAmount": 53.45,
"currency": "eur",
"issueDate": "2026-04-19T00:00:00.000Z",
"dueDate": null
},
"externalUser": { "externalId": "user_01HXYZ" },
"payment": {
"paymentIntentId": "pi-utilia-uuid",
"amount": 53.45,
"currency": "eur",
"isFullPayment": true
}
}
}INVOICE_PARTIALLY_PAID
{
"event": "INVOICE_PARTIALLY_PAID",
"timestamp": "2026-04-20T11:22:33.000Z",
"app": { "id": "app_1", "slug": "mi-aplicacion" },
"data": {
"invoice": {
"id": "inv-uuid",
"invoiceNumber": "202600001",
"status": "PARTIALLY_PAID",
"totalAmount": 100.00,
"paidAmount": 53.45,
"currency": "eur",
"issueDate": "2026-04-19T00:00:00.000Z",
"dueDate": null
},
"externalUser": { "externalId": "user_01HXYZ" },
"payment": {
"paymentIntentId": "pi-utilia-uuid",
"amount": 53.45,
"currency": "eur",
"isFullPayment": false
}
}
}INVOICE_PAYMENT_FAILED
{
"event": "INVOICE_PAYMENT_FAILED",
"timestamp": "2026-04-20T11:10:05.000Z",
"app": { "id": "app_1", "slug": "mi-aplicacion" },
"data": {
"invoice": {
"id": "inv-uuid",
"invoiceNumber": "202600001",
"status": "ISSUED",
"totalAmount": 53.45,
"paidAmount": 0,
"currency": "eur",
"issueDate": "2026-04-19T00:00:00.000Z",
"dueDate": null
},
"externalUser": { "externalId": "user_01HXYZ" },
"payment": {
"paymentIntentId": "pi-utilia-uuid",
"amount": 53.45,
"currency": "eur",
"errorCode": "card_declined",
"errorMessage": "Tu banco ha rechazado la tarjeta.",
"initiator": "SYSTEM_RECURRING"
}
}
}INVOICE_OVERDUE
{
"event": "INVOICE_OVERDUE",
"timestamp": "2026-05-20T06:00:00.000Z",
"app": { "id": "app_1", "slug": "mi-aplicacion" },
"data": {
"invoice": {
"id": "inv-uuid",
"invoiceNumber": "202600001",
"status": "OVERDUE",
"totalAmount": 53.45,
"paidAmount": 0,
"currency": "eur",
"issueDate": "2026-04-19T00:00:00.000Z",
"dueDate": "2026-05-19T00:00:00.000Z"
},
"externalUser": { "externalId": "user_01HXYZ" }
}
}INVOICE_CANCELLED
{
"event": "INVOICE_CANCELLED",
"timestamp": "2026-04-20T15:00:00.000Z",
"app": { "id": "app_1", "slug": "mi-aplicacion" },
"data": {
"invoice": {
"id": "inv-uuid",
"invoiceNumber": "202600001",
"status": "CANCELLED",
"totalAmount": 53.45,
"paidAmount": 0,
"currency": "eur",
"issueDate": "2026-04-19T00:00:00.000Z",
"dueDate": null
},
"externalUser": { "externalId": "user_01HXYZ" },
"reason": "Factura duplicada"
}
}INVOICE_REFUNDED
{
"event": "INVOICE_REFUNDED",
"timestamp": "2026-04-22T09:15:00.000Z",
"app": { "id": "app_1", "slug": "mi-aplicacion" },
"data": {
"invoice": {
"id": "inv-uuid",
"invoiceNumber": "202600001",
"status": "PARTIALLY_PAID",
"totalAmount": 53.45,
"paidAmount": 0,
"currency": "eur",
"issueDate": "2026-04-19T00:00:00.000Z",
"dueDate": null
},
"externalUser": { "externalId": "user_01HXYZ" },
"refund": {
"refundId": "ref-uuid",
"paymentIntentId": "pi-utilia-uuid",
"amount": 5345,
"currency": "eur",
"reason": "requested_by_customer",
"isFullRefund": true,
"source": "STRIPE"
}
}
}refund.amount viaja en céntimos, payment.amount no
refund.amount es el importe devuelto en céntimos (en el ejemplo, 5345 son 53,45 €). Los demás importes de los avisos de factura van en unidades de la divisa: invoice.totalAmount, invoice.paidAmount y también payment.amount de INVOICE_PAID, INVOICE_PARTIALLY_PAID e INVOICE_PAYMENT_FAILED. Si lo sumas o lo comparas con ellos sin convertir, obtendrás una cifra cien veces mayor. refund.source dice por dónde se devolvió: STRIPE, MANUAL_BANK, MANUAL_CASH o MANUAL_OTHER.
PAYMENT_METHOD_ADDED
{
"event": "PAYMENT_METHOD_ADDED",
"timestamp": "2026-04-10T12:00:00.000Z",
"app": { "id": "app_1", "slug": "mi-aplicacion" },
"data": {
"externalUser": { "externalId": "user_01HXYZ" },
"paymentMethod": {
"id": "pm-uuid",
"brand": "visa",
"last4": "4242"
}
}
}PAYMENT_METHOD_REMOVED
{
"event": "PAYMENT_METHOD_REMOVED",
"timestamp": "2026-04-15T09:30:00.000Z",
"app": { "id": "app_1", "slug": "mi-aplicacion" },
"data": {
"externalUser": { "externalId": "user_01HXYZ" },
"paymentMethod": {
"id": "pm-uuid",
"brand": "visa",
"last4": "4242"
}
}
}PAYMENT_AUTHORIZATION_GRANTED
Se dispara cada vez que un cliente firma una autorización de cobro automático sobre una tarjeta concreta. Una reactivación tras revocar no reusa el mandato anterior: emite un evento nuevo con mandateId distinto.
Forma del campo data
- mandateIdstringobligatorio
- Identificador interno UTILIA del mandato firmado. Único e idempotente.
- clientIdstringobligatorio
- Identificador del cliente al que pertenece la autorización.
- scopeenumobligatorio
- Alcance del mandato: SINGLE_INVOICE, PER_INVOICE_OPT_IN, SCHEDULED_INVOICE, RECURRING o RECURRING_SUBSCRIPTION.
- paymentMethod.idstringobligatorio
- Identificador de UTILIA OS del método de pago, no el de Stripe. Llega como cadena vacía en el caso límite de un mandato sin tarjeta vinculada, así que comprueba que no está vacío antes de usarlo como clave.
- paymentMethod.brandstring
- Marca de la tarjeta (visa, mastercard, amex, etc.). Puede ser null.
- paymentMethod.last4string
- Últimos cuatro dígitos. Puede ser null.
- paymentMethod.expMonthnumber
- Mes de caducidad (1-12). Puede ser null.
- paymentMethod.expYearnumber
- Año de caducidad (cuatro dígitos). Puede ser null.
- consent.versionstringobligatorio
- Versión del texto legal aceptado (no cambia aunque la plantilla evolucione).
- consent.ipAddressstring
- IP enmascarada con los dos primeros octetos (formato a.b.x.x para IPv4 o a:b:x:x:x para IPv6). Puede ser null si el proxy no la pudo resolver.
- consent.userAgentstring
- User-Agent del navegador o cliente que firmó (truncado a 500 caracteres). Puede no llegar.
- consent.signedAtdatetimeobligatorio
- Fecha y hora ISO 8601 de aceptación.
- linkedSubscriptionIdsstring[]obligatorio
- Suscripciones (RecurringInvoice) vinculadas al mandato en el instante de la activación. Puede crecer después: consulta la API antes de cada cobro para tener la lista vigente.
- sourceenum
- Origen del flujo: PORTAL_ADD_CARD, PORTAL_PAY_INVOICE, PORTAL_SUBSCRIPTION, PUBLIC_SETUP_INTENT, STAFF_DASHBOARD u OTHER. Puede ser null.
Ejemplo
{
"event": "PAYMENT_AUTHORIZATION_GRANTED",
"timestamp": "2026-05-09T18:42:13.000Z",
"app": { "id": "app_1", "slug": "mi-aplicacion" },
"data": {
"mandateId": "pma_01HQ2X7C9MZK3FQAGBYQ8K2W4P",
"clientId": "cli_01HXYZK6Z3AQAGBYQ8K2W4P",
"scope": "RECURRING_SUBSCRIPTION",
"paymentMethod": {
"id": "pm_01HQ2X7C9MZK3FQAGBYQ8K2W4P",
"brand": "visa",
"last4": "4242",
"expMonth": 4,
"expYear": 2029
},
"consent": {
"version": "v1.2",
"ipAddress": "203.0.x.x",
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_2) AppleWebKit/537.36",
"signedAt": "2026-05-09T18:42:13.000Z"
},
"linkedSubscriptionIds": ["rec_01HXYZK6Z3AQAGBYQ8K2W4P"],
"source": "PORTAL_ADD_CARD"
}
}La IP de consent.ipAddress llega recortada por protección de datos: en IPv4 se conservan los dos primeros bloques y los dos últimos se sustituyen por x.x (por ejemplo, 203.0.x.x); en IPv6 se conservan los dos primeros segmentos y el resto se sustituye por x:x:x. La IP completa queda en el registro de auditoría de UTILIA OS y no se envía por aviso.
paymentMethod.id es el identificador de UTILIA OS del método de pago, estable entre avisos del mismo método. No es el identificador de Stripe, que no se envía nunca.
PAYMENT_AUTHORIZATION_REVOKED
Se dispara cuando una autorización deja de valer. Las suscripciones afectadas pasan a cobro manual en UTILIA OS; si guardas una copia en tu sistema, refleja el cambio.
Forma del campo data
- mandateIdstringobligatorio
- Identificador del mandato revocado. Coincide con el mandateId del GRANTED previo.
- clientIdstringobligatorio
- Identificador del cliente al que pertenecía la autorización.
- scopeenumobligatorio
- Mismo enum que en GRANTED, pero NO es fiable en la revocación: si el mandato ya se ha marcado como revocado en base de datos, el emisor no puede recuperar su alcance original y envía siempre RECURRING_SUBSCRIPTION. No filtres por este campo en los eventos REVOKED.
- revokedByenumobligatorio
- Quién inició la revocación: PORTAL_USER, STAFF, SYSTEM_PAYMENT_METHOD_EXPIRED o SYSTEM_OTHER.
- revokedAtdatetimeobligatorio
- Fecha y hora ISO 8601 en la que se efectuó la revocación.
- revocationReasonenumobligatorio
- Motivo categorizado: USER_REVOKED, STAFF_REVOKED, PAYMENT_METHOD_EXPIRED, PAYMENT_METHOD_DETACHED u OTHER.
- affectedSubscriptionIdsstring[]obligatorio
- Suscripciones del cliente afectadas por la revocación. UTILIA OS las pasa a cobro manual; tu aplicación debería reflejar el cambio. La lista se calcula de forma aproximada (ver aviso más abajo): puede incluir alguna suscripción del mismo cliente que no dependía de este mandato.
Ejemplo
{
"event": "PAYMENT_AUTHORIZATION_REVOKED",
"timestamp": "2026-05-09T19:14:02.000Z",
"app": { "id": "app_1", "slug": "mi-aplicacion" },
"data": {
"mandateId": "pma_01HQ2X7C9MZK3FQAGBYQ8K2W4P",
"clientId": "cli_01HXYZK6Z3AQAGBYQ8K2W4P",
"scope": "RECURRING_SUBSCRIPTION",
"revokedBy": "PORTAL_USER",
"revokedAt": "2026-05-09T19:14:02.000Z",
"revocationReason": "USER_REVOKED",
"affectedSubscriptionIds": ["rec_01HXYZK6Z3AQAGBYQ8K2W4P"]
}
}Dos campos de la revocación que no son exactos
scopeno conserva el alcance original. Al revocar, el mandato pasa a estado revocado en base de datos y el emisor pierde el alcance con el que se firmó, así que rellena siempreRECURRING_SUBSCRIPTION. Un mandato que eraSINGLE_INVOICEllegará igualmente comoRECURRING_SUBSCRIPTIONen el evento REVOKED. Si necesitas el alcance real, guárdalo delPAYMENT_AUTHORIZATION_GRANTEDprevio, que sí lo trae bien, y búscalo pormandateId.affectedSubscriptionIdses una aproximación. Además de las suscripciones que todavía apuntan al mandato, la consulta recoge las del mismo cliente que se hayan quedado sin mandato en el último minuto, porque la cascada interna las desvincula antes de emitirse el webhook. En un cliente con varias suscripciones puede colarse alguna que no dependía de este mandato. Trátala como una lista de candidatas a revisar, no como una verdad cerrada.
Quién y por qué retiró la autorización:
revokedBy = PORTAL_USERconrevocationReason = USER_REVOKED: el cliente la retiró desde el portal del cliente.revokedBy = STAFFconrevocationReason = STAFF_REVOKED: una persona del equipo la retiró desde el panel.revokedBy = SYSTEM_PAYMENT_METHOD_EXPIREDconrevocationReason = PAYMENT_METHOD_EXPIRED: UTILIA OS detectó que la tarjeta del mandato había caducado y retiró la autorización.revokedBy = SYSTEM_OTHERconrevocationReason = PAYMENT_METHOD_DETACHED: la tarjeta se desvinculó en Stripe (por el cliente o por el equipo) y arrastró al mandato.revokedBy = SYSTEM_OTHERconrevocationReason = OTHER: cualquier otra causa interna.
Por protección de datos, los avisos de autorización no incluyen el texto íntegro del consentimiento (solo su versión), el identificador de Stripe del método de pago, la IP completa, el motivo libre que escribe el equipo al retirarla ni el identificador de la persona que firmó o retiró. Todo eso queda en el registro de auditoría de UTILIA OS.
INVOICE_SENT
Se emite cuando la factura sale por correo hacia el usuario final, tanto desde la API externa como cuando la envía el equipo desde el panel.
{
"event": "INVOICE_SENT",
"timestamp": "2026-10-01T09:13:10.482Z",
"app": { "id": "app_...", "slug": "mi-aplicacion" },
"data": {
"invoice": {
"id": "ckinv1234567890",
"invoiceNumber": "202600042",
"status": "SENT",
"totalAmount": 53.45,
"paidAmount": 0,
"currency": "eur",
"issueDate": "2026-10-01T00:00:00.000Z",
"dueDate": "2026-10-08T00:00:00.000Z"
},
"externalUser": { "externalId": "user_01HXYZ" },
"delivery": {
"recipients": ["administracion@estudiomarea.example"],
"sentAt": "2026-10-01T09:13:10.000Z"
}
},
"meta": { "schemaVersion": "2026-05" }
}El bloque payment de INVOICE_PAID e INVOICE_PARTIALLY_PAID incluye
source. Vale manual cuando el cobro se registró fuera de la pasarela (una
transferencia, un pago en efectivo, la llamada a mark-paid), y en ese caso
method dice con qué forma de pago.
Suscripciones
Los ocho avisos del dominio comparten estos campos en data, además de los que
añade cada uno:
- subscriptionIdstringobligatorio
- Identificador de la suscripción en UTILIA OS.
- externalUserIdstring | nullobligatorio
- Identificador del usuario en tu aplicación. Nulo si el usuario ya no existe.
- namestringobligatorio
- Nombre visible de la suscripción.
- statusenumobligatorio
- ACTIVE, PAUSED, CANCELLED o COMPLETED.
- periodAmountCentsintegerobligatorio
- Importe por periodo, en céntimos.
- currencystringobligatorio
- Divisa en minúsculas.
Campos propios de cada aviso:
| Aviso | Campos adicionales |
|---|---|
SUBSCRIPTION_CREATED | frequency, chargeMode, startDate, nextInvoiceDate |
SUBSCRIPTION_UPDATED | changes[] con field, from y to |
SUBSCRIPTION_PAUSED | pausedAt, reason |
SUBSCRIPTION_RESUMED | nextInvoiceDate |
SUBSCRIPTION_CANCELLED | cancelledAt, reason |
SUBSCRIPTION_COMPLETED | completedAt, invoiceCount |
SUBSCRIPTION_INVOICE_GENERATED | invoiceId, invoiceNumber, amountCents, dueDate, emailSent |
SUBSCRIPTION_PAYMENT_AUTHORIZATION_PENDING | nextInvoiceDate, reason |
SUBSCRIPTION_INVOICE_GENERATED
{
"event": "SUBSCRIPTION_INVOICE_GENERATED",
"timestamp": "2026-11-01T06:00:12.104Z",
"app": { "id": "app_...", "slug": "mi-aplicacion" },
"data": {
"subscriptionId": "cksub1234567890",
"externalUserId": "user_01HXYZ",
"name": "Plan profesional mensual",
"status": "ACTIVE",
"periodAmountCents": 5345,
"currency": "eur",
"invoiceId": "ckinv2234567890",
"invoiceNumber": "202600097",
"amountCents": 5345,
"dueDate": "2026-11-08T00:00:00.000Z",
"emailSent": true
},
"meta": { "schemaVersion": "2026-05" }
}SUBSCRIPTION_PAYMENT_AUTHORIZATION_PENDING es el aviso que hay que atender
Significa que la suscripción está configurada para cobrarse sola pero no tiene
autorización vigente. Si nadie hace nada, el periodo se facturará y no se
cobrará. Pide al usuario que autorice el cobro antes de nextInvoiceDate.
Cobros programados
Cada aviso de este dominio cuenta un paso en la vida de un cobro sin el usuario delante. Este es el orden en que pueden llegar:
Los avisos de un cobro programado
Cobro programado
CHARGE_SCHEDULED
Queda fecha para cobrar la factura.Se llega desde Cobro programado cuando: Se cancela antes del cobro
Cobro programado cancelado
CHARGE_CANCELLED
Estado final
Se llega desde Cobro programado cuando: Llega la fecha
Cobro iniciado
CHARGE_STARTED
Empieza un intento de cobro.Se llega desde Cobro iniciado cuando: El banco pide verificar al titular
Necesita que el usuario se identifique
CHARGE_REQUIRES_ACTION
Lleva a tu usuario al enlace público de la factura.Se llega desde Cobro iniciado cuando: La pasarela rechaza el cobro
Cobro fallido
CHARGE_FAILED
Se llega desde Cobro fallido cuando: Quedan intentos
Reintento de cobro programado
CHARGE_RETRY_SCHEDULED
Se llega desde Cobro fallido cuando: No quedan intentos
Reintentos de cobro agotados
CHARGE_RETRIES_EXHAUSTED
Estado final
Se llega desde Cobro iniciado cuando: La pasarela confirma el cobro
Cobro realizado
CHARGE_SUCCEEDED
Estado final
Renueva el servicio de tu usuario.
Otras transiciones
- Reintento de cobro programado pasa a Cobro iniciado: Llega la fecha del reintento.
Los ocho avisos del dominio comparten estos campos en data:
- scheduledChargeIdstring | nullobligatorio
- Cobro programado que originó el aviso. Viaja NULO cuando el cobro lo lanzó la aplicación con POST /invoices/:id/charge, porque en ese caso no hay programación detrás.
- invoiceIdstringobligatorio
- Factura que se cobra.
- invoiceNumberstringobligatorio
- Número fiscal de la factura.
- externalUserIdstring | nullobligatorio
- Identificador del usuario en tu aplicación.
- subscriptionIdstring | nullobligatorio
- Suscripción de la que viene la factura, si viene de una. Nulo en los cobros que lanza la aplicación.
- amountCentsintegerobligatorio
- Importe del cobro, en céntimos.
- currencystringobligatorio
- Divisa en minúsculas.
Campos propios de cada aviso:
| Aviso | Campos adicionales |
|---|---|
CHARGE_SCHEDULED | scheduledAt, cancellableUntil |
CHARGE_STARTED | startedAt, attemptNumber |
CHARGE_SUCCEEDED | paymentIntentId, succeededAt, invoiceFullyPaid |
CHARGE_FAILED | paymentIntentId, failedAt, attemptNumber, failureCode, failureMessage, willRetry |
CHARGE_REQUIRES_ACTION | paymentIntentId, expiresAt, publicUrl, publicLinkAvailable |
CHARGE_RETRY_SCHEDULED | nextRetryAt, attemptNumber, attemptsRemaining |
CHARGE_RETRIES_EXHAUSTED | exhaustedAt, totalAttempts, lastFailureCode, subscriptionPaused |
CHARGE_CANCELLED | cancelledAt, cancelledBy, reason |
CHARGE_REQUIRES_ACTION
Es el más importante de los dieciséis: el cobro no se ha producido y no prosperará sin que el usuario se identifique ante su banco.
{
"event": "CHARGE_REQUIRES_ACTION",
"timestamp": "2026-11-08T08:00:31.902Z",
"app": { "id": "app_...", "slug": "mi-aplicacion" },
"data": {
"scheduledChargeId": "cksch1234567890",
"invoiceId": "ckinv2234567890",
"invoiceNumber": "202600097",
"externalUserId": "user_01HXYZ",
"subscriptionId": "cksub1234567890",
"amountCents": 5345,
"currency": "eur",
"paymentIntentId": "pi_3QabcDEFghiJKLmn",
"expiresAt": "2026-11-09T08:00:00.000Z",
"publicUrl": null,
"publicLinkAvailable": true
},
"meta": { "schemaVersion": "2026-05" }
}La dirección del enlace no llega siempre:
- Cobro que lanza tu aplicación con
POST /invoices/:id/charge:publicUrlsí trae la dirección, porque el emisor acaba de crear el enlace y conoce su valor.scheduledChargeIdysubscriptionIdllegan nulos. - Cobro programado:
publicUrlllega nulo. De un enlace emitido solo se guarda su huella, así que no se puede reconstruir, y generar otro sería abrir un acceso público que nadie ha pedido. LlegapublicLinkAvailable: truey tu aplicación pide la dirección conPOST /external/v1/invoices/:id/public-link.
En los cobros programados, failureCode de CHARGE_FAILED llega nulo y el
motivo viene como texto en failureMessage. En los cobros que lanza tu
aplicación, failureCode sí trae el código de la pasarela.
PAYMENT_METHOD_EXPIRING
Avisa de que una tarjeta guardada está a punto de caducar, con lo que se queda sin cobrar si no se sustituye.
{
"event": "PAYMENT_METHOD_EXPIRING",
"timestamp": "2026-10-20T04:00:00.000Z",
"app": { "id": "app_...", "slug": "mi-aplicacion" },
"data": {
"paymentMethodId": "ckpm1234567890",
"externalUserId": "user_01HXYZ",
"brand": "visa",
"last4": "4242",
"expMonth": 11,
"expYear": 2026,
"affectedSubscriptionIds": ["cksub1234567890"],
"affectedScheduledChargeIds": []
},
"meta": { "schemaVersion": "2026-05" }
}PAYMENT_AUTHORIZATION_CONSUMED
Una autorización de un solo uso se ha agotado al ejecutarse el cobro que la
respaldaba. Deja de servir: si hace falta cobrar otra vez, el usuario tiene que
autorizarlo de nuevo. Las autorizaciones de alcance RECURRING no se consumen.
Buenas prácticas al procesarlos
- Procesa cada aviso una sola vez, usando
X-Utilia-Delivery-Idpara descartar repeticiones. - Todos los avisos siguen la misma política de reintentos: hasta 5 intentos con esperas de 5, 10, 20 y 40 segundos. Detalle en Entregas y reintentos de los avisos.
- En
PAYMENT_AUTHORIZATION_*, la parejamandateIdmástimestampidentifica un cambio: un mismo mandato no recibe dosGRANTEDni dosREVOKEDcon el mismotimestamp. Si guardas autorizaciones, usa esa pareja para detectar duplicados antes de aplicar el cambio.
Siguiente paso
Comprueba la firma de cada aviso antes de procesarlo, como se explica en Firma HMAC de los avisos.