@utilia-os/sdk-js
v2.1.0
Published
SDK JavaScript/TypeScript para UTILIA OS External Integrations
Maintainers
Readme
@utilia-os/sdk-js
SDK JavaScript/TypeScript para integrar aplicaciones externas con UTILIA OS.
Instalación
npm install @utilia-os/sdk-jsConfiguración
import { UtiliaSDK } from '@utilia-os/sdk-js';
const sdk = new UtiliaSDK({
baseURL: 'https://os.utilia.ai/api',
apiKey: 'tu-api-key',
timeout: 30000, // opcional, default: 30000ms
debug: false, // opcional, habilita logs de debug
});Uso
Identificar Usuario
Antes de crear tickets, identifica al usuario en tu sistema:
const user = await sdk.users.identify({
externalId: 'user-123', // ID único en tu sistema (requerido)
email: '[email protected]', // opcional
name: 'Juan Perez', // opcional
avatarUrl: 'https://...', // opcional
metadata: { // opcional, datos adicionales
plan: 'premium',
company: 'Acme Inc',
},
});Crear Ticket
const ticket = await sdk.tickets.create({
user: {
externalId: 'user-123',
email: '[email protected]',
name: 'Juan Perez',
},
title: 'Problema con la facturación',
description: 'No puedo ver mis facturas del mes pasado...',
category: 'PROBLEMA', // CONSULTA | PROBLEMA | SUGERENCIA | OTRO
priority: 'MEDIA', // BAJA | MEDIA | ALTA | CRÍTICA
context: { // opcional
url: window.location.href,
appVersion: '1.2.3',
browserInfo: navigator.userAgent,
},
});
console.log(ticket.ticketKey); // APP-0001
console.log(ticket.id); // uuid del ticketListar Tickets
const result = await sdk.tickets.list('user-123', {
status: 'OPEN', // OPEN | IN_REVIEW | RESOLVED | CLOSED
page: 1,
limit: 20,
});
console.log(result.data); // Array de tickets
console.log(result.pagination.total); // Total de ticketsObtener Detalle de Ticket
const ticket = await sdk.tickets.get('ticket-uuid', 'user-123');
console.log(ticket.title);
console.log(ticket.messages); // Array de mensajesAgregar Mensaje
const message = await sdk.tickets.addMessage('ticket-uuid', 'user-123', {
content: 'Gracias por la respuesta, el problema ya fue resuelto.',
});Cerrar/Reabrir Ticket
// Cerrar
await sdk.tickets.close('ticket-uuid', 'user-123');
// Reabrir
await sdk.tickets.reopen('ticket-uuid', 'user-123');Obtener Mensajes No Leídos
const { count } = await sdk.tickets.getUnreadCount('user-123');
if (count > 0) {
console.log(`Tienes ${count} mensajes sin leer`);
}Actualizaciones en Tiempo Real (SSE)
Recibe notificaciones cuando un agente responde, cambia el estado, resuelve o cierra un ticket:
const stream = sdk.tickets.streamUpdates('user-123', {
onTicketUpdated: (event) => {
console.log(`Ticket ${event.ticketKey} actualizado: ${event.type}`);
// event.type: 'comment-added' | 'status-changed'
// Refrescar la UI del ticket
},
onError: (error) => {
console.error('Error en conexion SSE:', error);
},
});
// Para cerrar la conexion cuando ya no se necesite:
stream.close();Nota: En Node.js se requiere un polyfill como
eventsource(npm install eventsource).
Reportar Error
const result = await sdk.errors.report({
message: 'Error al procesar pago',
module: 'payment-processor',
severity: 'critical',
stack: error.stack,
endpoint: '/api/payments',
method: 'POST',
context: {
gatewayId: 'stripe',
orderId: 'order-456',
},
});
console.log(result.hash); // Hash único del error
console.log(result.deduplicated); // true si el error ya existíaListar Errores
const result = await sdk.errors.list({
severity: ['critical', 'high'],
resolved: false,
limit: 20,
});
console.log(result.errors);
console.log(result.pagination.total);Estadísticas de Errores
const stats = await sdk.errors.stats();
console.log(stats.total); // Total de errores
console.log(stats.unresolved); // Errores sin resolver
console.log(stats.bySeverity); // { critical: 5, high: 10, ... }
console.log(stats.byModule); // { auth: 3, payments: 7, ... }Configuración pública de la organización
Desde la versión 1.10.0, puedes leer los datos públicos de la organización (branding, valores fiscales por defecto, contacto) sin necesidad de permisos de administración. Útil para auto-rellenar formularios de presupuestos, conocer el impuesto local del tenant o mostrar el logo en un portal externo:
const settings = await sdk.organizationSettings.getPublicSettings();
console.log(settings.companyName); // "UTILIA"
console.log(settings.defaultCurrency); // "EUR"
console.log(settings.defaultTaxName); // "IVA"
console.log(settings.defaultTaxRate); // 21
console.log(settings.country); // "ES"
console.log(settings.timezone); // "Atlantic/Canary"
console.log(settings.logoUrl); // URL presignada frescaCampos incluidos: identidad corporativa, domicilio social y operativo, canales de contacto dinámicos, datos bancarios, configuración fiscal por defecto (impuesto, tasa, país, moneda), timezone, branding (colores, logos) y la configuración visual personalizada para PDFs de presupuestos.
Los metadatos internos (id, createdAt, updatedAt, updatedById) y los emails
legacy (emailSupport, emailHr, etc.) no se exponen en esta respuesta; los
emails legacy se transforman automáticamente a contactChannels por
retrocompatibilidad.
Breaking change en v2.0.0: se ha eliminado el campo
businessActivitydeOrganizationPublicSettings. La actividad económica ya no forma parte de la configuración general de la organización; se ha migrado al modeloFiscalConfig.activityDesc, disponible vía el endpointfinance/fiscal-config(se añadirá un método dedicado al SDK en próximas versiones). Si tu integración leíasettings.businessActivity, debes migrar a la nueva ubicación.
Comentarios y firmas de presupuestos
Desde la versión 2.1.0, el SDK cubre los dos dominios centrales del flujo de aprobación de presupuestos: la conversación entre equipo y cliente y la firma electrónica con magic link.
Comentarios (sdk.budgetComments)
Dos visibilidades: INTERNAL (solo equipo) y CLIENT (visible también en el
portal del cliente). Las menciones con mentionedUserIds solo tienen efecto
cuando la visibilidad es INTERNAL.
import { UtiliaSDK } from '@utilia-os/sdk-js';
const sdk = new UtiliaSDK({ baseURL: 'https://os.utilia.ai/api', apiKey: '...' });
// Listar comentarios dirigidos al cliente
const page = await sdk.budgetComments.list(budgetId, {
visibility: 'CLIENT',
page: 1,
limit: 20,
});
// Crear un comentario interno con menciones
const comment = await sdk.budgetComments.create(budgetId, {
body: 'Revisar el descuento del item 2 antes de enviar al cliente.',
visibility: 'INTERNAL',
mentionedUserIds: ['6d1a4c6b-3f8b-4a0e-a0d1-b29f1f1c21cb'],
});
// Editar el propio comentario
await sdk.budgetComments.update(budgetId, comment.id, {
body: 'Revisar descuento y condiciones de pago.',
});
// Crear varios comentarios en lote, tolerante a fallos
const result = await sdk.budgetComments.bulkCreate(
budgetId,
[
{ body: 'Primer comentario' },
{ body: 'Segundo comentario', clientOperationId: 'op-2' },
],
{ idempotencyKey: 'migration-2026-04-17' },
);
console.log(result.summary); // { total: 2, ok: 2, failed: 0 }Firmas y magic link (sdk.budgetSignatures)
El flujo completo cubre la emisión del magic link, el listado y revocación, la verificación de integridad de una firma ya registrada y la descarga del certificado legal.
// Emitir un magic link (idempotente por email)
const link = await sdk.budgetSignatures.generateSigningLink(budgetId, {
signerEmail: '[email protected]',
signerName: 'María García',
expiresInHours: 72,
sendEmail: true,
});
if (!link.reused && link.signingUrl) {
console.log('Enviar al cliente:', link.signingUrl);
}
// Listar los magic links vivos
const activeLinks = await sdk.budgetSignatures.listSigningLinks(budgetId);
// Revocar un link
await sdk.budgetSignatures.revokeSigningLink(budgetId, link.tokenId);
// Verificar la integridad de una firma ya registrada
const signatures = await sdk.budgetSignatures.list(budgetId);
const verification = await sdk.budgetSignatures.verify(
budgetId,
signatures[0].id,
);
if (!verification.valid) {
console.warn('El documento ha cambiado después de la firma.');
}
// Descargar el certificado legal
const pdf = await sdk.budgetSignatures.downloadCertificate(
budgetId,
signatures[0].id,
);Audit trail
Historial paginado de eventos del proceso de firma (emisión, visualización, descarga del PDF, aprobación, rechazo, expiración, revocación y fallos):
const events = await sdk.budgetSignatures.getAuditTrail(budgetId, {
tokenId: link.tokenId,
limit: 100,
});OAuth y Sign In
Desde la versión 1.6.0, el SDK incluye soporte nativo para OAuth 2.1 con PKCE. Esto permite implementar "Iniciar sesión con UTILIA" en tu aplicación:
const sdk = new UtiliaSDK({
baseURL: 'https://os.utilia.ai/api',
oauth: {
clientId: 'client_xxxxxxxxxx',
redirectUri: 'http://localhost:3000/callback',
scopes: ['openid', 'profile', 'email'],
},
});
// Redirigir al usuario para autorizar
const authUrl = await sdk.oauth.getAuthorizationUrl();
window.location.href = authUrl;
// Opcionalmente, solicitar un tema específico para la pantalla de consentimiento
const darkUrl = await sdk.oauth.getAuthorizationUrl({ theme: 'dark' });
window.location.href = darkUrl;
// En la página de callback
const tokens = await sdk.oauth.handleCallback(code);
const userInfo = await sdk.oauth.getUserInfo();Documentación completa: https://os.utilia.ai/dashboard/docs/integraciones-sdk/sdk-js-guia-oauth
Manejo de Errores
import { UtiliaSDK, UtiliaSDKError, ErrorCode } from '@utilia-os/sdk-js';
try {
await sdk.tickets.create({ ... });
} catch (error) {
if (error instanceof UtiliaSDKError) {
switch (error.code) {
case ErrorCode.UNAUTHORIZED:
console.error('API Key inválida');
break;
case ErrorCode.RATE_LIMITED:
console.error('Demasiadas peticiones, espera antes de reintentar');
break;
case ErrorCode.VALIDATION_ERROR:
console.error('Datos inválidos:', error.message);
break;
case ErrorCode.NOT_FOUND:
console.error('Recurso no encontrado');
break;
default:
console.error('Error:', error.message);
}
// Verificar si se puede reintentar
if (error.isRetryable()) {
// Implementar lógica de reintento
}
}
}Tipos
El SDK exporta todos los tipos TypeScript necesarios:
import type {
// Configuración
UtiliaSDKConfig,
// Tickets
CreateTicketInput,
TicketFilters,
AddMessageInput,
CreatedTicket,
TicketListItem,
TicketDetail,
TicketMessage,
// Usuarios
IdentifyUserInput,
ExternalUser,
// Errores del sistema
ReportErrorInput,
ReportedError,
SystemError,
ErrorFilters,
ErrorStats,
// Comunes
TicketStatus,
TicketCategory,
TicketPriority,
PaginatedResponse,
} from '@utilia-os/sdk-js';Requisitos
- Node.js >= 18.0.0
- TypeScript >= 4.7 (opcional, para tipos)
Licencia
MIT
