Firmas de presupuesto
Lista, consulta y verifica las firmas electrónicas de un presupuesto con /external/v1/budgets/:budgetId/signatures y los permisos crm:budgets:signatures:*.
Actualizado el . 5 min de lectura
Incluido en todos los planes. Ver precios
Con /external/v1/budgets/:budgetId/signatures tu aplicación consulta
las firmas electrónicas de un presupuesto. Solo es lectura: listado,
detalle y verificación de la integridad de firmas ya hechas. Crear o
revocar los enlaces de firma queda dentro de UTILIA OS, porque son
acciones con efecto legal directo.
De la firma a su verificación
Participan: Firmante, UTILIA OS, Tu aplicación.
De UTILIA OS a Firmante:
Correo con su enlace de firma
Si tiene acceso al portal del cliente, también puede firmar desde allí.De Firmante a UTILIA OS:
Aceptar y firmar
Con su nombre, su correo y su firma.Nota en UTILIA OS:
Guarda la firma con la huella del documento (documentHash y hashVersion), la IP, el navegador y la fecha.De Tu aplicación a UTILIA OS:
GET /api/external/v1/budgets/:budgetId/signatures
Con X-Api-Key y el permiso crm:budgets:signatures:read.De UTILIA OS a Tu aplicación:
Firmas del presupuesto (items) (respuesta)
Cada una con su huella y la IP parcialmente oculta.De Tu aplicación a UTILIA OS:
GET /api/external/v1/budgets/:budgetId/signatures/:sigId/verify
Con el permiso crm:budgets:signatures:verify.Nota en UTILIA OS:
Recalcula la huella con la versión de la firma: con los datos actuales del presupuesto en las versiones 1 y 2, y con el PDF que firmó la persona en la 3. Después la compara con la guardada.De UTILIA OS a Tu aplicación:
status, valid, expectedHash y actualHash (respuesta)
status es VALID, MODIFIED o UNVERIFIABLE.
Autenticación y plan
Todas las peticiones llevan la clave de API de la aplicación:
X-Api-Key: <api_key_de_la_app>
El plan de tu organización tiene que incluir el aplicativo CRM; si
no, la respuesta es 403 MODULE_NOT_ENABLED.
Permisos de la API (scopes)
| Endpoint | Permiso obligatorio |
|---|---|
GET /external/v1/budgets/:budgetId/signatures | crm:budgets:signatures:read |
GET /external/v1/budgets/:budgetId/signatures/:sigId | crm:budgets:signatures:read |
GET /external/v1/budgets/:budgetId/signatures/:sigId/verify | crm:budgets:signatures:verify |
Sin el permiso adecuado, los endpoints responden 403 INSUFFICIENT_SCOPE
con un mensaje genérico que pide hablar con el administrador de la
organización. El permiso se exige desde la primera llamada, sin periodo
de gracia. El catálogo completo y las reglas de concesión están en
Permisos de la API.
crm:budgets:signatures:verify solo lo concede un Super Administrador
La verificación recalcula la huella del documento firmado y la compara con la guardada. En malas manos, permitiría comprobar si una manipulación del documento pasa desapercibida. Por eso es un permiso crítico: solo lo concede un Super Administrador, con un motivo de al menos 20 caracteres.
Endpoints
/api/external/v1/budgets/:budgetId/signaturesLista las firmas del presupuesto, con la IP parcialmente oculta. Permiso: crm:budgets:signatures:read.
/api/external/v1/budgets/:budgetId/signatures/:sigIdDetalle completo de una firma, con la IP parcialmente oculta. Permiso: crm:budgets:signatures:read.
/api/external/v1/budgets/:budgetId/signatures/:sigId/verifyRecalcula la huella con la versión de la firma y la compara con la guardada. Devuelve status VALID, MODIFIED o UNVERIFIABLE. Permiso: crm:budgets:signatures:verify.
Parámetros principales
- X-Api-Keyheaderobligatorio
- Clave de API de la aplicación externa.
- budgetIdpathobligatorio
- Identificador del presupuesto.
- sigIdpathobligatorio
- Identificador de la firma.
Respuesta de la verificación
La huella se recalcula siempre con la versión con la que se firmó
(hashVersion), que la respuesta devuelve tal cual:
hashVersion | Sobre qué se calcula la huella |
|---|---|
1 | Los datos básicos del presupuesto: código, total y partidas. Solo en firmas antiguas. |
2 | Todos los datos visibles del presupuesto: partidas, apartados, descuentos, impuestos, condiciones y notas. La usan las firmas desde el portal del cliente y las firmas por enlace que se hicieron sin el PDF preparado. |
3 | El PDF exacto que firmó la persona desde el enlace de firma, guardado sin posibilidad de cambios. Cambiar después el presupuesto no la invalida, porque lo firmado es ese PDF. |
- statusstringobligatorio
- VALID: las dos huellas coinciden. MODIFIED: no coinciden, lo firmado ya no es lo que hay. UNVERIFIABLE: no se puede recalcular porque el PDF firmado (versión 3) ya no está disponible o no se ha podido leer; no indica ningún cambio.
- validbooleanobligatorio
- true solo con VALID. Es false tanto con MODIFIED como con UNVERIFIABLE: decide siempre por status.
- expectedHashstringobligatorio
- Huella guardada al firmar.
- actualHashstring | nullobligatorio
- Huella recalculada ahora. null con UNVERIFIABLE.
- hashVersionnumberobligatorio
- Versión con la que se firmó: 1, 2 o 3.
- reasonobject | nullobligatorio
- Con UNVERIFIABLE, el motivo: errorCode y message (en castellano, listo para mostrar). null en el resto de casos.
- signedAt, signerName, signerEmailstringobligatorio
- Fecha de la firma y nombre y correo del firmante.
- ipAddressMasked, userAgentstringobligatorio
- IP parcialmente oculta y navegador desde el que se firmó.
- signingTokenIdstring | nullobligatorio
- Enlace de firma con el que se firmó. null si se firmó desde el portal o si el enlace ya no existe.
Cuándo una firma no se puede verificar:
reason.errorCode | Qué significa | Qué hacer |
|---|---|---|
BUDGET_SIGNED_PDF_UNAVAILABLE | El PDF firmado ya no existe: se borró el enlace de firma, su archivo o el propio documento del almacén. | La firma sigue registrada con su huella, pero esta llamada ya no puede comprobarla. |
BUDGET_SIGNED_PDF_UNREADABLE | El PDF existe, pero no se ha podido leer en este momento. | Vuelve a intentarlo pasados unos minutos. |
Respuesta (200), firma que coincide
{
"valid": true,
"status": "VALID",
"expectedHash": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"actualHash": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"hashVersion": 3,
"reason": null,
"signedAt": "2026-09-10T10:00:00.000Z",
"signerName": "Marta Ruiz",
"signerEmail": "marta@estudiomarea.example",
"ipAddressMasked": "***.***.12.34",
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)",
"signingTokenId": "cktok1234567890"
}Respuesta (200), firma que no se puede verificar
{
"valid": false,
"status": "UNVERIFIABLE",
"expectedHash": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"actualHash": null,
"hashVersion": 3,
"reason": {
"errorCode": "BUDGET_SIGNED_PDF_UNAVAILABLE",
"message": "No se puede verificar: el PDF firmado ya no está disponible."
},
"signedAt": "2026-09-10T10:00:00.000Z",
"signerName": "Marta Ruiz",
"signerEmail": "marta@estudiomarea.example",
"ipAddressMasked": "***.***.12.34",
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)",
"signingTokenId": "cktok1234567890"
}Ejemplos
# Listar firmas
curl -X GET 'https://os.utilia.ai/api/external/v1/budgets/budget-uuid/signatures' \
-H 'X-Api-Key: utl_xxx'
# Detalle de una firma
curl -X GET 'https://os.utilia.ai/api/external/v1/budgets/budget-uuid/signatures/sig-uuid' \
-H 'X-Api-Key: utl_xxx'
# Verificar integridad
curl -X GET 'https://os.utilia.ai/api/external/v1/budgets/budget-uuid/signatures/sig-uuid/verify' \
-H 'X-Api-Key: utl_xxx'Errores
401: falta la clave de API o no es válida.403 MODULE_NOT_ENABLED: el plan de tu organización no incluye el CRM.403 INSUFFICIENT_SCOPE: la aplicación no tiene concedido el permiso necesario.404: el presupuesto no existe o es de otra organización (mensajePresupuesto <budgetId> no encontrado, con el identificador que enviaste).404: la firma no existe o no es de ese presupuesto (mensaje «Firma no encontrada en este presupuesto»).
Los 404 llegan como respuesta estándar (statusCode, message,
error), sin errorCode: distíngelos por el código HTTP y, si hace
falta, por el texto del mensaje.
Que el PDF firmado no se pueda leer no es un error de la petición: la
verificación responde 200 con status: "UNVERIFIABLE" y el motivo en
reason.errorCode (BUDGET_SIGNED_PDF_UNAVAILABLE o
BUDGET_SIGNED_PDF_UNREADABLE).
Seguimiento de peticiones
Envía X-Request-Id en cada petición para poder cruzarla con los
registros de UTILIA OS. Si no lo envías, la respuesta trae el que se
haya generado.
Siguiente paso
Si la verificación devuelve status: "MODIFIED", lo firmado ya no
coincide con lo que hay: con hashVersion 1 o 2, los datos del
presupuesto han cambiado después de la firma; con 3, el PDF guardado ya
no es el que se firmó. En los dos casos, avisa al administrador de la
organización. Con UNVERIFIABLE no hay indicio de cambio: guarda el
motivo y, si es BUDGET_SIGNED_PDF_UNREADABLE, vuelve a intentarlo más
tarde.
Cómo funciona la firma de presupuestos dentro de UTILIA OS lo explica Solicitar la firma a personas concretas.