Versiones y compatibilidad
Matriz de versiones de la API externa, los SDK de JavaScript y Python y el widget, con lo que aporta cada versión y sus cambios incompatibles.
Actualizado el . 35 min de lectura
Incluido en todos los planes. Ver precios
Esta página es la referencia única de versiones del ecosistema de integración de UTILIA OS: la API externa, los dos SDK oficiales y el widget. Úsala para fijar las dependencias de tu proyecto y para saber qué aporta cada versión antes de actualizar.
Matriz de versiones
| Componente | Paquete | Versión en el árbol | Última versión publicada |
|---|---|---|---|
| API externa | /external/v1 | v1 | v1 |
| SDK JavaScript | @utilia-os/sdk-js | 4.8.0 | 4.1.0 |
| SDK Python | utilia-sdk | 4.8.0 | 4.2.0 |
| Widget | @utilia-os/widget | 1.2.0 | 1.1.0 |
La 4.8.0 es la versión del repositorio en los dos lenguajes y está sin publicar. La 4.7.0, del 22 de octubre de 2026, y la 4.6.0 y la 4.5.0, del 21, tampoco se han publicado todavía. La última publicada, la que instalan npm install @utilia-os/sdk-js y pip install utilia-sdk, es la 4.1.0 en npm y la 4.2.0 en PyPI. La 4.1.1 no llegó a publicarse por separado: sus cambios van incluidos en la 4.2.0, y cada versión incluye las anteriores. Los dos SDK comparten numeración, para que la paridad entre lenguajes se mantenga versión a versión.
Antes de dar por hecho que un método existe, comprueba qué versión tienes con npm ls @utilia-os/sdk-js o pip show utilia-sdk: si vienes de la 3.x, la 4.0.0 retiró tres métodos y tu código puede depender de ellos.
Lo que todavía no puedes instalar
Varias guías enseñan métodos de versiones sin publicar. Las rutas de la API funcionan igual sin ellos: mientras tanto, llámalas directamente con los ejemplos de curl de cada guía.
| Versión | Lo que usan las guías | npm | PyPI |
|---|---|---|---|
| 4.2.0 | tickets.createStreamToken, onApiKeyExpires, verificar avisos con varias claves y budgetSignatures con clave de API | No | Sí |
| 4.3.0 | Facturas simplificadas, pricesIncludeTax, el bloque payment y invoices.rectifications.create y list | No | No |
| 4.4.0 | invoices.listSeries, seriesCode e invoices.getEuVatRates | No | No |
| 4.5.0 | invoices.validate, validatePdf, rectifications.validate, webhooks.signTestPayload, los campos displayNumber y emailDelivery de las facturas y las claves de API de la organización por MCP (orgKeys*) | No | No |
| 4.6.0 | sdk.diagnostics (con getMarkdown() y organizationApiKey()), la orden doctor, sdk.aiAnalytics, onIgnoredFields, la política de reintentos nueva, retryable en los errores, appDiagnosis(), los tipos corregidos de sdk.mcp.integrationsOverview, la captura de errores (errors.install, capture) y su gestión (reportBatch, get, resolve, reopen, silence) | No | No |
| 4.7.0 | appInstallations.exchange, list y get (app_installations en Python) y webhooks.verifyInstallation (verify_installation), para las instalaciones del catálogo | No | No |
| 4.8.0 | La suscripción de las instalaciones de pago (subscription), el filtro subscriptionStatus, isAccessAllowed (is_access_allowed) y los tipos de los avisos APP_SUBSCRIPTION_* | No | No |
El widget 1.2.0, con la opción userSignature para la verificación de identidad, tampoco está publicado: el que se instala es el 1.1.0, que no manda la firma.
Novedades de la versión 4.8.0
La 4.8.0 está sin publicar en npm y en PyPI. Trae las suscripciones a las aplicaciones de pago del catálogo y necesita el servidor con los cobros de aplicaciones; contra uno anterior, subscription no llega. No hay métodos nuevos de escritura: comprar, cambiar de plan, cancelar o pagar se hace en UTILIA OS.
- La suscripción de cada instalación. En una ficha «De pago en UTILIA»,
appInstallations.list()yget()traensubscription(ExternalAppSubscription): plan, precio sin impuestos, estado (TRIALING,ACTIVE,PAST_DUE,PAUSEDoENDED), prueba, periodo, cancelación y cambio programado.list({ subscriptionStatus })(subscription_statusen Python) filtra por su estado. Ver La suscripción de una aplicación de pago. isAccessAllowed(instalacion)(is_access_allowed): si la empresa puede usar tu aplicación, mirando la instalación y su suscripción.- Avisos de la suscripción. Doce avisos
APP_SUBSCRIPTION_*en el dominiointegrations(APP_SUBSCRIPTION_WEBHOOK_EVENTS) y su carga tipada,AppSubscriptionWebhookData. Ver Los avisos de la suscripción. - MCP.
installationsGet()einstallationsList()traensubscription, sin importe si no puedes ver el plan de la empresa. SalenlistingsList()ylistingsGet(), que llegaron con la 4.7.0 sin publicarse: las fichas que publica una cuenta de desarrollador se gestionan en el servidor de desarrolladores.
Novedades de la versión 4.6.0
La 4.6.0 está sin publicar en npm y en PyPI. sdk.diagnostics, la orden doctor, el diagnóstico de las claves de la organización y la orientación de los errores necesitan el servidor con el sistema de pruebas de la API, y la captura y la gestión de los errores, el servidor con POST /external/v1/errors/batch y las rutas de gestión; el resto funciona igual contra un servidor anterior. La guía de la captura está en Capturar los errores de tu aplicación. Todo es aditivo salvo los tipos de sdk.mcp.integrationsOverview y los cambios de la tabla de debajo.
- Comprobar tu integración.
sdk.diagnostics, con la clave de tu aplicación y sin ningún permiso de la API:me(),get(),calls(),checkRequest(),run(),sendWebhookSample(),checkWebhookReceiver()yopenApi(), sobreGET /external/v1/mey las rutas de/external/v1/diagnostics. En Python, los mismos ensnake_case(check_request,send_webhook_sample,check_webhook_receiver,open_api), asíncronos y síncronos. Ver Referencia de métodos. - La orden
doctor.npx @utilia-os/sdk-js doctoren JavaScript yutilia-doctoren Python, con--run,--check "POST /external/v1/invoices" --body cuerpo.json,--json,--strict,--base-urly--allow-insecure-http. Lee la clave deUTILIA_API_KEY(nunca la muestra) y sale con0si no hay nada que bloquee,1si lo hay (con--strict, también si hay avisos) y2si no se pudo comprobar. No manda la clave porhttp://salvo a este mismo equipo (localhost,127.0.0.1o::1). Pensada para tu asistente de IA y tu integración continua: ver La orden doctor. - Instrucciones para tu asistente de IA.
diagnostics.getMarkdown()(get_markdown()) pideGET /external/v1/diagnostics?format=markdowny devuelve el texto, sin la clave. - Diagnóstico de las claves de API de la organización.
diagnostics.organizationApiKey()(organization_api_key()) pide el diagnóstico de una claveutk_. Una claveutk_pasada enapiKeyviaja ahora enAuthorization: Bearer, que es donde la acepta el servidor (antes iba enX-Api-Keyy se rechazaba siempre), ydoctorla reconoce por su prefijo. - Claves publicadas en una web. Con la clave de una aplicación con dominios autorizados,
calls(),run(),sendWebhookSample()ycheckWebhookReceiver()responden403 DIAGNOSTICS_NOT_AVAILABLE_FOR_PUBLISHABLE_KEY(constante nueva en los dos SDK) y el diagnóstico traecredential.publishable.doctor --runno lanza la prueba activa con esa clave: lo explica y no lo cuenta como fallo. - Campos que ya mandaba el servidor:
traffic.hourlydel diagnóstico,credential.publishabledeme(), ypayloadyresponseSnippetde la muestra de un aviso.DiagnosisAccessReasonganaTENANT_SUSPENDED,TENANT_CANCELLEDyPUBLISHABLE_KEY_CEILING. - Uso de IA de tu aplicación.
sdk.aiAnalytics.getOverview()ygetTrends()(sdk.ai_analytics.get_overview()yget_trends()), constartDateyendDateopcionales y el scopeanalytics:ai:read. - Comentarios de presupuesto con clave de API.
budgetComments.listycreatevan a/external/v1/budgets/{budgetId}/comments, siempre con visibilidadCLIENT.updateyremovelanzanMETHOD_NOT_AVAILABLE_WITH_API_KEYsin hacer la petición. - Campos descartados. La opción
onIgnoredFields(on_ignored_fields) recibe los campos que el servidor descartó sin error en una escritura (cabeceraX-Utilia-Ignored-Fields); sin la opción, el SDK deja un aviso en la consola o en el registroutilia_sdk. - Errores que dicen quién lo arregla.
UtiliaSDKErrorganafixedBy,docsUrl,requiredScopesyretryAfterMs(fixed_by,docs_url,required_scopesyretry_after, en segundos), yretryablellega del cuerpo de todo error de/external/v1, con el ayudantereadErrorGuidance(read_error_guidance). Códigos tipados nuevos:INSUFFICIENT_SCOPE(conisInsufficientScopeError),DIAGNOSTICS_RATE_LIMITED,DIAGNOSTICS_NOT_AVAILABLE_FOR_PUBLISHABLE_KEY,DIAGNOSTICS_UNAVAILABLE,BUDGET_ERROR_CODESy, entre los de las credenciales,API_KEY_WRONG_KIND,API_KEY_WRONG_HEADERyAUTH_TOKEN_INVALID.INSUFFICIENT_SCOPE_MESSAGEqueda en desuso: el servidor ya nombra el permiso que falta. - Catálogo público de errores.
PUBLIC_API_ERROR_CATALOGtrae cadaerrorCodede la API con quién lo arregla y si se puede repetir, comprobado contra el del servidor.REQUEST_ERROR_CODEStipa los errores de la forma de la petición (INVALID_JSON,JSON_CONTENT_TYPE_REQUIRED,METHOD_NOT_ALLOWEDyREQUEST_BODY_TOO_LARGE), y los de facturas gananINVOICE_PDF_PENDING,INVOICE_PDF_REVIEW_REQUIRED,INVOICE_PDF_STORAGE_UNAVAILABLE,INVOICE_NUMBERING_UNAVAILABLE,INVOICE_ISSUANCE_REVIEW_REQUIREDyRECTIFICATION_OPERATION_INCOMPLETE. - Avisos tipados con sus nombres reales.
ChatWebhookEventlleva los nombres que manda el servidor (CHAT_MESSAGE_CREATED…) y lleganALL_WEBHOOK_EVENTS,WEBHOOK_EVENTS_BY_DOMAIN,WebhookEventNameeisWebhookEventName(is_webhook_event_name), comprobados contra el catálogo del servidor. - Datos fiscales de los usuarios. Permiso nuevo
crm:users:read:fiscal-data: sin él,users.listno trae el NIF ni la dirección de facturación, eidentifysolo los que mandas en esa llamada.ExternalUserlos declara opcionales. Ver Usuarios externos. - Claves dentro de una página web. Constantes nuevas
SCOPE_NOT_ALLOWED_FROM_BROWSER(403, con los permisos enerror.details.notAllowedScopes) ySCOPE_NOT_ALLOWED_WITH_ALLOWED_ORIGINS(400), y el hallazgoPUBLISHABLE_KEY_SENSITIVE_SCOPES, quedoctorenseña como aviso. Ver Claves dentro de una página web. - Catálogo de permisos. Los nombres y las descripciones de
EXTERNAL_API_SCOPE_DESCRIPTIONSson copia de los que enseña el panel. Los valores de los permisos no cambian: compara el valor, no el texto. - Tipos de
sdk.mcp.integrationsOverviewcon la forma real del servidor. Los destatus(),health()yquickstart()prometían campos que el servidor no manda (worstApp.name,credentials.v1Count,webhooks.last24hSuccessRate,events.catalogCount…), y en Pythonstatus()fallaba al validar la respuesta. Ahora son copia de la respuesta:health.worstApp.appName(worst_app.app_name),status.webhooks.last24h.successRate,events.catalogSize, y los nuevosapiCalls,appsByStatusyappsNeedingAttention. Llega tambiénappDiagnosis(appId)(app_diagnosis), el diagnóstico de una aplicación por MCP. - Captura de errores.
errors.installengancha lo que nadie trata (en el navegador,erroryunhandledrejection; en Node,uncaughtExceptionMonitor; en Python,sys.excepthook,threading.excepthookyasyncio) yerrors.capture(capture_exception) ycaptureMessage(capture_message) envían lo que atrapas. Nunca lanzan: van a una cola en memoria que envía en lotes de 25 porPOST /external/v1/errors/batch, junta los repetidos, respetaRetry-Aftery no se reporta a sí misma.flushenvía lo pendiente. - Más métodos de
errors.reportBatch(report_batch),get,resolve,reopen,silenceyunsilence. La gestión exige el scope nuevoanalytics:errors:manage: el catálogo pasa a 59. - Respuestas que dicen la verdad.
reportdevuelveaccepted,status(created,updatedoregressed) oreason, ycount.SystemError(SystemErrorEntry) ganastatus,silencedUntil,environment,firstRelease,lastRelease,regressionCountylastRegressedAt, yErrorStats,silenced. - Campos nuevos al enviar:
release,environment,fingerprintytagsenReportErrorInput.ErrorFiltersganastatusyfirstRelease(first_release), y en JavaScriptseverityadmite una sola gravedad o una lista. ai.reportError(report_error) admitereleaseyenvironmenty hace un solo intento.- Corregido (JavaScript):
errors.listenviaba varias gravedades comoseverity[]=y el servidor ignoraba el filtro. Ahora van como parámetro repetido.
Qué cambia si vienes de la 4.5
| Antes | Después | Qué hay que cambiar |
|---|---|---|
Un POST fallido por la red, un 429 o un 5xx se repetía hasta tres veces | Un POST sin clave de idempotencia (campo idempotencyKey o, en JavaScript, la cabecera Idempotency-Key) se intenta una sola vez | Si una alta (ticket, mensaje, cliente, lead, archivo, IA) falla por la red, comprueba si se creó antes de repetirla |
| Ante un 429 esperaba 0,5 s y 1 s | Espera lo que pide Retry-After; X-Utilia-Quota-Reset solo en el 429 del cupo por hora agotado sin Retry-After, y los 5xx se repiten como siempre. Si la espera pasa de maxRetryAfterMs (max_retry_after, un minuto por defecto), no reintenta y el error trae retryAfterMs. Con retryable: false en el cuerpo, no repite nunca | Nada, salvo que quieras otro tope |
Un 409 llegaba como ErrorCode.UNKNOWN | Llega como ErrorCode.CONFLICT. Es un cambio visible en una versión menor: el código que entraba por la rama de UNKNOWN deja de hacerlo | Compara con ErrorCode.CONFLICT o, mejor, con statusCode 409 (status_code en Python) y su errorCode (BILLING_IDEMPOTENCY_CONFLICT, ROTATION_IN_PROGRESS…), que no cambian entre versiones |
La validación local del SDK hablaba en inglés (title must be at least 5 characters long) | Habla en castellano y con el criterio del servidor (title: escribe al menos 5 caracteres (ahora tiene 3)) | Si comparabas el texto, compara el código del error o el campo que nombra el mensaje |
budgetComments con clave de API respondía 401 | list y create funcionan; update y remove lanzan antes de la petición | Contempla METHOD_NOT_AVAILABLE_WITH_API_KEY |
ChatWebhookEvent declaraba nombres con puntos (chat.message.created) | Declara los reales (CHAT_MESSAGE_CREATED) | Si comparabas con los nombres con puntos, tu receptor no reaccionaba a ningún aviso: cámbialos |
subscriptions.retryCharge pedía billing:subscriptions:write | Pide billing:scheduled-charges:write, porque reintentar un cobro mueve dinero; sin él, 403 INSUFFICIENT_SCOPE | Pide a la organización que conceda «Programar cobros». La firma del método no cambia |
users.list e identify traían el NIF y la dirección de facturación guardados | Solo con crm:users:read:fiscal-data | Pide ese permiso o guarda esos datos en tu sistema cuando los mandes |
Los tipos de sdk.mcp.integrationsOverview declaraban worstApp.name, webhooks.last24hSuccessRate y otros campos que nunca llegaban | Declaran los que manda el servidor | El código que leía los nombres viejos deja de compilar: cámbialo por los nuevos (worstApp?.appName, webhooks.last24h.successRate…) |
errors.list con varias gravedades devolvía todas (JavaScript) | Filtra de verdad | Nada, salvo que contaras con ver todas |
process.on('unhandledRejection', ...) que llamaba a errors.report | errors.install | Quita ese manejador: provoca un bucle si el envío falla |
| Un error resuelto que volvía a ocurrir seguía resuelto | Se reabre solo y llega ERROR_REOPENED con reason: regression | Si escuchas ERROR_REOPENED, distingue reason |
Un 4xx en el contexto respondía como registrado, con hash vacío | accepted: false con reason: discarded_expected | Mira accepted antes de usar el hash |
| El envío de errores gastaba el cupo por hora de la clave | Límite propio de 120 peticiones por minuto en cada ruta | Nada |
Los tres últimos cambios de los errores son del servidor, no del SDK: llegan igual con cualquier versión.
Las lecturas, PUT, PATCH, DELETE y los POST que solo consultan (invoices.validate, users.identify, el pase de conexión, checkRequest, run y el carril MCP) se reintentan como antes.
Novedades de la versión 4.5.0
La 4.5.0 (2026-10-21) trae los métodos de las claves de API de la organización y el ensayo de facturas sin numerar, y retira los métodos que ninguna organización cliente podía usar. Está sin publicar en npm y en PyPI.
- Claves de API de la organización por MCP.
sdk.mcp.apiCredentialsganaorgKeysList,orgKeysGet,orgKeysUsage,orgKeysCatalog,orgKeysCandidates,orgKeysCreate,orgKeysUpdate,orgKeysRotate,orgKeysEndTransitionyorgKeysRevoke(en Python, ensnake_case). Crear, renovar y ampliar una clave piden la confirmación en el móvil: la primera llamada lanzaAPPROVAL_REQUIREDy se repite contwoFactorApprovalId. Ver las operacionesorg_keys_*en la referencia MCP. - Credenciales personales y de aplicación.
createyrotateadmitentwoFactorApprovalId, que el servidor ya pedía.createya no admitekind: 'SERVICE_ACCOUNT'yCreateApiCredentialInput.userIdqueda en desuso. - Probar sin facturas reales.
invoices.validate,invoices.validatePdf(validate_pdf),invoices.rectifications.validateywebhooks.signTestPayload(sign_test_payload), con los camposdisplayNumberyemailDeliveryen los tipos de las facturas ylivemodeymeta.testen los avisos. - Listados del CRM por MCP. El servidor ya no repite la lista en
clients,contactsyleads; el SDK rellena ese alias desdeitems, así que el código que aún lo lee sigue funcionando.
Qué cambia si vienes de la 4.4
| Antes | Después | Qué hay que cambiar |
|---|---|---|
apiCredentials.create admitía kind: 'SERVICE_ACCOUNT' | Ya no: el servidor lo rechazaba | Para conectar un programa, usa orgKeysCreate |
Métodos que ninguna organización cliente podía usar: AnnouncementsService y sdk.mcp.announcements, varios métodos de ajustes de sdk.mcp.orgIntegrationsSettings y el campo isFirstParty de las altas de OAuth | Ya no existen | Bórralos de tu código: respondían siempre «prohibido» |
Novedades de la versión 4.4.0
La 4.4.0 (2026-10-15) trae las series de facturación de las aplicaciones. Es aditiva: sin seriesCode ni serie por defecto en tu aplicación, todo se numera exactamente igual que en la 4.3.
- Series de tu aplicación. Tu organización puede reservar a tu aplicación una o varias series (por ejemplo,
APP-202600001), separadas de las facturas del equipo, y fijar cuál se usa por defecto.invoices.listSeries()(list_series(), asíncrono y síncrono) las consulta concrm:invoices:read, yinvoices.createlas elige conseriesCode(series_code) oDEFAULTpara la de siempre. Tu aplicación no crea ni cambia series. Ver Series disponibles. - Suscripciones:
type(ordinarias o simplificadas, estas solo con cuotas de hasta 400 € impuestos incluidos) yseriesCodeen el alta y en el cambio. La serie queda fijada en la suscripción. - Respuestas ampliadas:
seriesCodeen cada factura (también en las rectificativas, conDEFAULT) ytypeyseriesCodeen cada suscripción.invoices.listfiltra porseriesCode. - Fecha de expedición acotada:
issueDateno puede ser futura ni anterior a la de la última factura de la misma serie, y su año es el de la numeración (INVOICE_ISSUE_DATE_OUT_OF_ORDER). - Errores:
INVOICE_ERROR_CODES(InvoiceErrorCode) ganaINVOICE_SERIES_NOT_AVAILABLE,INVOICE_SERIES_TYPE_MISMATCH,INVOICE_SERIES_INACTIVEeINVOICE_ISSUE_DATE_OUT_OF_ORDER, y los dos de la gestión de series de la organización (INVOICE_SERIES_PREFIX_RESERVED,INVOICE_SERIES_IN_USE). - MCP:
appsBillingGetConfigyappsBillingUpdateConfig(apps_billing_get_configyapps_billing_update_config) leen y fijan las series por defecto de la aplicación. - Ventanilla única del IVA (Próximamente). El SDK trae ya el contrato de una función que todavía no está disponible para las organizaciones:
taxSystem: 'EU_OSS', líneasEU_VATconvatRateClassyossSupplyType,consumerResidence.evidenceItems,invoices.getEuVatRates()(get_eu_vat_rates(), asíncrono y síncrono),taxSystem,consumerCountryCodeyossSchemeen las respuestas y los 14 códigosOSS_*. Mientras tanto, cualquier dato suyo responde 403FEATURE_COMING_SOON, que se reconoce conisFeatureComingSoonErrorygetComingSoonFeature(is_feature_coming_soon_erroryget_coming_soon_feature). - Permisos: sin scopes nuevos; el catálogo sigue en 57. La descripción de
crm:invoices:readdice ahora que también permite consultar las series y, si la ventanilla única está disponible, los tipos de IVA de la Unión.
Qué cambia si vienes de la 4.3
| Antes | Después | Qué hay que cambiar |
|---|---|---|
issueDate libre, y el año del número salía del reloj | No puede ser futura ni anterior a la última factura de la misma serie; su año es el del número | Si mandas fechas pasadas o futuras, respeta el orden de la serie o captura INVOICE_ISSUE_DATE_OUT_OF_ORDER |
| Las facturas de la aplicación siempre en la serie general | En la serie por defecto de la aplicación, si tu organización le fija una | Nada, salvo que quieras la de siempre: manda seriesCode: 'DEFAULT' |
TaxSystem sin EU_OSS | TaxSystem gana EU_OSS, y las respuestas traen taxSystem, consumerCountryCode y ossScheme | Si haces un switch exhaustivo sobre TaxSystem, añade el caso |
Novedades de la versión 4.3.0
La 4.3.0 (2026-10-13) amplía la facturación de las aplicaciones que cobran con su propia pasarela. Todo es aditivo en la API externa.
- Facturas simplificadas.
invoices.createadmitetype: 'SIMPLIFIED'(type=InvoiceType.SIMPLIFIED): serie propia (T-), hasta 400 € impuestos incluidos y destinatario opcional. Sintype, la factura sigue siendo ordinaria.listfiltra portype. Ver Facturas. - Precios con impuestos incluidos (
pricesIncludeTax,prices_include_tax): el total de cada línea es exactamente lo cobrado; 39,99 € con IGIC al 7 % son 37,37 de base y 2,62 de cuota. - Factura ya cobrada con el bloque
paymenty residencia del consumidor conconsumerResidence(consumer_residence). - Régimen del pequeño empresario del IGIC (REPEP): causa
EXEMPT_IGIC_SMALL_BUSINESSy mención «Exención franquicia fiscal», que UTILIA OS pone sola dentro de un periodo del régimen. - Rectificativas desde tu aplicación:
invoices.rectifications.createylist, sobrePOSTyGET /external/v1/invoices/:id/rectifications. UTILIA OS decide el código legal (R5 o R1); el bloquerefundregistra la devolución hecha en tu pasarela. Scope nuevocrm:invoices:rectify, crítico financiero: el catálogo pasa a 57. Ver Facturas rectificativas. - Respuestas ampliadas:
type,legalMentions,originalInvoiceId,paymentRegisteredy, por línea,exemptionReasonyexempt; en el detalle,rectificationsyeffectiveTotal. - Errores:
INVOICE_ERROR_CODESeisInvoiceErrorCode(InvoiceErrorCodeeis_invoice_error_codeen Python) con los códigos nuevos, y los datos de un error de negocio (missingFields,lineNumber,remainingRectifiableAmount) enerror.details. - Corregido: un 403 cuyo mensaje contenía «origen» se reescribía como «Origen no permitido por CORS». El rechazo por origen se reconoce ahora solo por su
errorCode(ORIGIN_ERROR_CODES,isOriginErrorCode).
Qué cambia si vienes de la 4.2
| Antes | Después | Qué hay que cambiar |
|---|---|---|
CreateInvoiceInput.recipient y sus campos de dirección obligatorios en el tipo | Opcionales en el tipo (None por defecto en Python) | Si leías esos campos de un objeto propio tipado así, contempla que falten |
Ordinaria sin recipient en Python: error de validación del servidor | ValueError al construir el modelo, antes de salir a la red | Captura ValueError |
UtiliaSDKError.details vacío en los errores de negocio | Trae el cuerpo de la respuesta | Nada; puedes leer los datos nuevos |
| Un 403 con «origen» en el mensaje se convertía en «Origen no permitido por CORS» | Llegan el mensaje y el errorCode del servidor | Si decidías por ese texto, decide por errorCode |
InvoiceRectificationsService construido sin argumentos | Recibe el cliente en el constructor | Usa sdk.invoices.rectifications, que ya viene construido |
Novedades de la versión 4.2.0
La 4.2.0 (2026-10-13) acompaña los cambios de la API externa sobre las claves de las aplicaciones: la clave ya no viaja en ninguna dirección, las claves se pueden renovar con un periodo de transición y el SDK avisa cuando usas la anterior.
- Pase de conexión.
sdk.tickets.createStreamToken({ route, userId })o({ route, jobId })(tickets.create_stream_token(route, user_id=..., job_id=...)en Python) pide un pase de un solo uso y cinco minutos para abrir por tu cuenta una conexión en tiempo real, sin la clave en la dirección. En JavaScript,streamUpdates,ai.streamSuggestionsyai.streamTranscriptionya lo piden solos, ystreamUpdatesreconecta con un pase nuevo cada vez (esperas de 1 a 30 segundos) y deja de intentarlo si el servidor rechaza el pase con un 4xx. En Python, las conexiones del SDK llevan la clave en la cabecera. Ver Abrir la conexión por tu cuenta. - Verificar avisos con varias claves.
sdk.webhooks.verifyyparseleen tambiénX-Utilia-Webhook-Signaturesy aceptan una clave o varias, con fecha de caducidad (validUntil,WebhookSecret(..., valid_until=...)en Python), para poner la clave de firma nueva en tu servidor en cuanto la renuevas. Errores locales nuevos:WEBHOOK_SECRET_EXPIREDyWEBHOOK_SECRET_INVALID. Ver Cuando renuevas la clave de firma. - Aviso de clave anterior. La opción
onApiKeyExpires(on_api_key_expires) recibe, una vez por fecha, el aviso de que la clave en uso es la anterior de una renovación y hasta cuándo vale;sdk.apiKeyExpiresAt(api_key_expires_at) guarda esa fecha. Sin la opción, el SDK deja un único aviso por fecha en la consola o en el registroutilia_sdk. Ver Configurar el cliente. - El SDK se identifica con la cabecera
X-Utilia-SDK(utilia-sdk-js/4.2.0,utilia-sdk-python/4.2.0). - Errores de las credenciales tipados:
CREDENTIAL_ERROR_CODES,isCredentialErrorCodeyKnownErrorCodeen JavaScript;CredentialErrorCodeeis_credential_error_codeen Python. Con OAuth, un 401OAUTH_BEARER_NOT_ALLOWEDya no renueva el token ni repite la petición. - Confirmación en el móvil de las operaciones MCP:
getApprovalChallenge,isApprovalRequiredyAPPROVAL_REQUIRED(get_approval_challenge,is_approval_required). Ver Confirmar una operación en el móvil. budgetSignaturescon clave de API:list,getyverifyvan a/external/v1/budgets/{budgetId}/signaturesy funcionan. El resto de métodos lanzanMETHOD_NOT_AVAILABLE_WITH_API_KEYantes de hacer la petición.SignerAuditEventganaipAddressMaskedydeviceLabel.- MCP, renovación de claves de las aplicaciones externas:
appsRegenerateApiKeyyappsWebhooksRegenerateSecretadmitengraceDaysytwoFactorApprovalId, y lleganappsWebhooksSecretCutover,appsWebhooksSecretRetirePrevious,appsWebhooksSecretAdvisoryDismiss,appsApiKeyRetirePrevious,appsApiKeyAdvisoryDismiss,appsSetResponsible,appsListResponsibleCandidatesyappsWebhooksTestSigningKey(en Python, ensnake_case).
Qué cambia si vienes de la 4.1
| Antes | Después | Qué hay que cambiar |
|---|---|---|
EventSource propio con buildSseUrl('/external/v1/tickets/...') (JavaScript) | La dirección ya no lleva la clave | Usa el url de tickets.createStreamToken |
SignerAuditEvent.ipAddress y userAgent | Ya no llegan | Usa ipAddressMasked y deviceLabel |
Métodos de budgetSignatures sin acceso con clave de API: 401 (UNAUTHORIZED) | METHOD_NOT_AVAILABLE_WITH_API_KEY (FORBIDDEN), sin hacer la petición | Si decidías por el código, contempla el nuevo |
downloadCertificate con OAuth en Python: httpx.HTTPStatusError | UtiliaSDKError | Captura UtiliaSDKError |
RegenerateApiKeyResponse.app obligatorio | Opcional y en desuso: el servidor nunca lo ha enviado | No dependas de ese campo |
streamUpdates sin errores del pase (JavaScript) | Puede pasar a onError un UtiliaSDKError y, tras un 4xx del pase, no reconecta | Trata ese error en onError |
Responsable inactivo: 401 API_KEY_INVALID o 422 EXTERNAL_APP_CREATOR_MISSING | 403 APP_RESPONSIBLE_INACTIVE | Contempla el código nuevo |
El último cambio es del servidor, no del SDK: llega igual con cualquier versión.
Novedades de la versión 4.1.1
La 4.1.1 (2026-10-11) dejó de poner credenciales en las direcciones que genera el SDK. budgets.getPdfUrl, budgetSignatures.getCertificateUrl y budgets.streamGenerateItemsStatus ya no incluyen la clave, y, con clave de API, lanzan antes de construir nada METHOD_NOT_AVAILABLE_WITH_API_KEY, porque sus rutas exigen la sesión de una persona y nunca han aceptado la clave. Con OAuth devuelven la dirección como hasta ahora, sin credenciales. En Python, la clave de las conexiones en tiempo real pasó a viajar solo en la cabecera. Sin cambios incompatibles para ninguna integración que funcionara. No se publicó por separado: va incluida en la 4.2.0.
Novedades de la versión 4.1.0
La 4.1.0 (2026-09-12) añade el calendario laboral de la empresa por MCP, sin
cambios incompatibles. sdk.mcp.attendance.holidays envuelve la parte de
festivos de la herramienta attendance del servidor de recursos humanos
(POST /api/mcp/rrhh):
getScope()(get_scope()en Python) devuelve el ámbito de la empresa (comunidad autónoma, isla y municipio) y el vocabulario cerrado para elegirlo.setScope()(set_scope()) lo fija. No borra ningún festivo: los del ámbito anterior dejan de contar y quedan visibles como «de otros ámbitos».import(year)(import_year(year), porqueimportes palabra reservada en Python) carga el calendario oficial de ese año. Es idempotente.list()lista los festivos;applicablefiltra los que cuentan para la empresa o los de otros ámbitos.
Los festivos municipales no se cargan: los publica cada ayuntamiento y la
respuesta lo avisa en notice. Consultar y listar exige hr:attendance:view;
elegir el ámbito y cargar el año, hr:attendance:manage.
Si ya estás en la 4.0.1, subir a la 4.1.0 no te obliga a tocar nada.
Novedades de la versión 4.0.1
La 4.0.1 (2026-09-09) es una versión solo de documentación interna del paquete: no cambia ningún comportamiento ni ninguna firma. Además alinea el número de versión del paquete de Python, que arrastraba un 2.23.0 en su package.json que no correspondía con lo que de verdad se publica.
Si ya estás en la 4.0.0, subir a la 4.0.1 no te obliga a tocar nada.
Novedades de la versión 4.0.0
La 4.0.0 (2026-09-07) añade sdk.paymentMethods.getReadiness()
(payment_methods.get_readiness() en Python, en las variantes asíncrona y
síncrona), que responde si la organización puede cobrar con tarjeta ahora mismo
y por qué no. Es el mismo criterio que usa la interfaz de UTILIA OS para
decidir si enseña el botón de cobrar. Tipos nuevos: ChargingReadiness y
ChargingReadinessReason. El detalle está en
Comprobar si se puede cobrar.
Es una versión mayor porque retira los tres métodos del correo al proveedor: getEmailRecipients, sendEmail y replyEmail.
Novedades de la versión 3.0.0
La 3.0.0 (2026-09-05) hace que la facturación y el cobro de una aplicación externa funcionen de verdad. Es una versión mayor, y la razón principal no es el volumen de cambios: es que cinco servicios que existían y devolvían 401 siempre pasan a funcionar contra otra ruta, y uno desaparece.
Lo que gana:
sdk.subscriptionsfunciona, sobre/external/v1/subscriptions, con trece métodos: alta, listado, detalle, cambios, pausa, reanudación, baja, reactivación, generación adelantada, próximo cobro, facturas generadas, historial de ejecuciones y reintento del cobro.sdk.paymentAuthorizationses nuevo (sdk.payment_authorizationsen Python), con el texto de consentimiento, el alta de la autorización firmada, el listado, el detalle y la retirada.sdk.invoicesgana cinco acciones:send,charge,markPaid,getPublicLinkyregeneratePublicLink.sdk.invoices.scheduledChargesfunciona, con programar, listar, cancelar y adelantar el reintento.sdk.invoices.refunds.createemite el reembolso por la API externa.sdk.webhooks.verifyysdk.webhooks.parseverifican la firma de cualquier familia de avisos, con tipos por evento para facturación, suscripciones y cobros.sdk.users.identifyaceptaclientIdpara vincular el usuario a una ficha de cliente que la aplicación creó antes.- Los enumerados se derivan del esquema real:
InvoiceStatuspasa de 7 a 12 valores yTaxSystemsumaIPSIeINTERNATIONAL. En Python, un enumerado incompleto reventaba con un error de validación en la primera factura reembolsada. - El catálogo de permisos de los dos SDK pasa a 56, con la categoría nueva de críticos financieros.
Tabla de rupturas
| Antes | Después | Qué hay que cambiar |
|---|---|---|
sdk.subscriptions.* sobre /finance/recurring-invoices (401 siempre) | sobre /external/v1/subscriptions | clientId desaparece y userId pasa a obligatorio en todos los métodos |
sdk.subscriptions.create({ clientId, ... }) | create({ userId, ... }) | Cambiar el identificador |
sdk.subscriptions.remove(id) | retirado | La baja es cancel(id, { userId }), que conserva el historial |
sdk.subscriptions.stats() | retirado | Era una métrica del tenant, no de la aplicación |
sdk.subscriptions.pause(id) | pause(id, { userId }) | Añadir el usuario |
sdk.mandates.* | sdk.paymentAuthorizations.* | Renombre visible. El alias antiguo sigue funcionando y avisa |
sdk.mandates.listForClient(clientId) | retirado | Una aplicación externa no conoce clientes del CRM |
sdk.mandates.revoke(id) | revoke(id, { userId }) | Añadir el usuario |
sdk.invoices.scheduledCharges.schedule(invoiceId, { stripePaymentMethodId, mandateConsent }) | .schedule(invoiceId, { userId, scheduledAt, paymentAuthorizationId }) | Cuerpo distinto. El consentimiento se firma antes, en su propio endpoint |
sdk.invoices.scheduledCharges.update(...) | retirado | Cambiar la fecha es cancelar y volver a programar |
sdk.invoices.refunds.cancelPreview y .cancel | sdk.invoices.refunds.create(...) | Deshacer un reembolso es del equipo; lo que la aplicación puede hacer es emitirlo |
sdk.invoices.rectifications.updateLegalMetadata | retirado con error claro | Reservado al equipo; usar la herramienta MCP crm_invoices_rectifications |
sdk.invoices.getPdfUrl(id, userId) | retirado | Sustituto: regeneratePublicLink. Si lo usaste, regenera la clave de API |
sdk.chat.webhooks.verify(...) | sdk.webhooks.verify(rawBody, headers, secret) | Sale de chat. El alias antiguo conserva su firma |
sdk.scheduled_charges (Python, en la raíz) | sdk.invoices.scheduled_charges | Reubicación. El alias antiguo sigue funcionando y avisa |
sdk.scheduled_charges.retrieve(id) (Python) | retirado | La ruta no existía |
InvoiceStatus con 7 valores (Python) | 12 valores | El enumerado reventaba en la primera factura reembolsada |
Antes de actualizar, revisa tus permisos
La 3.0.0 estrena once permisos y endurece uno existente: POST /external/v1/users/identify pasa a exigir crm:users:write. Si tu aplicación ya usaba identify, concédele ese permiso antes de desplegar, o la llamada empezará a responder 403.
Novedades de la versión 2.27.0
La 2.27.0 (2026-09-04) completa la paridad del CRM en los dos SDK oficiales.
sdk.mcp.crmes nuevo, con clientes, contactos y leads: altas y ediciones unitarias o por lotes, duplicados, fusiones y transiciones.- Las operaciones destructivas o con efecto externo mandan confirmación explícita desde sus métodos de alto nivel.
- Los errores MCP conservan
errorCodeydetails, de modo que una integración puede distinguir permisos, campos inválidos y reglas de negocio.
Novedades de la versión 2.26.0
La 2.26.0 (2026-09-02) recoge el lote MCP del 2 de septiembre: dos herramientas transversales nuevas y varios cambios de contrato del servidor. Los detalles y las fechas de caducidad de las redes de compatibilidad están en la sección de cambios de contrato.
sdk.mcp.searchysdk.mcp.notifications, nuevos. El buscador global del ecosistema y los avisos de la persona que autorizó el token. Tienen página propia.- El cliente MCP deja de tragarse el mensaje de los errores de una herramienta. Ahora lee las tres formas que conviven, añade la pista al mensaje y conserva el código original en
errorCode.
Novedades de la versión 2.23.0
La 2.23.0 (2026-08-05) cierra el lote de medición y atribución de Marketing: sdk.mcp.marketing pasa de 20 a 87 métodos por lenguaje y cubre enteras marketing_campaigns (35 operaciones), marketing_lists (17) y marketing_deliverability (10).
- Comportamiento de la audiencia y segundos intentos:
engagementBreakdown(siete grupos, dos niveles, y el recuento es gratuito),resendPreview,createResendylistResends. - Interés de los suscriptores:
listSubscribers—que no existía en MCP—,engagementSummary,setAsideInactiveyreactivateSubscriber. Apartar no es dar de baja y el consentimiento no se toca. - Quejas por correo no deseado:
reports.complaints,deliverability.getComplaintPolicyysetComplaintPolicy. La tasa esnullcuando no hay forma de medirla, nunca0 %. - Medición del correo:
reports.linkHeatmapyreports.audienceTech. El país es aproximado y sale de una base local.
Novedades de la versión 2.22.0
La 2.22.0 (2026-08-03) completa las imágenes de presupuesto en sdk.budgets:
uploadImageToSection(budgetId, sectionId, file, placement?)/upload_image_to_section(...): atajo para colocar una imagen dentro de un apartado. El apartado lo fija la dirección, así que el cuerpo no puede apuntar a otro.scanLegacyImages(budgetId)/scan_legacy_images(...): detecta las imágenes heredadas incrustadas en el HTML de los apartados, que se ven en el editor y no salen en el PDF.migrateLegacyImages(budgetId)/migrate_legacy_images(...): las convierte en colocaciones reales. Es idempotente y tolera fallos parciales: cada imagen vuelve comoMIGRATEDoFAILEDcon su motivo.- Quedan obsoletos
textWrap(el generador del PDF nunca lo aplicó: la imagen siempre ocupa su propia línea) y la opcióndeleteFileal borrar una imagen (el borrado del binario pasó a ser automático por recuento de referencias). Los dos se siguen aceptando y el servidor los ignora.
Novedades de la versión 2.21.0
La 2.21.0 (2026-07-28) hace que sdk.oauth.getAuthorizedApps() / get_authorized_apps() devuelva los permisos ya traducidos y recortados por el plan de la organización: permissions, permissionCount, unavailablePermissionCount y unavailableModules, todos opcionales para no romper contra un backend anterior.
AuthorizedApp.scope cambió de significado en la 2.21.0. El campo queda obsoleto (retirada prevista el 2027-02-01) y, sobre todo, ya no es el universo de scopes concedidos a la aplicación, sino ese universo recortado por el plan. Un scope que sigue vivo en el token puede no aparecer en el campo si su aplicativo no está contratado, así que quien lo use para decidir qué puede hacer la aplicación verá menos de lo que hay. Sustitúyelo por permissions (lo ejercible) y unavailablePermissionCount (lo que quedó fuera).
Novedades de la versión 2.18.0
La 2.18.0 (2026-07-05) incorpora los grupos de empresas del CRM (matriz y filiales) a la API externa de clientes:
- El tipo
ExternalClientincluyeparentClientId(escalar, presente en todas las respuestas) yparent { id, name, slug }(solo en el detalle). - Nuevo método
sdk.externalClients.getGroup(id)/sdk.external_clients.get_group(client_id): rol del cliente en su grupo (PARENT/SUBSIDIARY/STANDALONE), referencia a la matriz y lista de filiales. Requiere el scopecrm:clients:read. updateaceptaparentClientIdpara vincular una filial a su matriz o desvincularla (nullen JavaScript;Noneexplícito en Python). Requiere el sub-scope nuevocrm:clients:write:hierarchyademás decrm:clients:write.- Webhooks nuevos
CLIENT_LINKED_TO_GROUPyCLIENT_UNLINKED_FROM_GROUP(payload de solo identificadores). - El espejo del catálogo de scopes pasa a 36 entradas con
crm:clients:write:hierarchy.
La 2.17.0 (2026-07-02) añadió sdk.mcp.opportunities.listStages() / list_stages() para las etapas de oportunidad configurables por organización.
Cambios aditivos, sin breaking changes. Detalle completo en Endpoints de clientes del CRM.
Novedades de la versión 2.16.0, y por qué quedó rota
La 2.16.0 (5 de junio de 2026) añadió sdk.subscriptions y sdk.mandates
envolviendo rutas internas de /finance/*, no la API externa.
Esas rutas exigen la sesión de un usuario del panel, que el SDK no sabe obtener: con clave de API devolvían 401 y con OAuth también, porque el token OAuth es opaco y solo se resuelve en el carril MCP. Hasta el 4 de agosto de 2026 esta página y los comentarios del SDK decían que bastaba con OAuth, y era falso.
Se arregló en la 3.0.0, que reescribe esos servicios sobre /external/v1/*
con la clave de API. Si tu código quedó escrito contra la 2.16.0, revisa la
tabla de rupturas de arriba: las firmas cambian.
Hitos por versión
Resumen de las versiones más relevantes de ambos SDK, de la rama 1.x a la actual. La numeración es compartida entre JavaScript y Python.
| Versión | Fecha | Aportación principal |
|---|---|---|
| 4.6.0 | Sin publicar | Comprobar tu integración: sdk.diagnostics, la orden doctor, getMarkdown(), sdk.aiAnalytics, onIgnoredFields, errores con fixedBy, docsUrl y retryable y su catálogo público, reintentos que respetan Retry-After y no repiten altas sin clave de idempotencia, la validación local en castellano y la captura de errores en dos líneas (errors.install, capture), con su gestión (resolve, reopen, silence). Catálogo de permisos: 59. Ver la tabla de cambios de la 4.5. |
| 4.5.0 | 2026-10-21 | Claves de API de la organización por MCP (orgKeys*), la validación en seco de facturas y rectificativas, signTestPayload y la retirada de los métodos que solo servían a la organización dueña de la plataforma. Catálogo de permisos: 57. Ver la tabla de cambios de la 4.4. |
| 4.4.0 | 2026-10-15 | Series de facturación de la aplicación: listSeries, seriesCode y fecha de expedición acotada; contrato de la ventanilla única del IVA (Próximamente). Ver la tabla de cambios de la 4.3. |
| 4.3.0 | 2026-10-13 | Facturas simplificadas y rectificativas desde tu aplicación: type: 'SIMPLIFIED', pricesIncludeTax, payment, consumerResidence, régimen del pequeño empresario del IGIC y invoices.rectifications.create y list. Catálogo de permisos: 57. Ver la tabla de cambios de la 4.2. |
| 4.2.0 | 2026-10-13 | Claves de las aplicaciones sin exponer: pase de conexión (createStreamToken), verificación de avisos con varias claves, aviso de clave anterior (onApiKeyExpires), errores de credenciales tipados, confirmación en el móvil de las operaciones MCP y budgetSignatures con clave de API. Ver la tabla de cambios de la 4.1. |
| 4.1.1 | 2026-10-11 | Ninguna dirección lleva la clave: getPdfUrl, getCertificateUrl y streamGenerateItemsStatus avisan de que no funcionan con clave de API. Incluida en la 4.2.0. |
| 4.1.0 | 2026-09-12 | Calendario laboral por MCP: sdk.mcp.attendance.holidays consulta y fija el ámbito de festivos de la empresa, carga el calendario oficial de un año y lista los festivos que cuentan. Sin cambios incompatibles. |
| 4.0.1 | 2026-09-09 | Solo documentación interna del paquete y número de versión del paquete de Python alineado. Sin cambios de comportamiento ni de firma. |
| 4.0.0 | 2026-09-07 | Disposición de cobro con tarjeta: sdk.paymentMethods.getReadiness() (payment_methods.get_readiness() en Python) responde si la organización puede cobrar ahora mismo y por qué no. Versión mayor por una retirada en un servicio no disponible para las organizaciones cliente. |
| 3.1.0 | 2026-09-05 | Honestidad de la medición y fiabilidad del envío en Marketing por correo: 39 métodos nuevos y dos sub-espacios nuevos, sin cambios incompatibles. Aviso: marketing:write deja de enviar por sí solo. |
| 3.0.0 | 2026-09-05 | Facturación y cobro de aplicaciones externas: sdk.subscriptions y sdk.invoices.scheduledCharges funcionan de verdad sobre /external/v1/*, sdk.paymentAuthorizations es nuevo, sdk.invoices gana send, charge, markPaid y el enlace público, y sdk.webhooks verifica cualquier familia de avisos. Versión MAYOR: ver la tabla de rupturas. Catálogo de permisos: 56. |
| 2.27.0 | 2026-09-04 | CRM completo por MCP: servicio sdk.mcp.crm, con lotes y fusión de duplicados. |
| 2.26.0 | 2026-09-02 | Buscador global y avisos por MCP (sdk.mcp.search, sdk.mcp.notifications). |
| 2.23.0 | 2026-08-05 | Medición y atribución de Marketing: marketing_campaigns, marketing_lists y marketing_deliverability completas; comportamiento de la audiencia, segundos intentos, interés de suscriptores, quejas por correo no deseado, mapa de calor por enlace y desglose por aparato, programa y país. |
| 2.22.0 | 2026-08-03 | Imágenes de presupuesto completas: uploadImageToSection, scanLegacyImages y migrateLegacyImages. Quedan obsoletos textWrap y la opción deleteFile. |
| 2.21.0 | 2026-07-28 | Permisos recortados por el plan en getAuthorizedApps(): permissions, permissionCount, unavailablePermissionCount y unavailableModules. AuthorizedApp.scope queda obsoleto y cambia de significado. |
| 2.20.0 | 2026-07-12 | Plataforma de chat extensible (sdk.chat): publicar (sendMessage, v1) y leer canales, miembros e historial visible (listChannels/getChannel/listMembers/listMessages/getMessage, v2), más sdk.chat.webhooks.verify() para validar localmente la firma HMAC de los webhooks de chat. Catálogo de scopes: 40 (Chat pasa de 1 a 4 con chat:channels:view, chat:messages:view y chat:events:subscribe). |
| 2.18.0 | 2026-07-05 | Grupos de empresas: parentClientId/parent en ExternalClient, getGroup, vínculo vía update con el sub-scope crm:clients:write:hierarchy y webhooks CLIENT_LINKED_TO_GROUP / CLIENT_UNLINKED_FROM_GROUP. Catálogo de scopes: 36. |
| 2.17.0 | 2026-07-02 | Etapas de oportunidad configurables: sdk.mcp.opportunities.listStages(). |
| 2.16.0 | 2026-06-05 | Paridad de suscripciones (sdk.subscriptions) y mandatos (sdk.mandates). |
| 2.15.1 | 2026-05-25 | Ajuste del catálogo de scopes externos: se retira fm:files:delete y se añaden 7 scopes (presupuestos, analítica de IA, telemetría de errores). Catálogo resultante en esa versión: 35 scopes. |
| 2.15.0 | 2026-05-25 | Catálogo público de scopes de la API externa como tipos TypeScript / modelos, espejo 1:1 del backend. |
| 2.13.0 | 2026-05-24 | Servicios sdk.externalLeads y sdk.externalClients (/external/v1/leads y /external/v1/clients). |
| 2.12.1 | 2026-05-21 | Servicio sdk.externalContacts (/external/v1/contacts), pensado para aplicaciones de envío de correo. |
| 2.10.0 | 2026-05-14 | Servicio MCP tipado sdk.mcp.opportunities (refleja la tool crm_opportunities_stats). |
| 2.9.0 | 2026-05-12 | Tool MCP de tickets y ampliaciones del árbol sdk.mcp.*. |
| 2.8.0 | 2026-05-12 | Módulo sdk.mcp.*: integraciones, credenciales de API, ajustes de integraciones de la organización y vista de resumen. |
| 1.8.0 | 2026-04-08 | Parámetro theme ('light' / 'dark') en la pantalla de consentimiento OAuth (en Python, 0.7.0). |
Los dos SDK admiten OAuth 2.1 con PKCE como alternativa a la clave de la aplicación. El parámetro theme para elegir tema claro u oscuro en la pantalla de consentimiento llegó en las versiones 1.8.0 (JavaScript) y 0.7.0 (Python). Ver OAuth con el SDK.
Catálogo de scopes de la API externa
El catálogo de scopes que una ExternalApp puede tener concedidos se expone en el endpoint:
/api/integrations/apps/scopes/catalogLa fuente de verdad es la lista EXTERNAL_API_SCOPES_LIST del backend, que tiene 61 permisos. Las cifras que aparecen en la tabla de hitos (35, 36, 40, 45, 56, 57) son el tamaño del catálogo en aquella versión, no el de hoy. El conteo canónico y su desglose viven en Catálogo de permisos de la API:
| Dominio | Permisos |
|---|---|
| Contactos | 6 |
| Leads | 5 |
| Clientes | 5 |
| Usuarios externos | 3 |
| Facturación | 6 |
| Cobros con tarjeta | 4 |
| Suscripciones | 3 |
| Autorizaciones de cobro | 3 |
| Cobros programados | 2 |
| Soporte | 3 |
| Ficheros | 2 |
| Presupuestos | 4 |
| Analítica y telemetría | 4 |
| Chat | 9 |
| Instalaciones del catálogo | 2 |
| Total | 61 |
El cuarto scope del dominio Clientes es crm:clients:write:hierarchy (2.18.0): gestiona el vínculo matriz-filial de los grupos de empresas y se concede por separado del write genérico. El dominio Chat aporta nueve scopes con la plataforma de chat extensible: chat:messages:send (v1, publicar); chat:channels:view, chat:messages:view y chat:events:subscribe (v2: leer canales y mensajes, y recibir webhooks); chat:channels:create, chat:channels:manage y chat:members:manage (v3: crear, editar y archivar canales y gestionar sus miembros); y chat:agents:view y chat:agents:manage:external (v4: gestión de agentes de IA por apps). De todos ellos, solo chat:agents:manage:external exige SUPER_ADMIN y queda fuera de todo paquete.
Los scopes de gobierno del canal (v3) tienen ya sus métodos en sdk.chat: createChannel, updateChannel, archiveChannel, addMember y removeMember (en Python, en snake_case). La gestión de agentes de IA de la v4 (sdk.chat.agents) se documenta en Gestionar agentes de IA.
El cuarto scope de Analítica y telemetría es analytics:errors:manage (4.6.0): resolver, reabrir y silenciar los errores que ha enviado la propia aplicación. Los dos de Instalaciones del catálogo, apps:installations:read y apps:installations:exchange, llegan con la 4.7.0 y son de la aplicación que publica una ficha en el catálogo.
De esos 61 permisos, 17 solo puede concederlos un Super Administrador, con un motivo razonado de al menos veinte caracteres. Nueve lo son por datos personales críticos (contactos en derecho de oposición, notas internas de contactos, leads y clientes, conversión de leads, datos fiscales de clientes y de usuarios, envío de datos de tickets a proveedores de IA y verificación de firmas), uno por permitir que una aplicación gestione agentes de IA (chat:agents:manage:external) y siete por ser críticos financieros: billing:charge, billing:refund, billing:subscriptions:cancel, billing:payment-authorizations:write, billing:scheduled-charges:write (desde el 21 de octubre de 2026), crm:invoices:cancel y crm:invoices:rectify (4.3.0). crm:users:read:fiscal-data llegó con la 4.6.0.
La categoría de críticos financieros es nueva en la 3.0.0. Existe porque quien revisa la solicitud de una aplicación necesita saber por qué un permiso está blindado: no es lo mismo «leerá notas sobre personas» que «podrá cobrar y devolver dinero de tus clientes».
Cambio de catálogo en la 2.15.1. El scope fm:files:delete se retiró del catálogo porque ningún endpoint de la API externa lo aplicaba (no existe DELETE /external/v1/files/:id), de modo que concederlo generaba una falsa sensación de seguridad. Las aplicaciones que ya lo tuvieran concedido seguirán mostrándolo hasta que el operador lo retire desde el panel, pero nunca habilitó ninguna operación real. Volverá al catálogo cuando se exponga el endpoint correspondiente.
Política de compatibilidad
La API externa es estable en v1
Todos los endpoints de la API externa viven bajo
/external/v1. Mientras tu integración consuma esa versión, los contratos publicados se mantienen. Las novedades se añaden de forma aditiva.Los SDK siguen versionado semántico
La rama actual de ambos SDK es la 4.x. Los cambios menores (segundo número) añaden funcionalidad de forma compatible; los de parche (tercer número) corrigen o ajustan sin romper. Fija una versión mínima conocida y revisa el changelog antes de subir de versión menor.
Las retiradas se anuncian con antelación
Los métodos que se van a eliminar se marcan primero como
@deprecatedy se mantienen durante una ventana de coexistencia antes de desaparecer en una versión mayor. Por ejemplo,generateSigningLinkquedó obsoleto en favor deforceSendSignature: hoy lanza siempre un error que remite al método nuevo y se conserva solo para que el código antiguo compile.Mantén SDK y backend alineados
Algunas funcionalidades nuevas del SDK dependen de endpoints que deben existir en tu instancia de UTILIA OS. Si llamas a un método nuevo contra un backend más antiguo, recibirás un error de endpoint no encontrado. Actualiza ambos lados de forma coordinada.