Esta página cubre dos cosas: las métricas de runtime que expone todo Bot
(parte de @botmesh/core) y los paquetes operativos opcionales (journal de
eventos, inbox, CRM, campañas…). Para enviar un mensaje a muchos chats o
iniciar un flujo desde un job, ve Difusiones
e Iniciar un flujo desde código.
Para sobrevivir a caídas sin perder ni duplicar mensajes (inbox/outbox durables,
reintentos, cola de muertos), ve Procesamiento durable.
Para las comprobaciones de salud y la lista de producción, ve
Despliegue y Pasar a producción.
bot.metrics() devuelve una foto en memoria de contadores que solo el motor
conoce. Es JSON plano y no se persiste nada: los contadores son monótonos
desde que se construyó el bot, o desde bot.resetMetrics() (que los pone a
cero y conserva el uptime).
messages.in cuenta los mensajes e interacciones de entrada aceptados
tras las listas de acceso y el dedupe. out / outFailed cuentan los
envíos que el provider aceptó o rechazó.
dropped.duplicate cuenta las reentregas que descarta el
dedupe de entrada, blocked los mensajes,
interacciones y llamadas que descartan la blacklist / whitelist (cada
entrega, porque el dedupe nunca registra lo bloqueado), y handoff los mensajes que no se enrutan porque el chat está
en manos de un humano. echo (solo existe con
echoFilter activo) cuenta los ecos de los
envíos del propio bot que descarta el filtro.
presenceSkipped.typing / markRead cuentan las señales de presencia
(ctx.typing(), ctx.recording(), el indicador automático de escritura,
ctx.markRead()) omitidas porque el transporte estaba desconectado o parado
(de mejor esfuerzo, ver Canales).
flows va por nombre de conversación/flujo. avgDurationMs es la duración
media de las conversaciones que ya terminaron (0 si ninguna terminó).
Un flujo que termina su función y un goto cuentan como completed;
c.end() cuenta como ended; un .guard() que falla (despedida o
redirect) cuenta como guarded; /cancel, /reset, un reemplazo o
bot.stop() cuentan como cancelled.
toPrometheus(snapshot, { prefix?, labels? }) renderiza una foto en el
formato de texto de Prometheus (0.0.4), sin dependencias. Sírvela con
PROMETHEUS_CONTENT_TYPE:
El prefijo por defecto es botmesh_ (prefix: 'botwa_' conserva los nombres de las versiones alpha). Familias: uptime_seconds,
conversations_active, conversations_started_total,
conversations_ended_total{outcome} (completed / ended / guarded /
cancelled / timed_out / retries_exceeded / error),
conversation_duration_avg_seconds,
flow_started_total{flow}, flow_ended_total{flow,outcome},
flow_duration_avg_seconds{flow}, messages_in_total,
messages_out_total{result} (sent / failed),
inbound_dropped_total{reason} (duplicate / blocked / handoff, más echo con echoFilter),
presence_skipped_total{kind} (typing / mark_read),
triggers_total y errors_total. Un prefijo o un nombre de label inválido
lanza TypeError.
conversation:started se emite cada vez que se entra en un flujo o una
bot.conversation: un trigger que coincide, bot.trigger(), una
reanudación dormida o un c.goto().
conversation:ended añade outcome ('completed' | 'ended' | 'guarded' | 'cancelled' | 'timedOut' | 'retriesExceeded' | 'error'), completed (atajo de
outcome === 'completed'), durationMs y, si terminó con un goto, su
destino en goto (le sigue un nuevo started).
id es único por instancia de conversación, así que empareja cada ended
con su started. key es la clave del runtime (chatId, o
chatId:userId para un participante de grupo). Cuando una conversación se
reemplaza, su ended (outcome: 'cancelled') se emite antes del started
de la nueva.
Un listener que lanza se reporta en bot.on('error'); nunca rompe la
conversación.
@botmesh/events está publicado en npm (pnpm add @botmesh/events).
Paquetes de producto — sin publicarLos paquetes de producto (@botmesh/inbox,
@botmesh/crm, @botmesh/hub, @botmesh/campaigns y @botmesh/deployments)
son reales y persistentes: cada uno tiene un contrato de almacenamiento,
una implementación de referencia en RAM y un adaptador Kv* sobre cualquier
DurableKv del core (SQLite, PostgreSQL, Redis, Redis Cluster o archivo), con
una suite de conformidad compartida y suites opt-in contra servidores reales.
Solo están en el repositorio y su API todavía puede cambiar.
Los paquetes operativos son agnósticos de canal y están aislados por tenant.
Usa MemoryEventJournal en tests. FileEventJournal.open('./data/events.json') persiste el journal de una sola instancia con snapshots atómicos, idempotencia, leases, reintentos y replay. Para varios procesos en un mismo host, SharedFileEventJournal.open(path) comparte un archivo bajo un mutex entre procesos; entre hosts, implementa LeasedEventJournal sobre PostgreSQL, Redis o un broker. Los journals de archivo guardan JSON: un sobre con un BigInt o una referencia circular se rechaza con un TypeError (no se guarda nada), y un Date se lee de vuelta como su cadena ISO. Un registro que no se leería de vuelta como válido (sin partitionKey, con un occurredAtNaN o Date, o con un toJSON que cambia el id del evento) se rechaza igual, en todos los journals, así que nada de lo que un journal acepta le impide volver a abrirse. Cada operación de SharedFileEventJournal toma el mutex y vuelve a leer el archivo (milisegundos en Linux, decenas de milisegundos en Windows), así que resérvalo para volúmenes modestos.EventWorker reclama eventos, invoca el handler, confirma el trabajo exitoso y mueve a la cola de mensajes muertos (DLQ) los que agotan sus reintentos.Cada claim entrega un fencing token (record.fencingToken, estrictamente creciente por evento y nunca reutilizado, ni siquiera tras un replay). El worker lo devuelve en ack/fail y el journal rechaza un token superado con StaleLeaseError: un worker cuyo lease expiró mientras su handler seguía corriendo no puede completar, reintentar ni mandar a la DLQ un evento que otro worker ya reclamó. runBatch() los cuenta como stale ({ processed, failed, stale }) y se salta un registro cuyo lease ya venció antes de empezar su handler; lo devuelve con journal.release(eventId, { now, fencingToken }), así que saltárselo no gasta un intento. Los handlers siguen ejecutándose al menos una vez, así que hazlos idempotentes (para eso está el idempotencyKey del envelope). new EventWorker(journal, { leaseMs, clock }) fija la duración del lease y la fuente de tiempo. Un adaptador de EventJournal sobre PostgreSQL o Redis debe comprobar el token (en ack, fail y release) en la misma actualización atómica que cambia el estado.El mismo journal puede servir además como backend de durabilidad de un bot (eventsDurability({ journal }) de @botmesh/events/durability, ver Procesamiento durable de mensajes). Los registros entrantes del bot se escriben en su propio canal ('botwa:durable'). list() y replay() los muestran, pero un EventWorker nunca los reclama: un claim sin channel solo toma registros que no tienen canal. El propio bot los procesa.
@botmesh/inbox incorpora asignación con concurrencia optimista (compare-and-set sobre version), handoff, notas y unread. Proyecta los StoredMessage del ledger con inbox.projectMessage(message) para reconstruir conversaciones entre canales; attachInbox(bot, inbox) lo hace para un bot en marcha, tanto con los mensajes entrantes como con los envíos aceptados, sin retrasar ninguno; un fallo de la proyección va al evento 'error' del bot ({ error }) o, si nadie lo escucha, al logger del bot. Sin la opción tenantId, las conversaciones van al tenant del propio bot (BotConfig.tenantId). Persiste con new Inbox(new KvInboxStore(kv)). Las notas se paginan con listNotes(tenant, conversación, { limit, after }) (after = el id de la última nota de la página anterior), e inbox.erase(tenant, conversación) borra una conversación con sus notas (derecho de supresión; borra también los mensajes en el store del bot).@botmesh/crm guarda canales de contacto, tags, atributos y consentimiento de marketing, con segmentos por reglas (tagsAll, atributos comparados de forma estricta, consentimiento). ContactDirectory es async sobre MemoryContactStore o KvContactStore; setConsent reintenta a los escritores concurrentes en vez de pisarlos, y erase(tenant, id) borra un contacto (derecho de supresión). Segmenta siempre las campañas con datos aislados por tenant y con consentimiento.
@botmesh/campaigns descarta opt-outs, segmentos que no coinciden y destinatarios duplicados. El store reserva un hueco de envío por (tenant, campaña, destinatario) con TTL; markSent cuenta el envío y markFailed libera el hueco. sendCampaign(bot, planner, …) envía con bot.broadcast() y asienta cada delivery con el resultado real (aceptado, fallido, descartado por un hook o cancelado), emparejado por el chat de destino aunque el proveedor responda con un id canónico; si el propio broadcast rechaza, se liberan todos los huecos reservados.@botmesh/hub valida manifiestos de plugins, compatibilidad con el runtime y permisos concedidos antes de habilitar un plugin. Los manifiestos se tratan como JSON no confiable (se comprueba el tipo de cada campo y se rechazan los permisos desconocidos) y el enable vuelve a comprobar los permisos de forma atómica, así que una reinstalación concurrente con más permisos nunca queda habilitada sin ellos. Las instalaciones y el enable/disable se persisten por tenant; por sí solo no ejecuta ni aísla código no confiable.@botmesh/deployments registra versiones inmutables y un despliegue activo por tenant/bot, con reservas para gates en ejecución y auditoría de gate runs. list(tenant, bot, { limit, after }) pagina las versiones, prune(tenant, bot, keep) borra las anteriores a las keep más nuevas (nunca la activa) y new DeploymentRegistry(store, { retainVersions }) poda tras cada publicación, así que la lista de versiones no crece sin límite. Conéctalo a tu sistema de despliegue para hacer rollouts graduales y rollback físico.Los cinco comparten el mismo modelo de almacenamiento: Memory…Store (referencia), Kv…Store sobre cualquier DurableKv, una suite de conformidad en test-utils/ y aislamiento de tenant impuesto por el nombre de la tabla en el backend (@botmesh/tenant-kv). Toda escritura se valida antes de llegar al backend (un campo mal formado falla con un TypeError que lo nombra), así que una escritura mala nunca deja ilegibles los listados de un tenant.
bot.metrics() cuenta; las trazas explican. Con BotConfig.telemetry el bot emite un árbol de spans por mensaje (entrada → dispatch → handler / turno de conversación → llamadas al modelo y herramientas de IA → envío), con los ids con hash por defecto y sin contenido de mensajes. @botmesh/otel manda esos spans a OpenTelemetry (Jaeger, Grafana Tempo, cualquier backend OTLP) y publica bot.metrics() como métricas de OpenTelemetry (bot.plug(otelMetricsPlugin())). Configuración, catálogo de spans, política de datos personales y un ejemplo con Jaeger / Grafana Tempo: Trazas.
{"docsConfig":{"theme":"mint","$schema":"https://mintlify.com/docs.json","name":"botmesh","description":"A transport-agnostic TypeScript framework for messaging bots: WhatsApp (Baileys or the Cloud API), Telegram, Discord and more behind one IProvider.","colors":{"primary":"#3451B2","light":"#A8B1FF","dark":"#3451B2"},"logo":{"light":"https://mintcdn.com/afaya/32LyKbpS8BIF3Yl-/logo/light.svg?fit=max\u0026auto=format\u0026n=32LyKbpS8BIF3Yl-\u0026q=85\u0026s=5a9c4db10ba52bbd645a009cc009e31e","dark":"https://mintcdn.com/afaya/32LyKbpS8BIF3Yl-/logo/dark.svg?fit=max\u0026auto=format\u0026n=32LyKbpS8BIF3Yl-\u0026q=85\u0026s=be155346190b752c815b6d06f1134702"},"favicon":"https://mintcdn.com/afaya/32LyKbpS8BIF3Yl-/favicon.svg?fit=max\u0026auto=format\u0026n=32LyKbpS8BIF3Yl-\u0026q=85\u0026s=abe4b13c733adb336381bebc0220d3e5","navbar":{"links":[{"href":"https://www.npmjs.com/package/@botmesh/core","label":"npm"}],"primary":{"type":"github","href":"https://github.com/andresayac/botmesh"}},"footer":{"socials":{"github":"https://github.com/andresayac/botmesh"},"links":[{"header":"Project","items":[{"label":"GitHub","href":"https://github.com/andresayac/botmesh"},{"label":"npm","href":"https://www.npmjs.com/package/@botmesh/core"},{"label":"MIT License","href":"https://github.com/andresayac/botmesh/blob/main/LICENSE"},{"label":"Changelog","href":"https://github.com/andresayac/botmesh/blob/main/CHANGELOG.md"}]}]},"search":{"prompt":"Search the botmesh docs…"},"seo":{"indexing":"navigable"},"icons":{"library":"lucide"},"contextual":{"options":["copy","view","chatgpt","claude","perplexity","mcp","cursor","vscode"],"display":"header"},"styling":{"latex":true},"navigation":{"languages":[{"language":"en","default":true,"href":""},{"language":"es","footer":{"socials":{"github":"https://github.com/andresayac/botmesh"},"links":[{"header":"Proyecto","items":[{"label":"GitHub","href":"https://github.com/andresayac/botmesh"},{"label":"npm","href":"https://www.npmjs.com/package/@botmesh/core"},{"label":"Licencia MIT","href":"https://github.com/andresayac/botmesh/blob/main/LICENSE"},{"label":"Changelog","href":"https://github.com/andresayac/botmesh/blob/main/CHANGELOG.md"}]}]},"href":"es"}]}},"deployment":{"deploymentMetadataValue":{"subdomain":"afaya","hasAudioNarration":false,"actualSubdomain":"afaya","customDomains":["botmesh.afaya.com.co"],"gitSource":{"type":"github","owner":"andresayac","repo":"botmesh-docs","deployBranch":"main","contentDirectory":"","isPrivate":false,"mountPath":"","label":"","_id":"6ae0b3e80b849c49dba39507"},"chroma":{"latestBuiltDeploymentHistoryId":"6ae0b3f8dc8e261a8bb682ee","collectionId":"af19fec4-32c9-4ad1-b8a5-5db01ce875d1","liveDeploymentHistoryId":"6ae0b3f8dc8e261a8bb682ee","schemaVersion":"v3"},"entitlements":{"AI_CHAT":{"status":"ENABLED"},"WEB_MCP":{"status":"ENABLED"},"NARRATION":{"status":"ENABLED"}},"buildId":"6ae0b3fe0b849c49dba39747","clientVersion":"0.0.3971","searchFilterCounts":[{"language":"en","tag":"API reference","count":541},{"language":"en","tag":"@botmesh/core","count":373},{"language":"en","tag":"@botmesh/core/testing","count":93},{"language":"en","tag":"@botmesh/core/spi","count":75},{"language":"en","tag":"Guide","count":53},{"language":"es","tag":"Guía","count":53},{"language":"en","tag":"Golden transcripts","count":41},{"language":"en","tag":"Durability","count":35},{"language":"en","tag":"Flows","count":34},{"language":"en","tag":"Messages \u0026 context","count":32},{"language":"en","tag":"Forms","count":30},{"language":"es","tag":"Ejemplos","count":29},{"language":"es","tag":"Características","count":25},{"language":"es","tag":"Avanzado","count":19},{"language":"es","tag":"Intermedio","count":9},{"language":"es","tag":"Principiante","count":8},{"language":"es","tag":"Desarrollo y tests","count":8},{"language":"es","tag":"IA","count":6},{"language":"es","tag":"Primeros pasos","count":6},{"language":"es","tag":"Resumen","count":1}]},"assistantConfig":{"enableAskAiButton":false,"enableStarterQuestions":false,"enableContextualStarterQuestions":false,"enableAskAiSearchBar":true},"basePath":"","hasChatPermissions":true,"hasWebMcpPermissions":true,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-v7-file-tree","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"pause-universal-search-reconcile","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"per-site-search-delta","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-private-page-search","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-nav-generations","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"byok-sep-25","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"vite-origin","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-agent-model-picker","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-updated-9-15","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-sso-scim","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-signup","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-enabled","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"isAtlas":false,"googleFontsInlined":false},"customCss":[],"page":{"pagePath":"es/guide/operations","slug":"/es/guide/operations","pageMetadata":{"title":"Operación: métricas, eventos, inbox y entregas","sidebarTitle":"Operación","description":null,"href":"/es/guide/operations"},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx,jsxs:_jsxs}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){const _components={a:`a`,code:`code`,li:`li`,p:`p`,pre:`pre`,span:`span`,strong:`strong`,ul:`ul`,..._provideComponents(),...props.components},{CodeBlock,Heading,Warning}=_components;if(!CodeBlock)_missingMdxReference(`CodeBlock`,true);if(!Heading)_missingMdxReference(`Heading`,true);if(!Warning)_missingMdxReference(`Warning`,true);return _jsxs(_Fragment,{children:[_jsxs(_components.p,{children:[`Esta página cubre dos cosas: las métricas de runtime que expone todo `,_jsx(_components.code,{children:`Bot`}),`\n(parte de `,_jsx(_components.code,{children:`@botmesh/core`}),`) y los paquetes operativos opcionales (journal de\neventos, inbox, CRM, campañas…). Para enviar un mensaje a muchos chats o\niniciar un flujo desde un job, ve `,_jsx(_components.a,{href:`/es/guide/messages#difusiones-broadcast`,children:`Difusiones`}),`\ne `,_jsx(_components.a,{href:`/es/guide/messages#iniciar-un-flujo-desde-c%C3%B3digo`,children:`Iniciar un flujo desde código`}),`.\nPara sobrevivir a caídas sin perder ni duplicar mensajes (inbox/outbox durables,\nreintentos, cola de muertos), ve `,_jsx(_components.a,{href:`/es/guide/durability`,children:`Procesamiento durable`}),`.\nPara las comprobaciones de salud y la lista de producción, ve\n`,_jsx(_components.a,{href:`/es/guide/deploy#comprobaciones-de-salud`,children:`Despliegue`}),` y `,_jsx(_components.a,{href:`/es/guide/production`,children:`Pasar a producción`}),`.`]}),`\n`,_jsx(Heading,{level:`2`,id:`métricas-de-runtime`,children:`Métricas de runtime`}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`bot.metrics()`}),` devuelve una foto en memoria de contadores que solo el motor\nconoce. Es JSON plano y no se persiste nada: los contadores son monótonos\ndesde que se construyó el bot, o desde `,_jsx(_components.code,{children:`bot.resetMetrics()`}),` (que los pone a\ncero y conserva el uptime).`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`11`,language:`typescript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`typescript`,children:_jsxs(_components.code,{language:`typescript`,numberOfLines:`11`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`const`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#4FC1FF`},children:` m`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` =`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:` bot`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`.`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:`metrics`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`()`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`// {`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`// startedAt, uptimeMs, uptime,`}),_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:` // uptime: '3d 4h', '5m 12s'...`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`// conversations: { started, completed, ended, guarded, cancelled, timedOut, retriesExceeded, errored, avgDurationMs, active },`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`// flows: { registro: { started, completed, ended, guarded, cancelled, timedOut, retriesExceeded, errored, avgDurationMs } },`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`// messages: { in, out, outFailed },`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`// dropped: { duplicate, blocked, handoff, echo? },`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`// presenceSkipped: { typing, markRead },`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`// triggered,`}),_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:` // llamadas a bot.trigger() que iniciaron una conversación`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`// errors,`}),_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:` // errores reportados en bot.on('error')`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`// }`})}),`\n`]})})}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`messages.in`}),` cuenta los mensajes e interacciones de entrada aceptados\ntras las listas de acceso y el dedupe. `,_jsx(_components.code,{children:`out`}),` / `,_jsx(_components.code,{children:`outFailed`}),` cuentan los\nenvíos que el provider aceptó o rechazó.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`dropped.duplicate`}),` cuenta las reentregas que descarta el\n`,_jsx(_components.a,{href:`/es/guide/concepts#dedupe-de-entrada`,children:`dedupe de entrada`}),`, `,_jsx(_components.code,{children:`blocked`}),` los mensajes,\ninteracciones y llamadas que descartan la blacklist / whitelist (cada\nentrega, porque el dedupe nunca registra lo bloqueado), y `,_jsx(_components.code,{children:`handoff`}),` los mensajes que no se enrutan porque el chat está\n`,_jsx(_components.a,{href:`/es/guide/handoff`,children:`en manos de un humano`}),`. `,_jsx(_components.code,{children:`echo`}),` (solo existe con\n`,_jsx(_components.a,{href:`/es/guide/concepts#filtro-de-ecos`,children:_jsx(_components.code,{children:`echoFilter`})}),` activo) cuenta los ecos de los\nenvíos del propio bot que descarta el filtro.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`presenceSkipped.typing`}),` / `,_jsx(_components.code,{children:`markRead`}),` cuentan las señales de presencia\n(`,_jsx(_components.code,{children:`ctx.typing()`}),`, `,_jsx(_components.code,{children:`ctx.recording()`}),`, el indicador automático de escritura,\n`,_jsx(_components.code,{children:`ctx.markRead()`}),`) omitidas porque el transporte estaba desconectado o parado\n(de mejor esfuerzo, ver `,_jsx(_components.a,{href:`/es/guide/channels#contrato-com%C3%BAn-de-los-canales`,children:`Canales`}),`).`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`flows`}),` va por nombre de conversación/flujo. `,_jsx(_components.code,{children:`avgDurationMs`}),` es la duración\nmedia de las conversaciones que ya terminaron (`,_jsx(_components.code,{children:`0`}),` si ninguna terminó).`]}),`\n`,_jsxs(_components.li,{children:[`Un flujo que termina su función y un `,_jsx(_components.code,{children:`goto`}),` cuentan como `,_jsx(_components.code,{children:`completed`}),`;\n`,_jsx(_components.code,{children:`c.end()`}),` cuenta como `,_jsx(_components.code,{children:`ended`}),`; un `,_jsx(_components.code,{children:`.guard()`}),` que falla (despedida o\n`,_jsx(_components.code,{children:`redirect`}),`) cuenta como `,_jsx(_components.code,{children:`guarded`}),`; `,_jsx(_components.code,{children:`/cancel`}),`, `,_jsx(_components.code,{children:`/reset`}),`, un reemplazo o\n`,_jsx(_components.code,{children:`bot.stop()`}),` cuentan como `,_jsx(_components.code,{children:`cancelled`}),`.`]}),`\n`]}),`\n`,_jsx(Heading,{level:`3`,id:`prometheus`,children:`Prometheus`}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`toPrometheus(snapshot, { prefix?, labels? })`}),` renderiza una foto en el\nformato de texto de Prometheus (0.0.4), sin dependencias. Sírvela con\n`,_jsx(_components.code,{children:`PROMETHEUS_CONTENT_TYPE`}),`:`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`7`,language:`typescript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`typescript`,children:_jsxs(_components.code,{language:`typescript`,numberOfLines:`7`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#C586C0`},children:`import`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` { `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`createServer`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` } `}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#C586C0`},children:`from`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` 'node:http'`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#C586C0`},children:`import`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` { `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`toPrometheus`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`PROMETHEUS_CONTENT_TYPE`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` } `}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#C586C0`},children:`from`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` '@botmesh/core'`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:`createServer`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`((`}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#9CDCFE`},children:`_req`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#9CDCFE`},children:`res`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`) `}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`=>`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` {`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:` res`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`.`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:`setHeader`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`(`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`'Content-Type'`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#4FC1FF`},children:`PROMETHEUS_CONTENT_TYPE`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`)`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:` res`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`.`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:`end`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`(`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:`toPrometheus`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`(`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`bot`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`.`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:`metrics`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`(), { `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`labels:`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` { `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`bot:`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` 'ventas'`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` } }))`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`}).`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:`listen`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`(`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#B5CEA8`},children:`9464`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`)`})]}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[`El prefijo por defecto es `,_jsx(_components.code,{children:`botmesh_`}),` (`,_jsx(_components.code,{children:`prefix: 'botwa_'`}),` conserva los nombres de las versiones alpha). Familias: `,_jsx(_components.code,{children:`uptime_seconds`}),`,\n`,_jsx(_components.code,{children:`conversations_active`}),`, `,_jsx(_components.code,{children:`conversations_started_total`}),`,\n`,_jsx(_components.code,{children:`conversations_ended_total{outcome}`}),` (`,_jsx(_components.code,{children:`completed`}),` / `,_jsx(_components.code,{children:`ended`}),` / `,_jsx(_components.code,{children:`guarded`}),` /\n`,_jsx(_components.code,{children:`cancelled`}),` / `,_jsx(_components.code,{children:`timed_out`}),` / `,_jsx(_components.code,{children:`retries_exceeded`}),` / `,_jsx(_components.code,{children:`error`}),`),\n`,_jsx(_components.code,{children:`conversation_duration_avg_seconds`}),`,\n`,_jsx(_components.code,{children:`flow_started_total{flow}`}),`, `,_jsx(_components.code,{children:`flow_ended_total{flow,outcome}`}),`,\n`,_jsx(_components.code,{children:`flow_duration_avg_seconds{flow}`}),`, `,_jsx(_components.code,{children:`messages_in_total`}),`,\n`,_jsx(_components.code,{children:`messages_out_total{result}`}),` (`,_jsx(_components.code,{children:`sent`}),` / `,_jsx(_components.code,{children:`failed`}),`),\n`,_jsx(_components.code,{children:`inbound_dropped_total{reason}`}),` (`,_jsx(_components.code,{children:`duplicate`}),` / `,_jsx(_components.code,{children:`blocked`}),` / `,_jsx(_components.code,{children:`handoff`}),`, más `,_jsx(_components.code,{children:`echo`}),` con `,_jsx(_components.code,{children:`echoFilter`}),`),\n`,_jsx(_components.code,{children:`presence_skipped_total{kind}`}),` (`,_jsx(_components.code,{children:`typing`}),` / `,_jsx(_components.code,{children:`mark_read`}),`),\n`,_jsx(_components.code,{children:`triggers_total`}),` y `,_jsx(_components.code,{children:`errors_total`}),`. Un prefijo o un nombre de label inválido\nlanza `,_jsx(_components.code,{children:`TypeError`}),`.`]}),`\n`,_jsx(Heading,{level:`3`,id:`eventos-de-conversación`,children:`Eventos de conversación`}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`2`,language:`typescript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`typescript`,children:_jsxs(_components.code,{language:`typescript`,numberOfLines:`2`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`bot`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`.`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:`on`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`(`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`'conversation:started'`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, ({ `}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#9CDCFE`},children:`id`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#9CDCFE`},children:`name`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#9CDCFE`},children:`chatId`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#9CDCFE`},children:`userId`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#9CDCFE`},children:`key`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` }) `}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`=>`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` { `}),_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`/* ... */`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` })`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`bot`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`.`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:`on`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`(`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`'conversation:ended'`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, ({ `}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#9CDCFE`},children:`id`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#9CDCFE`},children:`name`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#9CDCFE`},children:`chatId`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#9CDCFE`},children:`outcome`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#9CDCFE`},children:`completed`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#9CDCFE`},children:`durationMs`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#9CDCFE`},children:`goto`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` }) `}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`=>`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` { `}),_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`/* ... */`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` })`})]}),`\n`]})})}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`conversation:started`}),` se emite cada vez que se entra en un flujo o una\n`,_jsx(_components.code,{children:`bot.conversation`}),`: un trigger que coincide, `,_jsx(_components.code,{children:`bot.trigger()`}),`, una\nreanudación dormida o un `,_jsx(_components.code,{children:`c.goto()`}),`.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`conversation:ended`}),` añade `,_jsx(_components.code,{children:`outcome`}),` (`,_jsx(_components.code,{children:`'completed' | 'ended' | 'guarded' | 'cancelled' | 'timedOut' | 'retriesExceeded' | 'error'`}),`), `,_jsx(_components.code,{children:`completed`}),` (atajo de\n`,_jsx(_components.code,{children:`outcome === 'completed'`}),`), `,_jsx(_components.code,{children:`durationMs`}),` y, si terminó con un `,_jsx(_components.code,{children:`goto`}),`, su\ndestino en `,_jsx(_components.code,{children:`goto`}),` (le sigue un nuevo `,_jsx(_components.code,{children:`started`}),`).`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`id`}),` es único por instancia de conversación, así que empareja cada `,_jsx(_components.code,{children:`ended`}),`\ncon su `,_jsx(_components.code,{children:`started`}),`. `,_jsx(_components.code,{children:`key`}),` es la clave del runtime (`,_jsx(_components.code,{children:`chatId`}),`, o\n`,_jsx(_components.code,{children:`chatId:userId`}),` para un participante de grupo). Cuando una conversación se\nreemplaza, su `,_jsx(_components.code,{children:`ended`}),` (`,_jsx(_components.code,{children:`outcome: 'cancelled'`}),`) se emite antes del `,_jsx(_components.code,{children:`started`}),`\nde la nueva.`]}),`\n`,_jsxs(_components.li,{children:[`Un listener que lanza se reporta en `,_jsx(_components.code,{children:`bot.on('error')`}),`; nunca rompe la\nconversación.`]}),`\n`]}),`\n`,_jsx(Heading,{level:`2`,id:`paquetes-operativos`,children:`Paquetes operativos`}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`@botmesh/events`}),` está publicado en npm (`,_jsx(_components.code,{children:`pnpm add @botmesh/events`}),`).`]}),`\n`,_jsxs(Warning,{children:[_jsx(_components.p,{children:_jsx(_components.strong,{children:`Paquetes de producto — sin publicar`})}),_jsxs(_components.p,{children:[`Los paquetes de producto (`,_jsx(_components.code,{children:`@botmesh/inbox`}),`,\n`,_jsx(_components.code,{children:`@botmesh/crm`}),`, `,_jsx(_components.code,{children:`@botmesh/hub`}),`, `,_jsx(_components.code,{children:`@botmesh/campaigns`}),` y `,_jsx(_components.code,{children:`@botmesh/deployments`}),`)\nson reales y `,_jsx(_components.strong,{children:`persistentes`}),`: cada uno tiene un contrato de almacenamiento,\nuna implementación de referencia en RAM y un adaptador `,_jsx(_components.code,{children:`Kv*`}),` sobre cualquier\n`,_jsx(_components.code,{children:`DurableKv`}),` del core (SQLite, PostgreSQL, Redis, Redis Cluster o archivo), con\nuna suite de conformidad compartida y suites opt-in contra servidores reales.\nSolo están en el repositorio y su API todavía puede cambiar.`]})]}),`\n`,_jsx(_components.p,{children:`Los paquetes operativos son agnósticos de canal y están aislados por tenant.`}),`\n`,_jsx(Heading,{level:`3`,id:`procesamiento-de-eventos`,children:`Procesamiento de eventos`}),`\n`,_jsxs(_components.p,{children:[`Usa `,_jsx(_components.code,{children:`MemoryEventJournal`}),` en tests. `,_jsx(_components.code,{children:`FileEventJournal.open('./data/events.json')`}),` persiste el journal de una sola instancia con snapshots atómicos, idempotencia, leases, reintentos y replay. Para varios procesos en un mismo host, `,_jsx(_components.code,{children:`SharedFileEventJournal.open(path)`}),` comparte un archivo bajo un mutex entre procesos; entre hosts, implementa `,_jsx(_components.code,{children:`LeasedEventJournal`}),` sobre PostgreSQL, Redis o un broker. Los journals de archivo guardan JSON: un sobre con un `,_jsx(_components.code,{children:`BigInt`}),` o una referencia circular se rechaza con un `,_jsx(_components.code,{children:`TypeError`}),` (no se guarda nada), y un `,_jsx(_components.code,{children:`Date`}),` se lee de vuelta como su cadena ISO. Un registro que no se leería de vuelta como válido (sin `,_jsx(_components.code,{children:`partitionKey`}),`, con un `,_jsx(_components.code,{children:`occurredAt`}),` `,_jsx(_components.code,{children:`NaN`}),` o `,_jsx(_components.code,{children:`Date`}),`, o con un `,_jsx(_components.code,{children:`toJSON`}),` que cambia el id del evento) se rechaza igual, en todos los journals, así que nada de lo que un journal acepta le impide volver a abrirse. Cada operación de `,_jsx(_components.code,{children:`SharedFileEventJournal`}),` toma el mutex y vuelve a leer el archivo (milisegundos en Linux, decenas de milisegundos en Windows), así que resérvalo para volúmenes modestos.`]}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`EventWorker`}),` reclama eventos, invoca el handler, confirma el trabajo exitoso y mueve a la cola de mensajes muertos (DLQ) los que agotan sus reintentos.`]}),`\n`,_jsxs(_components.p,{children:[`Cada claim entrega un `,_jsx(_components.strong,{children:`fencing token`}),` (`,_jsx(_components.code,{children:`record.fencingToken`}),`, estrictamente creciente por evento y nunca reutilizado, ni siquiera tras un `,_jsx(_components.code,{children:`replay`}),`). El worker lo devuelve en `,_jsx(_components.code,{children:`ack`}),`/`,_jsx(_components.code,{children:`fail`}),` y el journal rechaza un token superado con `,_jsx(_components.code,{children:`StaleLeaseError`}),`: un worker cuyo lease expiró mientras su handler seguía corriendo no puede completar, reintentar ni mandar a la DLQ un evento que otro worker ya reclamó. `,_jsx(_components.code,{children:`runBatch()`}),` los cuenta como `,_jsx(_components.code,{children:`stale`}),` (`,_jsx(_components.code,{children:`{ processed, failed, stale }`}),`) y se salta un registro cuyo lease ya venció antes de empezar su handler; lo devuelve con `,_jsx(_components.code,{children:`journal.release(eventId, { now, fencingToken })`}),`, así que saltárselo no gasta un intento. Los handlers siguen ejecutándose al menos una vez, así que hazlos idempotentes (para eso está el `,_jsx(_components.code,{children:`idempotencyKey`}),` del envelope). `,_jsx(_components.code,{children:`new EventWorker(journal, { leaseMs, clock })`}),` fija la duración del lease y la fuente de tiempo. Un adaptador de `,_jsx(_components.code,{children:`EventJournal`}),` sobre PostgreSQL o Redis debe comprobar el token (en `,_jsx(_components.code,{children:`ack`}),`, `,_jsx(_components.code,{children:`fail`}),` y `,_jsx(_components.code,{children:`release`}),`) en la misma actualización atómica que cambia el estado.`]}),`\n`,_jsxs(_components.p,{children:[`El mismo journal puede servir además como backend de durabilidad de un bot (`,_jsx(_components.code,{children:`eventsDurability({ journal })`}),` de `,_jsx(_components.code,{children:`@botmesh/events/durability`}),`, ver `,_jsx(_components.a,{href:`/es/guide/durability`,children:`Procesamiento durable de mensajes`}),`). Los registros entrantes del bot se escriben en su propio `,_jsx(_components.strong,{children:`canal`}),` (`,_jsx(_components.code,{children:`'botwa:durable'`}),`). `,_jsx(_components.code,{children:`list()`}),` y `,_jsx(_components.code,{children:`replay()`}),` los muestran, pero un `,_jsx(_components.code,{children:`EventWorker`}),` nunca los reclama: un claim sin `,_jsx(_components.code,{children:`channel`}),` solo toma registros que no tienen canal. El propio bot los procesa.`]}),`\n`,_jsx(Heading,{level:`3`,id:`inbox-y-crm`,children:`Inbox y CRM`}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`@botmesh/inbox`}),` incorpora asignación con concurrencia optimista (compare-and-set sobre `,_jsx(_components.code,{children:`version`}),`), handoff, notas y unread. Proyecta los `,_jsx(_components.code,{children:`StoredMessage`}),` del ledger con `,_jsx(_components.code,{children:`inbox.projectMessage(message)`}),` para reconstruir conversaciones entre canales; `,_jsx(_components.code,{children:`attachInbox(bot, inbox)`}),` lo hace para un bot en marcha, tanto con los mensajes entrantes como con los envíos aceptados, sin retrasar ninguno; un fallo de la proyección va al evento `,_jsx(_components.code,{children:`'error'`}),` del bot (`,_jsx(_components.code,{children:`{ error }`}),`) o, si nadie lo escucha, al logger del bot. Sin la opción `,_jsx(_components.code,{children:`tenantId`}),`, las conversaciones van al tenant del propio bot (`,_jsx(_components.code,{children:`BotConfig.tenantId`}),`). Persiste con `,_jsx(_components.code,{children:`new Inbox(new KvInboxStore(kv))`}),`. Las notas se paginan con `,_jsx(_components.code,{children:`listNotes(tenant, conversación, { limit, after })`}),` (`,_jsx(_components.code,{children:`after`}),` = el id de la última nota de la página anterior), e `,_jsx(_components.code,{children:`inbox.erase(tenant, conversación)`}),` borra una conversación con sus notas (derecho de supresión; borra también los mensajes en el store del bot).`]}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`@botmesh/crm`}),` guarda canales de contacto, tags, atributos y consentimiento de marketing, con segmentos por reglas (`,_jsx(_components.code,{children:`tagsAll`}),`, atributos comparados de forma estricta, consentimiento). `,_jsx(_components.code,{children:`ContactDirectory`}),` es async sobre `,_jsx(_components.code,{children:`MemoryContactStore`}),` o `,_jsx(_components.code,{children:`KvContactStore`}),`; `,_jsx(_components.code,{children:`setConsent`}),` reintenta a los escritores concurrentes en vez de pisarlos, y `,_jsx(_components.code,{children:`erase(tenant, id)`}),` borra un contacto (derecho de supresión). Segmenta siempre las campañas con datos aislados por tenant y con consentimiento.`]}),`\n`,_jsx(Heading,{level:`3`,id:`campañas-plugins-y-despliegues`,children:`Campañas, plugins y despliegues`}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`@botmesh/campaigns`}),` descarta opt-outs, segmentos que no coinciden y destinatarios duplicados. El store reserva un hueco de envío por `,_jsx(_components.code,{children:`(tenant, campaña, destinatario)`}),` con TTL; `,_jsx(_components.code,{children:`markSent`}),` cuenta el envío y `,_jsx(_components.code,{children:`markFailed`}),` libera el hueco. `,_jsx(_components.code,{children:`sendCampaign(bot, planner, …)`}),` envía con `,_jsx(_components.code,{children:`bot.broadcast()`}),` y asienta cada delivery con el resultado real (aceptado, fallido, descartado por un hook o cancelado), emparejado por el chat de destino aunque el proveedor responda con un id canónico; si el propio broadcast rechaza, se liberan todos los huecos reservados.`]}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`@botmesh/hub`}),` valida manifiestos de plugins, compatibilidad con el runtime y permisos concedidos antes de habilitar un plugin. Los manifiestos se tratan como JSON no confiable (se comprueba el tipo de cada campo y se rechazan los permisos desconocidos) y el enable vuelve a comprobar los permisos de forma atómica, así que una reinstalación concurrente con más permisos nunca queda habilitada sin ellos. Las instalaciones y el enable/disable se persisten por tenant; por sí solo no ejecuta ni aísla código no confiable.`]}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`@botmesh/deployments`}),` registra versiones inmutables y un despliegue activo por tenant/bot, con reservas para gates en ejecución y auditoría de gate runs. `,_jsx(_components.code,{children:`list(tenant, bot, { limit, after })`}),` pagina las versiones, `,_jsx(_components.code,{children:`prune(tenant, bot, keep)`}),` borra las anteriores a las `,_jsx(_components.code,{children:`keep`}),` más nuevas (nunca la activa) y `,_jsx(_components.code,{children:`new DeploymentRegistry(store, { retainVersions })`}),` poda tras cada publicación, así que la lista de versiones no crece sin límite. Conéctalo a tu sistema de despliegue para hacer rollouts graduales y rollback físico.`]}),`\n`,_jsxs(_components.p,{children:[`Los cinco comparten el mismo modelo de almacenamiento: `,_jsx(_components.code,{children:`Memory…Store`}),` (referencia), `,_jsx(_components.code,{children:`Kv…Store`}),` sobre cualquier `,_jsx(_components.code,{children:`DurableKv`}),`, una suite de conformidad en `,_jsx(_components.code,{children:`test-utils/`}),` y aislamiento de tenant impuesto por el nombre de la tabla en el backend (`,_jsx(_components.code,{children:`@botmesh/tenant-kv`}),`). Toda escritura se valida antes de llegar al backend (un campo mal formado falla con un `,_jsx(_components.code,{children:`TypeError`}),` que lo nombra), así que una escritura mala nunca deja ilegibles los listados de un tenant.`]}),`\n`,_jsx(Heading,{level:`2`,id:`trazas-opentelemetry`,children:`Trazas (OpenTelemetry)`}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`bot.metrics()`}),` cuenta; las trazas explican. Con `,_jsx(_components.code,{children:`BotConfig.telemetry`}),` el bot emite un árbol de spans por mensaje (entrada → dispatch → handler / turno de conversación → llamadas al modelo y herramientas de IA → envío), con los ids con hash por defecto y sin contenido de mensajes. `,_jsx(_components.code,{children:`@botmesh/otel`}),` manda esos spans a OpenTelemetry (Jaeger, Grafana Tempo, cualquier backend OTLP) y publica `,_jsx(_components.code,{children:`bot.metrics()`}),` como métricas de OpenTelemetry (`,_jsx(_components.code,{children:`bot.plug(otelMetricsPlugin())`}),`). Configuración, catálogo de spans, política de datos personales y un ejemplo con Jaeger / Grafana Tempo: `,_jsx(_components.a,{href:`/es/guide/tracing`,children:`Trazas`}),`.`]})]})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent};function _missingMdxReference(id,component){throw new Error(`Expected `+(component?`component`:`object`)+\" `\"+id+\"` to be defined: you likely forgot to import, pass, or provide it.\")}","frontmatter":{},"scope":{"config":{},"pageMetadata":{"title":"Operación: métricas, eventos, inbox y entregas","sidebarTitle":"Operación","description":null,"href":"/es/guide/operations"}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[{"title":"Métricas de runtime","slug":"métricas-de-runtime","depth":2,"children":[{"title":"Prometheus","slug":"prometheus","depth":3,"children":[]},{"title":"Eventos de conversación","slug":"eventos-de-conversación","depth":3,"children":[]}]},{"title":"Paquetes operativos","slug":"paquetes-operativos","depth":2,"children":[{"title":"Procesamiento de eventos","slug":"procesamiento-de-eventos","depth":3,"children":[]},{"title":"Inbox y CRM","slug":"inbox-y-crm","depth":3,"children":[]},{"title":"Campañas, plugins y despliegues","slug":"campañas-plugins-y-despliegues","depth":3,"children":[]}]},{"title":"Trazas (OpenTelemetry)","slug":"trazas-opentelemetry","depth":2,"children":[]}],"codeExamples":{}},"jsLinks":[],"subdomain":"afaya","siteName":"botmesh","title":"Operación: métricas, eventos, inbox y entregas - botmesh","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{"en":{"href":"/guide/operations","matchLevel":1},"es":{"href":"/es/guide/operations","matchLevel":0}}},"lastModified":"2026-10-27T14:26:08.440Z","originalFileLocation":"es/guide/operations.mdx","locale":"es"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"system\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"application-name\" content=\"botmesh\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/afaya/omilwj1Ifh1eDwt5/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/mintlify-assets/_mintlify/favicons/afaya/omilwj1Ifh1eDwt5/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/afaya/omilwj1Ifh1eDwt5/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/afaya/omilwj1Ifh1eDwt5/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/afaya/omilwj1Ifh1eDwt5/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/afaya/omilwj1Ifh1eDwt5/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/afaya/omilwj1Ifh1eDwt5/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/afaya/omilwj1Ifh1eDwt5/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/afaya/omilwj1Ifh1eDwt5/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"Operación: métricas, eventos, inbox y entregas - botmesh\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"botmesh\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://botmesh.afaya.com.co/es/guide/operations\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://afaya.mintlify.app/_mintlify/api/og/v2?division=Caracter%C3%ADsticas\u0026amp;title=Operaci%C3%B3n%3A+m%C3%A9tricas%2C+eventos%2C+inbox+y+entregas\u0026amp;theme=dea9f3ba838ba9e4866bef64\u0026amp;signature=c0522bfd3c08359b2392ee8b2c2828833df5bf70a5507690afd00f732db01e86\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"Operación: métricas, eventos, inbox y entregas - botmesh\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://afaya.mintlify.app/_mintlify/api/og/v2?division=Caracter%C3%ADsticas\u0026amp;title=Operaci%C3%B3n%3A+m%C3%A9tricas%2C+eventos%2C+inbox+y+entregas\u0026amp;theme=dea9f3ba838ba9e4866bef64\u0026amp;signature=c0522bfd3c08359b2392ee8b2c2828833df5bf70a5507690afd00f732db01e86\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/mintlify-assets/_mintlify/favicons/afaya/omilwj1Ifh1eDwt5/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"botmesh\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#3451B2\">\u003clink data-mint-head rel=\"canonical\" href=\"https://botmesh.afaya.com.co/es/guide/operations\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/es/guide/operations.md\">\u003clink data-mint-head rel=\"alternate\" hreflang=\"es\" href=\"https://botmesh.afaya.com.co/es/guide/operations\">\u003clink data-mint-head rel=\"alternate\" hreflang=\"en\" href=\"https://botmesh.afaya.com.co/guide/operations\">\u003clink data-mint-head rel=\"alternate\" hreflang=\"x-default\" href=\"https://botmesh.afaya.com.co/guide/operations\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://botmesh.afaya.com.co/#organization\",\"name\":\"botmesh\",\"url\":\"https://botmesh.afaya.com.co\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintcdn.com/afaya/32LyKbpS8BIF3Yl-/logo/light.svg?fit=max\u0026auto=format\u0026n=32LyKbpS8BIF3Yl-\u0026q=85\u0026s=5a9c4db10ba52bbd645a009cc009e31e\"}},{\"@type\":\"WebSite\",\"@id\":\"https://botmesh.afaya.com.co#website\",\"name\":\"botmesh\",\"url\":\"https://botmesh.afaya.com.co\",\"publisher\":{\"@id\":\"https://botmesh.afaya.com.co/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://botmesh.afaya.com.co/es/guide/operations#webpage\",\"url\":\"https://botmesh.afaya.com.co/es/guide/operations\",\"name\":\"Operación: métricas, eventos, inbox y entregas\",\"dateModified\":\"2026-10-27T14:26:08.440Z\",\"isPartOf\":{\"@id\":\"https://botmesh.afaya.com.co#website\"},\"breadcrumb\":{\"@id\":\"https://botmesh.afaya.com.co/es/guide/operations#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://botmesh.afaya.com.co/es/guide/operations#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Características\",\"item\":\"https://botmesh.afaya.com.co/es/guide/messages\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Operación: métricas, eventos, inbox y entregas\",\"item\":\"https://botmesh.afaya.com.co/es/guide/operations\"}]},{\"@type\":[\"Article\",\"TechArticle\"],\"@id\":\"https://botmesh.afaya.com.co/es/guide/operations#article\",\"headline\":\"Operación: métricas, eventos, inbox y entregas\",\"name\":\"Operación: métricas, eventos, inbox y entregas\",\"url\":\"https://botmesh.afaya.com.co/es/guide/operations\",\"mainEntityOfPage\":{\"@id\":\"https://botmesh.afaya.com.co/es/guide/operations#webpage\"},\"dateModified\":\"2026-10-27T14:26:08.440Z\",\"publisher\":{\"@id\":\"https://botmesh.afaya.com.co/#organization\"},\"isPartOf\":{\"@id\":\"https://botmesh.afaya.com.co#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"botmesh\",\"creator\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","siteName":"botmesh","nav":{"global":null,"languages":[{"language":"en","default":true,"pages":[{"title":"en","href":"/guide/operations"}]},{"language":"es","footer":{"socials":{"github":"https://github.com/andresayac/botmesh"},"links":[{"header":"Proyecto","items":[{"label":"GitHub","href":"https://github.com/andresayac/botmesh"},{"label":"npm","href":"https://www.npmjs.com/package/@botmesh/core"},{"label":"Licencia MIT","href":"https://github.com/andresayac/botmesh/blob/main/LICENSE"},{"label":"Changelog","href":"https://github.com/andresayac/botmesh/blob/main/CHANGELOG.md"}]}]},"tabs":[{"tab":"Guía","icon":"book-open","groups":[{"group":"Primeros pasos","pages":[{"title":"botmesh","sidebarTitle":"Inicio","mode":"wide","href":"/es"},{"title":"Inicio rápido","href":"/es/guide/getting-started"},{"title":"Tu primer bot","href":"/es/guide/first-bot"},{"title":"Conceptos básicos","href":"/es/guide/concepts"},{"title":"Referencia de configuración","href":"/es/guide/configuration"},{"title":"Canales (providers)","sidebarTitle":"Canales","href":"/es/guide/channels"}]},{"group":"Características","pages":[{"title":"Mensajes y media","href":"/es/guide/messages"},{"title":"Flujos conversacionales","href":"/es/guide/flows"},{"title":"Triggers especiales: WELCOME, tipos de mensaje y CALL","sidebarTitle":"Triggers especiales","href":"/es/guide/triggers"},{"title":"Formularios (WhatsApp Flows)","href":"/es/guide/forms"},{"title":"Datos persistentes por usuario (`ctx.user`)","sidebarTitle":"Datos por usuario","href":"/es/guide/user-data"},{"title":"Internacionalización (i18n)","href":"/es/guide/i18n"},{"title":"Handoff humano y listas de acceso","sidebarTitle":"Handoff y listas de acceso","href":"/es/guide/handoff"},{"title":"Hooks (middleware)","href":"/es/guide/hooks"},{"title":"Plugins","href":"/es/guide/plugins"},{"title":"Plugin de IA","href":"/es/guide/ai"},{"title":"Herramientas de IA (tool calling)","sidebarTitle":"Herramientas de IA","href":"/es/guide/ai-tools"},{"title":"Memoria y RAG","href":"/es/guide/ai-memory-rag"},{"title":"Respuestas en streaming (`ctx.stream`)","sidebarTitle":"Respuestas en streaming","href":"/es/guide/streaming"},{"title":"Voz (`@botmesh/voice`)","sidebarTitle":"Voz (STT/TTS)","href":"/es/guide/voice"},{"title":"Integraciones","href":"/es/guide/integrations"},{"title":"Inyección de dependencias","href":"/es/guide/di"},{"title":"Scheduler","href":"/es/guide/scheduler"},{"title":"Rate Limiting (Anti-Ban)","sidebarTitle":"Rate limiting (anti-ban)","href":"/es/guide/rate-limiting"},{"title":"Store persistente","href":"/es/guide/store"},{"title":"Control plane","href":"/es/guide/control-plane"},{"title":"Servidor de webhooks (`@botmesh/server`)","sidebarTitle":"Servidor de webhooks","href":"/es/guide/server"},{"title":"Ventana de 24 h y costos (Cloud API)","sidebarTitle":"Ventana de 24 h de WhatsApp","description":null,"href":"/es/guide/session-window"},{"title":"Operación: métricas, eventos, inbox y entregas","sidebarTitle":"Operación","href":"/es/guide/operations"},{"title":"Trazas (OpenTelemetry)","description":null,"href":"/es/guide/tracing"},{"title":"Procesamiento durable de mensajes","sidebarTitle":"Procesamiento durable","href":"/es/guide/durability"}]},{"group":"Desarrollo y tests","pages":[{"title":"Simulador en la terminal (`botmesh dev`)","sidebarTitle":"Simulador en la terminal","href":"/es/guide/dev-simulator"},{"title":"Sandbox de plataformas","href":"/es/guide/sandbox"},{"title":"Testing","href":"/es/guide/testing"},{"title":"Inspeccionar y validar flujos","href":"/es/guide/flow-tools"},{"title":"Esquema JSON de flujos","href":"/es/guide/flow-schema"},{"title":"Servidor MCP","href":"/es/guide/mcp"},{"title":"Transcripciones doradas","href":"/es/guide/golden-transcripts"},{"title":"Plugin de ESLint","href":"/es/guide/eslint"}]},{"group":"Avanzado","pages":[{"title":"Sub-APIs (v1) → escape hatches del transporte (v2)","sidebarTitle":"Sub-APIs","href":"/es/guide/sub-apis"},{"title":"Pasar a producción","href":"/es/guide/production"},{"title":"Despliegue","href":"/es/guide/deploy"},{"title":"Varias réplicas","href":"/es/guide/replicas"},{"title":"Arquitectura","href":"/es/guide/architecture"},{"title":"Redis Cluster","href":"/es/guide/redis-cluster"},{"title":"Seguridad","href":"/es/guide/security"},{"title":"Modelo de amenazas","href":"/es/guide/threat-model"},{"title":"Actualizar a estable: checklist y cambios de API","sidebarTitle":"Actualizar a estable: checklist y API","href":"/es/guide/stable-migration"},{"title":"Actualizar a estable: dependencias","href":"/es/guide/migration"},{"title":"Actualizar a estable: cambios de comportamiento","sidebarTitle":"Actualizar a estable: comportamiento","href":"/es/guide/migration-2"},{"title":"Notas de actualización: canales y plugins","href":"/es/guide/upgrade-channels-plugins"},{"title":"Solución de problemas","href":"/es/guide/troubleshooting"},{"title":"Preguntas frecuentes","href":"/es/guide/faq"}]}]},{"tab":"Ejemplos","icon":"chef-hat","pages":[{"title":"Ejemplos","href":"/es/examples"}]},{"tab":"Referencia de la API","icon":"code","href":"/api/core"}]}]},"title":"Operación: métricas, eventos, inbox y entregas - botmesh","buildId":"dpl_DFJrtvUi8HS5F9nkooKuDJsuD5BM","deploymentId":"dpl_DFJrtvUi8HS5F9nkooKuDJsuD5BM","dynamics":[],"chunks":[]}