Acciones sobre una factura emitida
Enviar por correo, registrar un cobro fuera de la pasarela, devolver dinero, obtener el enlace público y cobrar sin el usuario delante, desde la API externa /external/v1/invoices/:id.
Actualizado el . 10 min de lectura
Incluido en todos los planes. Ver precios
Crear la factura es el principio. Lo que viene después (mandársela al cliente, cobrarla, registrar una transferencia, devolver el dinero) está en este grupo de endpoints, todos bajo /external/v1/invoices/:id.
Todos exigen la cabecera X-Api-Key, comparten un límite de 60 peticiones por minuto y buscan la factura por su identificador, la aplicación dueña y el usuario indicado. Si la factura es de otra aplicación o de otro usuario, la respuesta es un 404 INVOICE_NOT_ACCESSIBLE idéntico al de una factura inexistente: nunca revela que existe.
Plan y permisos
Antes que el permiso de la aplicación se comprueba el plan de tu organización. Si falta el aplicativo, la respuesta es 403 con errorCode: 'MODULE_NOT_ENABLED' y su moduleKey; si falta la capacidad, 403 con errorCode: 'CAPABILITY_NOT_ENABLED' y su capabilityKey. Los dos son estados comerciales esperados, no fallos: no se reintentan.
| Endpoint | Aplicativos del plan | Capacidad del plan | Permiso de la API (scope) |
|---|---|---|---|
POST /invoices/:id/send | Finanzas | — | crm:invoices:send |
POST /invoices/:id/mark-paid | Finanzas | — | crm:invoices:write |
POST /invoices/:id/refund | Finanzas y Pagos con tarjeta (Stripe) | — | billing:refund |
GET /invoices/:id/public-link | Finanzas | — | crm:invoices:read |
POST /invoices/:id/public-link | Finanzas | — | crm:invoices:write |
POST /invoices/:id/charge | Finanzas y Pagos con tarjeta (Stripe) | Cobro automático (finance.direct_debit) | billing:charge |
Estos endpoints no tienen periodo de gracia: sin el permiso, la respuesta es 403 con errorCode: 'INSUFFICIENT_SCOPE' y un mensaje genérico que no dice cuál falta. Concédelo en la ficha de la aplicación antes de integrar. billing:refund y billing:charge son críticos financieros: solo un Super Administrador puede concederlos, con un motivo.
Enviar la factura por correo
/api/external/v1/invoices/:id/sendEnvía la factura al usuario final por correo, con el PDF adjunto, usando la plantilla y el remitente de tu organización. La factura pasa a estado Enviada.
Es el mismo envío que usa tu equipo desde el panel: misma plantilla, mismo remitente, misma vía de salida y mismo registro de comunicación. Úsalo para reenviar una factura cuyo envío automático falló, o cuando prefieras decidir tú el momento del envío en lugar de dejarlo al interruptor de la aplicación.
Cuerpo
- userIdstringobligatorio
- Identificador del usuario en tu aplicación (externalId) al que pertenece la factura.
- tostring[]
- Destinatarios. Máximo 10 correos, y todos tienen que ser direcciones ya conocidas de esta factura. Sin este campo, el destinatario es el contacto de facturación principal del cliente y, si no hay, el correo del usuario externo.
- ccstring[]
- Copia. Máximo 10 correos, con la misma restricción que to.
- messagestring
- Mensaje personal, máximo 2.000 caracteres. Sustituye el texto de la plantilla y conserva el bloque de datos de la factura.
- templateIdstring
- Plantilla de correo concreta. Sin este campo se usa la predeterminada de tu organización.
Solo direcciones conocidas de esta factura
send no es un servicio de correo general: escribe en nombre de tu organización, con su remitente y su plantilla, así que solo llega a la otra parte de la factura. Son direcciones aceptadas:
- los contactos de facturación activos del cliente puente del usuario,
- los contactos del CRM de ese cliente marcados para recibir facturas,
- el correo del usuario externo,
- el destinatario con el que se emitió la factura.
Cualquier otra dirección en to o en cc rechaza el envío entero con 422 INVOICE_EMAIL_RECIPIENT_NOT_ALLOWED, sin enviar nada. La comparación no distingue mayúsculas de minúsculas, y el mensaje explica la regla pero nunca enumera las direcciones registradas. Para escribir a una dirección nueva, regístrala antes como contacto de facturación del cliente.
Cada aplicación puede enviar 100 facturas por hora dentro de tu organización. Superado el cupo, la respuesta es 429 con errorCode: 'RATE_LIMIT_EXCEEDED', la cabecera Retry-After y el campo retryAfterSeconds en el cuerpo. La ventana es la hora en curso: espera los segundos que indique en lugar de reintentar sin parar.
Respuesta (200)
{
"sent": true,
"invoiceId": "ckinv1234567890",
"recipients": ["administracion@estudiomarea.example"],
"sentAt": "2026-10-01T09:13:10.000Z"
}Errores
| Situación | HTTP | errorCode |
|---|---|---|
| La factura es de otra aplicación o de otro usuario | 404 | INVOICE_NOT_ACCESSIBLE |
| La factura está en borrador o anulada | 409 | INVOICE_STATE_INVALID |
| No hay ningún destinatario al que enviarla | 422 | INVOICE_EMAIL_NO_RECIPIENT |
| Un destinatario no es una dirección conocida de la factura | 422 | INVOICE_EMAIL_RECIPIENT_NOT_ALLOWED |
| La aplicación superó su cupo de envíos por hora | 429 | RATE_LIMIT_EXCEEDED |
| El servidor de salida rechazó el envío | 502 | INVOICE_EMAIL_NOT_SENT |
Registrar un cobro fuera de la pasarela
/api/external/v1/invoices/:id/mark-paidMarca la factura como cobrada por una vía que no pasa por la pasarela, como una transferencia o un pago en efectivo. Usa el mismo proceso que el panel, así que la contabilidad y el justificante se comportan igual.
Cuerpo
- userIdstringobligatorio
- Identificador del usuario en tu aplicación (externalId).
- methodenumobligatorio
- Forma de pago: TRANSFER, CASH, CHECK, CARD u OTHER. Las formas de la pasarela (STRIPE_*) se rechazan a propósito: ese cobro lo registra el propio pago y volver a registrarlo duplicaría el asiento.
- amountnumber
- Importe cobrado, con dos decimales y mínimo 0,01. Sin este campo la factura se marca como pagada por completo; con él se registra un cobro parcial.
- paidAtstring
- Fecha del cobro en formato ISO 8601. Sin ella, el momento de la llamada.
- referencestring
- Referencia bancaria o de tu aplicación, máximo 200 caracteres. Se guarda en la auditoría y se añade a las notas internas de la factura, que es donde la busca quien concilia.
La respuesta es el resumen de la factura ya actualizada, con el mismo formato que devuelve GET /external/v1/invoices.
Errores
| Situación | HTTP | errorCode |
|---|---|---|
| La factura es de otra aplicación o de otro usuario | 404 | INVOICE_NOT_ACCESSIBLE |
| La factura está en borrador o anulada | 409 | INVOICE_STATE_INVALID |
| La factura ya está cobrada por completo | 409 | INVOICE_ALREADY_PAID |
Devolver dinero cobrado
/api/external/v1/invoices/:id/refundReembolsa por la pasarela, entero o en parte, el dinero ya cobrado de una factura. Anular una factura no devuelve dinero; esto sí.
Cuerpo
- userIdstringobligatorio
- Identificador del usuario en tu aplicación (externalId).
- amountCentsinteger
- Importe a devolver en céntimos, mínimo 1. Sin este campo se devuelve todo lo cobrado.
- reasonenum
- duplicate, fraudulent o requested_by_customer. Los mismos valores que la pasarela.
- createRectifyingInvoiceboolean
- Emitir además la factura rectificativa. Por defecto, no: la rectificativa la emite tu equipo desde el panel para elegir su código legal.
- paymentIntentIdstring
- Cobro concreto sobre el que devolver, en facturas con varios.
- idempotencyKeystring
- Clave de idempotencia, válida 24 horas. Repetir la misma clave devuelve el resultado de la primera llamada en lugar de reembolsar dos veces.
Respuesta (200)
{
"id": "ckref1234567890",
"invoiceId": "ckinv1234567890",
"amountCents": 5345,
"currency": "eur",
"status": "SUCCEEDED",
"isFullRefund": true,
"reason": "requested_by_customer",
"rectifyingInvoiceId": null,
"createdAt": "2026-10-15T11:02:31.000Z"
}Produce el aviso INVOICE_REFUNDED, dirigido a tu aplicación.
Errores
| Situación | HTTP | errorCode |
|---|---|---|
| La factura es de otra aplicación o de otro usuario | 404 | INVOICE_NOT_ACCESSIBLE |
| No hay cobros completados, o el importe supera lo cobrado | 409 | REFUND_NOT_ALLOWED |
| La cuenta de la pasarela no está lista para operar | 409 | STRIPE_ACCOUNT_NOT_READY |
Enlace público de la factura
Es la única dirección que puedes entregar a tu usuario final. La ruta del PDF de la API externa exige la clave de API y nunca debe salir de tu servidor.
/api/external/v1/invoices/:id/public-linkConsulta si la factura tiene un enlace público vigente, hasta cuándo y qué acciones permite. Solo lee: nunca devuelve la dirección.
/api/external/v1/invoices/:id/public-linkEmite un enlace público nuevo y devuelve su dirección. Es la única vía para obtener una dirección utilizable.
La dirección se entrega una sola vez
De un enlace emitido, UTILIA OS guarda solo su huella, nunca el valor en claro. Por eso la lectura devuelve siempre publicUrl: null y solo el POST trae la dirección. Y por eso emitir revoca el enlace anterior de esa factura: si ya habías repartido uno a tu usuario, deja de funcionar y tendrás que repartir el nuevo. No consultes una y otra vez esperando que aparezca la dirección, ni emitas un enlace nuevo por costumbre: guárdalo en tu base de datos la primera vez.
Parámetros
- userIdquery o cuerpoobligatorio
- Identificador del usuario en tu aplicación (externalId). Va en la cadena de consulta en el GET y en el cuerpo en el POST.
- expiresInDaysinteger
- Solo en el POST. Días de vigencia del enlace nuevo, entre 1 y 365. Sin este campo se aplica la vigencia predeterminada de tu organización.
Respuesta del POST (200)
{
"invoiceId": "ckinv1234567890",
"publicUrl": "https://os.utilia.ai/public/invoices/9f3c1a6e4b2d",
"expiresAt": "2026-12-30T23:59:59.000Z",
"allowedActions": ["VIEW", "PAY"],
"createdAt": "2026-10-01T09:12:45.000Z"
}La respuesta del GET tiene la misma forma con publicUrl a nulo.
allowedActions es siempre ["VIEW", "PAY"] en los enlaces que emite la API externa, también cuando la factura viene de una suscripción. Firmar o retirar una autorización de cobro recurrente no cabe aquí: son actos del usuario final que se recogen por su camino propio, con su texto de consentimiento y su prueba legal, en Autorizaciones de cobro. El enlace que emite tu equipo desde el panel sí puede habilitar esas acciones, porque lo reparte una persona con sesión iniciada.
Errores
| Situación | HTTP | errorCode |
|---|---|---|
| La factura es de otra aplicación o de otro usuario | 404 | INVOICE_NOT_ACCESSIBLE |
El GET no encuentra ningún enlace vigente | 404 | INVOICE_PUBLIC_LINK_NOT_AVAILABLE |
Cobrar sin el usuario delante
/api/external/v1/invoices/:id/chargeCobra la factura contra la autorización de cobro vigente del usuario, sin que haya nadie delante. Es lo que hace posible la renovación mensual desatendida.
No lo confundas con el cobro con el usuario delante: con POST /invoices/:id/pay tu aplicación monta la interfaz de la pasarela con el clientSecret y la persona confirma. POST /invoices/:id/charge es el cobro sin nadie delante, y exige que el usuario haya firmado antes una autorización de cobro.
Cobro sin nadie delante
Participan: Tu servidor, UTILIA OS.
De Tu servidor a UTILIA OS:
POST /api/external/v1/invoices/:id/charge
Con userId e idempotencyKey.Nota en UTILIA OS:
Cobra la tarjeta que respalda la autorización vigente.De UTILIA OS a Tu servidor:
status: SUCCEEDED (respuesta)
El dinero ya está cobrado.De UTILIA OS a Tu servidor:
Aviso CHARGE_SUCCEEDED
Con invoiceFullyPaid, que dice si la factura queda cobrada entera.
Cuerpo
- userIdstringobligatorio
- Identificador del usuario en tu aplicación (externalId).
- paymentAuthorizationIdstring
- Autorización de cobro concreta. Sin este campo se usa la vigente y compatible del usuario.
- paymentMethodIdstring
- Tarjeta concreta. Sin este campo se usa la que respalda la autorización.
- idempotencyKeystring
- Clave de idempotencia, válida 24 horas. Envía siempre la misma para reintentar la MISMA operación: una clave nueva es un cobro nuevo.
Respuesta (200)
{
"status": "REQUIRES_ACTION",
"paymentIntentId": "pi_3QabcDEFghiJKLmn",
"invoiceId": "ckinv1234567890",
"amountCents": 5345,
"currency": "eur",
"publicUrl": "https://os.utilia.ai/public/invoices/9f3c1a6e4b2d",
"clientSecret": "pi_3QabcDEFghiJKLmn_secret_XyZ",
"failureCode": null,
"failureMessage": null
}- statusenumobligatorio
- SUCCEEDED (dinero cobrado), REQUIRES_ACTION (el banco pide autenticación reforzada), PROCESSING (en curso) o FAILED (rechazado).
- publicUrlstring
- Con REQUIRES_ACTION, la dirección a la que llevar al usuario para que complete la autenticación. Es un enlace adicional, limitado a ver y pagar: no revoca el que ya tuviera repartido de esa factura.
- clientSecretstring
- Con REQUIRES_ACTION, el secreto para completar la autenticación desde tu propia interfaz si prefieres no usar el enlace público.
- failureCodestring
- Con FAILED, el código de rechazo de la pasarela.
- failureMessagestring
- Con FAILED, la explicación legible del rechazo.
No siempre sale a la primera. Qué hacer según el status de la respuesta:
¿Qué hago con la respuesta de un cobro?
¿Qué status trae la respuesta?
SUCCEEDED:
Cobro hecho
CHARGE_SUCCEEDED
El dinero está cobrado. El aviso lo confirma.REQUIRES_ACTION:
Falta la autenticación del banco
CHARGE_REQUIRES_ACTION
No es un error: lleva al usuario a publicUrl, o complétala en tu interfaz con clientSecret. Cuando pague, llega INVOICE_PAID.PROCESSING:
En curso
Espera el aviso. No repitas el cobro con otra clave de idempotencia.FAILED:
Rechazado
CHARGE_FAILED
failureCode y failureMessage dicen por qué. UTILIA OS no lo reintenta: decides tú.
Errores
| Situación | HTTP | errorCode |
|---|---|---|
| El usuario no tiene autorización de cobro vigente | 409 | PAYMENT_AUTHORIZATION_REQUIRED, con nextStep |
| La autorización indicada ya no está vigente | 409 | PAYMENT_AUTHORIZATION_NOT_ACTIVE |
| La tarjeta indicada no existe o ya no está activa | 404 o 409 | PAYMENT_METHOD_NOT_FOUND o PAYMENT_METHOD_INACTIVE |
| La factura ya está cobrada | 409 | INVOICE_ALREADY_PAID |
| La divisa de la factura no la admite la cuenta de la pasarela | 409 | BILLING_CURRENCY_NOT_SUPPORTED |
| La cuenta de la pasarela no está lista para operar | 409 | STRIPE_ACCOUNT_NOT_READY |
El error de autorización ausente es accionable: dice qué hacer a continuación.
{
"errorCode": "PAYMENT_AUTHORIZATION_REQUIRED",
"message": "La suscripción no puede cobrarse sola porque este usuario todavía no ha autorizado el cobro.",
"nextStep": {
"operation": "paymentAuthorizations.getConsentText",
"then": "paymentAuthorizations.create",
"scope": "RECURRING"
}
}Avisos que producen estas acciones
| Acción | Aviso |
|---|---|
| Envío por correo | INVOICE_SENT |
| Cobro fuera de la pasarela | INVOICE_PAID o INVOICE_PARTIALLY_PAID, con source: 'manual' |
| Reembolso | INVOICE_REFUNDED |
| Cobro sin el usuario delante | CHARGE_STARTED y después CHARGE_SUCCEEDED, CHARGE_FAILED o CHARGE_REQUIRES_ACTION |
| Enlace público | ninguno |
Con el SDK
import { UtiliaSDK } from '@utilia-os/sdk-js';
const sdk = new UtiliaSDK({
baseURL: 'https://os.utilia.ai/api',
apiKey: process.env.UTILIA_API_KEY!,
});
await sdk.invoices.send('ckinv1234567890', {
userId: 'user_01HXYZ',
message: 'Adjuntamos la factura del mes de octubre.',
});
await sdk.invoices.markPaid('ckinv1234567890', {
userId: 'user_01HXYZ',
method: 'TRANSFER',
reference: 'TRF-2026-10-0042',
});
const enlace = await sdk.invoices.regeneratePublicLink('ckinv1234567890', {
userId: 'user_01HXYZ',
});
const cobro = await sdk.invoices.charge('ckinv1234567890', {
userId: 'user_01HXYZ',
idempotencyKey: 'cobro_ckinv1234567890',
});
if (cobro.status === 'REQUIRES_ACTION') {
llevarAlUsuario(cobro.publicUrl);
}
await sdk.invoices.refunds.create('ckinv1234567890', {
userId: 'user_01HXYZ',
reason: 'requested_by_customer',
idempotencyKey: 'devolucion_ckinv1234567890',
});Lo que no se expone
Las facturas rectificativas no se emiten desde la API externa. Elegir su código legal es una decisión de tu equipo, así que se emiten desde Finanzas o con la herramienta crm_invoices_rectifications del servidor MCP de finanzas. Para devolver dinero, usa el reembolso de esta misma página.
Siguiente paso
- Autorizaciones de cobroRecoger el consentimiento que habilita cobrar sin el usuario delante.
- SuscripcionesCuotas mensuales con factura y cobro automáticos.
- Cobros programadosCobrar una factura suelta en una fecha futura.
- Guía de facturaciónEl recorrido completo, de la primera factura al cobro recurrente.