# Rules - Cognigy / Postman

## Objective

This file defines the working rules for future agents that need to operate on Cognigy.AI in this environment.
Act as a senior architect for Cognigy.AI and use the Cognigy MCPs available in this environment to work directly on the Mercantil_DEMO project.

## Available MCPs

### 1. Cognigy Tools MCP

This MCP is used to work directly with Cognigy.AI resources.

Main usage:

* list projects,
* list agents,
* list Flows,
* list Endpoints,
* list Knowledge Stores,
* list Tools,
* create or update AI Agents,
* create or update supported Tools,
* manage Packages,
* inspect existing resources.

Rule:

```text
Use the Cognigy Tools MCP first for any operational action on Cognigy.
```

Examples:

```text
mcp__cognigy.list_resources
mcp__cognigy.get_resource
mcp__cognigy.manage_packages
mcp__cognigy.update_ai_agent
mcp__cognigy.update_tool
```

### 2. Cognigy Documentation MCP

This MCP is used to consult the official Cognigy documentation when it is necessary to validate endpoints, payloads, limits, or platform behavior.

Main usage:

* search official documentation,
* confirm API routes,
* review the behavior of snapshots, packages, endpoints, handover, Knowledge, Flow Nodes, or AI Agents,
* avoid assumptions when an operation is not clear.

Rule:

```text
If an action is not clear or the Tools MCP does not expose an operation, consult the Cognigy documentation first.
```

## Postman MCP

There is also a Postman MCP available in this environment.

In Postman, there is a collection with well-documented Cognigy APIs, including OpenAPI definitions.

Main usage:

* search for Cognigy endpoints that are not available in the Tools MCP,
* review payloads,
* confirm HTTP methods,
* validate headers,
* generate code/API context from existing requests,
* use the collection as the source of truth when the Cognigy MCP does not cover an action.

Rule:

```text
If the Cognigy MCP does not have a tool for a required action, use the Postman MCP and the Cognigy OpenAPI collection to find the correct endpoint.
```

## Recommended Work Order

1. Try to resolve the task with the Cognigy Tools MCP.
2. If the tool does not exist or does not support the required resource, consult the Cognigy documentation.
3. If direct API usage is required, search for the endpoint in Postman/OpenAPI.
4. Execute the direct API only with the confirmed endpoint and payload.
5. Verify the result with the Cognigy MCP or the direct API.
6. Document any action that could not be completed through the MCP.

## Rule for Direct API Usage

Use the direct Cognigy API only when:

* the MCP does not expose the required tool,
* the MCP fails with a confirmed error,
* the documentation or Postman confirms the correct endpoint,
* the action is necessary to complete the task.

Examples of actions that may require direct API usage:

```text
POST /v2.0/snapshots
GET /v2.0/snapshots
POST /v2.0/flows/{flowId}/chart/nodes
PATCH /v2.0/flows/{flowId}/chart/nodes/{nodeId}
PATCH /v2.0/aiagents/{aiAgentId}
```

## Verified Workaround: update_ai_agent Fails with 500

In this environment, the MCP `mcp__cognigy.update_ai_agent` may fail with:

```text
Internal Server Error
status: 500
code: 1008
```

This occurred when trying to update `jobConfig`.

Rule:

```text
If update_ai_agent fails with 500 while updating jobConfig, do not retry blindly.
Update through the direct API in two steps:
1. PATCH the AI Agent resource for instructions/persona.
2. PATCH the AI Agent Job Node for jobConfig/instructions/description/temperature/maxTokens.
```

### Step 1: Identify the AI Agent Job Node

Use the MCP first if available:

```text
mcp__cognigy.manage_flow_nodes
operation: list
flowId: <flowId>
```

Look for the node:

```text
type: aiAgentJob
label: AI Agent
```

### Step 2: Read the Current Job Node

Before making a PATCH request, read the full node configuration.

Endpoint:

```http
GET /v2.0/flows/{flowId}/chart/nodes/{nodeId}
```

Example:

```bash
curl -L -sS \
  'https://api-trial.cognigy.ai/new/v2.0/flows/<flowId>/chart/nodes/<nodeId>' \
  -H 'X-API-Key: <COGNIGY_API_KEY>'
```

Important rule:

```text
Do not build the config from scratch.
Read the existing config, modify only the required fields, and send the full config back.
```

Fields that have been successfully modified:

```text
config.name
config.description
config.instructions
config.temperature
config.maxTokens
```

Fields that must be preserved:

```text
config.aiAgent
config.llmProviderReferenceId
config.toolChoice
config.memoryType
config.knowledgeSearchBehavior
config.knowledgeSearchTopK
config.timeoutInMs
config.inputKey
config.contextKey
config.storeLocation
config.customModelOptions
config.customRequestOptions
```

### Step 3: Update the Job Node

Endpoint:

```http
PATCH /v2.0/flows/{flowId}/chart/nodes/{nodeId}
```

Payload:

```json
{
  "config": {
    "...": "full existing config with only the required changes"
  }
}
```

Expected response:

```text
HTTP 204
```

### Step 4: Update the AI Agent Instructions

Endpoint:

```http
PATCH /v2.0/aiagents/{aiAgentId}
```

Typical payload:

```json
{
  "instructions": "New agent instructions..."
}
```

Expected response:

```text
HTTP 204
```

### Step 5: Verify

Verify with:

```text
mcp__cognigy.get_resource
resourceType: agent
raw: true
```

and/or:

```http
GET /v2.0/flows/{flowId}/chart/nodes/{nodeId}
```

Then test with:

```text
mcp__cognigy.talk_to_agent or socket.io package
```

## Security

Do not expose credentials.

Rules:

* do not print API keys in final responses,
* do not store API keys in Markdown,
* do not share connection secrets,
* do not delete resources without explicit confirmation,
* do not use destructive commands,
* do not duplicate resources if they already exist.

## Server

To run the server with cloudflared, use the --protocol http2 flag. Execute this command when you need to expose localhost to the internet.
`npx -y cloudflared tunnel --url <http://127.0.0.1:3001> --protocol http2 --no-autoupdate`

## Final Rule

```text
Before executing API calls, check the scripts/ folder to reuse existing code.
After using a direct API call, shell workaround, or repeatable operational command, create or update a reusable script in scripts/.
After discovering an error pattern or workaround, update AGENTS.md > UPDATES with the symptom, cause, and solution.
Cognigy MCP first.
Cognigy documentation for validation.
Postman/OpenAPI when the MCP does not have the action.
Direct API only if it is confirmed and necessary.
```

## UPDATES

### 2026-07-10 - Crear snapshot de Mercantil_Demo

Symptom:

```text
The Cognigy Tools MCP does not expose a snapshot creation operation.
```

Cause:

```text
Snapshot creation is available through the Cognigy REST API, not through the current Tools MCP surface.
```

Solution:

```text
Use scripts/create_mercantil_snapshot.js. It calls POST /v2.0/snapshots, polls GET /v2.0/tasks/{taskId}, then verifies the result with GET /v2.0/snapshots?projectId=<projectId>.
Default project: Mercantil_Demo / 6a4e7963fac4430baeb323a4.
```

### 2026-07-10 - Migracion backend Mercantil sin omnicanalidad

Symptom:

```text
Mercantil needed the reusable Bancamiga backend capabilities, but without NICE/omnichannel, demo reset, or Contact Profile cleanup.
```

Cause:

```text
The previous Mercantil backend was a compact native HTTP server with OTP demo fijo and OCR/reclamos logic, while Bancamiga had reusable Express modules for TOTP, cards, cases, replacements, and commercial applications.
```

Solution:

```text
Mercantil now uses a modular Express backend under /bank/mercantil/demo with Mercantil-only seed data, TOTP real, API key protection, card blocking, cases, replacements, applications, and the existing OCR/reclamos logic.
No /demo/reset, /demo/state, Contact Profile cleanup service, or omnichannel/NICE backend route was added.
Use npm test and npm run check for validation. Use scripts/verify_mercantil_demo_services.js with --base-url after starting the backend.
```

Operational note:

```text
Detached background startup can fail in the Codex sandbox with listen EPERM. Foreground npm start works for validation. For a public tunnel, use scripts/start_mercantil_demo_services.sh so the server stays alive for the lifetime of the tunnel process.
```

### 2026-07-10 - Cloudflared public tunnel blocked, localtunnel fallback

Symptom:

```text
cloudflared prints a trycloudflare.com URL, but the hostname does not resolve and the tunnel logs repeat edge connection failures on TCP/7844.
```

Cause:

```text
The local network/session can block Cloudflare edge tunnel connectivity even when the backend is healthy on localhost.
```

Solution:

```text
Keep cloudflared as the default path with scripts/start_mercantil_demo_services.sh. If the trycloudflare.com URL is not reachable, rerun with TUNNEL_PROVIDER=localtunnel scripts/start_mercantil_demo_services.sh and use the loca.lt URL for Cognigy tool testing.
```

Follow-up:

```text
In this environment, a later cloudflared run became reachable after forcing IPv4:
npx -y cloudflared tunnel --url http://127.0.0.1:3001 --protocol http2 --edge-ip-version 4 --no-autoupdate
The reusable script supports this as CLOUDFLARED_EDGE_IP_VERSION=4 ./scripts/start_mercantil_demo_services.sh.
Wait for a "Registered tunnel connection" log before updating Cognigy.
```

### 2026-07-10 - Mercantil debe conservar el nodo NiCE Handover

Symptom:

```text
The migration plan said not to include omnichannel/NiCE, but the Mercantil demo Flow must keep the handoverToAgent node in tool_build_handover_summary.
```

Cause:

```text
The handover summary tool prepares safe mc_* attributes, and the following NiCE handover node is required for the demo escalation path.
```

Solution:

```text
Do not delete the handoverToAgent node from the tool_build_handover_summary branch.
If it is missing, use scripts/list_cognigy_handover_providers.js to find the project provider, then scripts/create_or_update_mercantil_handover.js to recreate it after the handover summary post-process node.
Verified chain: Pre-Process -> HTTP Request -> Post-Process -> handoverToAgent NiCE -> Resolve.
```

### 2026-07-10 - QR TOTP cuando el usuario no tiene OTP

Symptom:

```text
MIA responded "No puedo mostrar ni regenerar el QR de autenticación" when the user said they did not have the OTP.
```

Cause:

```text
The backend already had GET /auth/qr/:userId?format=json, but Mercantil did not have the Bancamiga-style tool_get_totp_qr branch and the agent guardrails did not explicitly allow QR enrollment display.
```

Solution:

```text
Mercantil now has tool_get_totp_qr. It calls GET /auth/qr/{{input.backendUserId}}?format=json, stores qrDataUrl only in input.result, shows it through a Say image node, and returns only a compact llmResult to the AI Agent.
QR enrollment display is allowed when the user has no OTP or needs to configure an authenticator app. Do not expose manualSecret, otpauthUrl, raw TOTP secret, or OTP codes in agent text.
```

### 2026-07-10 - OCR real sin fallback demo

Symptom:

```text
When tool_extract_ticket_data received an image attachment without OCR text, the backend returned fixed demo ticket data.
```

Cause:

```text
The old adapter used a DEFAULT_TICKET_TEXT fallback for attachments with no text. This made unreadable images look like successful OCR extractions.
```

Solution:

```text
Do not simulate extraction from an attachment alone. The backend must only extract from nativeOcrText, ocrText, ticketText, structured fields already produced by Cognigy vision/OCR, or message/text only when no attachment is present.
If an image attachment arrives without OCR text or structured fields, return OCR_UNREADABLE_IMAGE with ocrMode unreadable_attachment and include that condition in the handover summary for NiCE/manual review.
When an attachment is present, do not treat the user's conversational message as OCR text; otherwise generic phrases such as "quiero reclamar un cobro" can create a false extraction.
```

### 2026-07-10 - No exponer lenguaje de demo o sistema al cliente

Symptom:

```text
When the user asked "cuales son tus funciones", MIA listed capabilities with the word "demo" and could expose phrases such as "base de conocimiento".
```

Cause:

```text
The AI Agent persona, job instructions, and several tool descriptions used internal implementation wording such as demo, tool, backend, endpoint, or Knowledge Store.
```

Solution:

```text
Keep internal IDs/endpoints unchanged, but do not expose them in user-facing language. Agent instructions must prohibit saying demo, prueba, base de conocimiento, tool, API, endpoint, flujo, sistema interno, or internal node/tool IDs. Tool descriptions should describe customer-facing banking actions without demo wording.
For unknown information, MIA should answer like a human advisor: offer orientation or escalation, without saying "no encontre informacion en mi base de conocimiento".
```

### 2026-07-10 - WhatsApp OCR con imagenes

Symptom:

```text
When a customer sent an image by WhatsApp, Cognigy logs showed text: null and the image only inside data.request.entry[].changes[].value.messages[].image.url. MIA asked again for the receipt instead of processing OCR.
```

Cause:

```text
The WhatsApp endpoint did not have an input transformer enabled. The raw WhatsApp image payload was not normalized into data.inputAttachments/data.attachments, so the AI Agent did not see a file attachment. Also, Meta lookaside media URLs are not reliable as direct AI Agent image URLs because the media can require the WhatsApp bearer token.
```

Solution:

```text
Use scripts/update_mercantil_whatsapp_transformer.js. It enables the WhatsApp input transformer, imports WhatsApp image/document/video media through POST /bank/mercantil/demo/whatsapp-media/import, converts the imported public /uploads/... URL into inputAttachments, masks endpoint logs/analytics/IP, and stores the latest WhatsApp attachment in transformer session storage for 10 minutes. If the user sends the photo first and the issue type in the next message, the transformer reattaches the stored image when the text matches a claim/issue-type phrase.
If the cloudflared URL changes, reapply the transformer with `node scripts/update_mercantil_whatsapp_transformer.js --backend-url <public-url>`.
The endpoint is Mercantil DEMO WS / 6a501c77fac4430baec2fc44. Keep internal endpoint names as-is; do not expose "demo" to customers.
```

### 2026-07-10 - NiCE handover falls back into Default

Symptom:

```text
After adding the NiCE handover node, MIA kept looping, falling back into Default behavior, or asking for more receipt/claim details instead of treating the transfer as terminal.
```

Cause:

```text
Mercantil was missing the Bancamiga-style preparation step before the handover node. The handover node read context.mercantilHandover.customAttributes, but the tool branch only set input.result from the HTTP response. The tool description also did not tell the LLM that tool_build_handover_summary executes the NiCE handover and should be terminal.
```

Solution:

```text
Inside tool_build_handover_summary, keep this chain:
Pre-Process -> HTTP Request -> Post-Process -> Prepare NiCE Handover -> Execute NiCE Handover -> Resolve.
The Prepare NiCE Handover code node must set input.mercantilHandover, context.mercantilHandover, context.mercantilHandoverReady, and input.result.handoverWillExecute.
The tool description and AI Agent Job instructions must say that after calling tool_build_handover_summary the conversation is escalated and MIA must not ask for more receipt, issue type, TOTP, or additional data unless the handover explicitly fails.
Verified smoke session mercantil-handover-smoke-20260710-002 returned only: "He iniciado la transferencia por WhatsApp. Un especialista continuara su atencion."
```

### 2026-07-10 - NiCE rejects handover with blank custom attributes

Symptom:

```text
service-handover logged DigitalEngagementApiError: Could not send a message to NiCE CXOne: 400 Bad Request with repeated "This value should not be blank."
```

Cause:

```text
The Mercantil handover node sent mc_* custom attributes with empty string fallbacks. In a generic transfer without a created claim or readable ticket, fields such as mc_claimId, mc_ticketSummary, mc_amount, mc_currency, mc_merchant, mc_cardLast4, and mc_ocrConfidence were blank. The seven blank attributes matched the seven NiCE validation messages.
```

Solution:

```text
Run scripts/update_mercantil_handover_attribute_defaults.js. It patches tool_build_handover_summary - Execute NiCE Handover so every mc_* custom attribute has a non-empty safe fallback, for example sin_reclamo, sin_ticket, 0, VES, no_disponible, 0000, and webchat.
After patching, smoke test the REST/NiCE endpoint and check Cognigy logs for absence of the DigitalEngagementApiError.
```

### 2026-07-10 - NiCE receives masked conversation history

Symptom:

```text
The NiCE agent desktop showed "No conversation history available" and masked values such as "*****" after the Mercantil handover reached the agent.
```

Cause:

```text
Mercantil Webchat, WhatsApp, and NiCE endpoints had maskLogging, maskAnalytics, and maskIPAddress enabled. Bancamiga's working Webchat/NiCE endpoints had those flags disabled, so the handover transcript and related data reached NiCE in readable form.
```

Solution:

```text
Use scripts/update_cognigy_endpoint_privacy.js on Mercantil endpoints only. Keep storeDataPayload=false, but set --mask-logging false --mask-analytics false --mask-ip-address false for the channel endpoint and the NiCE endpoint.
Applied endpoints: Webchat 6a4eaad6bad16ab4ee468a95, WhatsApp 6a501c77fac4430baec2fc44, NiCE 6a4e8ad3d9543729fbb4266e.
Smoke test with scripts/test_cognigy_nice_endpoint.js and then verify in the NiCE agent desktop because Cognigy can only confirm that the handover request succeeded.
```

### 2026-07-11 - WhatsApp shows handover accepted text but does not enter NiCE

Symptom:

```text
From WhatsApp, MIA sent the Handover Accepted Message ("Te conecto con un especialista Mercantil..."), but the conversation did not appear in the NiCE agent chat. Webchat handover with the same node/provider did appear in NiCE.
```

Cause:

```text
Conversation inspection showed the WhatsApp session emitted the handover text, but all conversation events stayed with inHandoverRequest=false and inHandoverConversation=false. Logs in the same window showed no service-handover error and no provider rejection. Bancamiga is not an exact WhatsApp comparison in this environment because it has Webchat, REST, and NiCE endpoints, but no native WhatsApp endpoint.
```

Solution:

```text
Use scripts/inspect_cognigy_conversations.js and scripts/inspect_cognigy_logs.js to compare the WhatsApp session against a successful Webchat session. First low-risk adjustment: run scripts/update_mercantil_handover_nice_options.js to keep sendTranscriptAsFirstMessage=true and set handoverProviderConfig.showAgentDetails=false, matching Bancamiga and avoiding a Webchat-only NiCE display option on WhatsApp.
If WhatsApp still does not create inHandoverRequest=true after this change, the next candidate is architectural: move the actual handover execution to a normal Flow handover path after the AI Agent tool sets context.mercantilHandoverReady, or route WhatsApp through NiCE instead of the native Cognigy WhatsApp endpoint.
```

### 2026-07-11 - WhatsApp handover router outside AI Agent tool

Symptom:

```text
After setting showAgentDetails=false, WhatsApp still sent the handover accepted text but did not create a NiCE conversation. The latest failing WhatsApp session c6287c65-49f3-4f10-8ca2-97366d385b5d stayed with inHandoverRequest=false and no service-handover logs.
```

Cause:

```text
The Handover node inside tool_build_handover_summary can produce the accepted text on WhatsApp without starting the actual handover state. Webchat still works through the existing in-tool handover path, so the fix should not replace the working Webchat path.
```

Solution:

```text
Use scripts/patch_mercantil_whatsapp_handover_router.js. It keeps the existing handover node enabled for Webchat and adds a WhatsApp-only router after the AI Agent node:
Detect -> If context.mercantilHandoverRouteWhatsApp === true -> Mark Consumed -> Execute NiCE Handover.
It also updates tool_build_handover_summary - Pre-Process so the backend handover summary receives the detected channel instead of defaulting to webchat.
Snapshot before the structural change: pre-whatsapp-handover-structural-20260711 / 6a52509d5523ba360979e0fa.
Snapshot after the structural change: post-whatsapp-handover-router-20260711 / 6a525314fac4430baed2d6d1.
Created router nodes: Detect 6a5251a85523ba360979e4cc, If 6a5251ddfac4430baed2d322, Mark Consumed 6a525201fac4430baed2d3ab, Execute NiCE Handover 6a525202d9543729fbd4fd11.
REST smoke tests are not conclusive because they run on channel rest. Verify with a real WhatsApp transfer and inspect for service-handover logs or inHandoverRequest=true.
Detailed local log: whatsapp-handover-change-log.md.
```

Follow-up:

```text
If Cognigy Flow UI shows "Unable to render the main content" after this router change, do not remove the working handover first. Check node 6a5251ddfac4430baed2d322. The UI expects the if node condition as a condition object, not a plain string. Normalize it with mcp__cognigy.manage_flow_nodes update or the current scripts/patch_mercantil_whatsapp_handover_router.js, which now preserves config.condition.condition = "context.mercantilHandoverRouteWhatsApp === true".
```

### 2026-07-11 - WhatsApp re-transfers when NiCE closes the conversation

Symptom:

```text
After a WhatsApp transfer reached NiCE, closing/resolving the conversation in NiCE sent the transfer text again ("Te conecto con un especialista Mercantil...") and started another transfer.
```

Cause:

```text
The WhatsApp router handover node had sendResolveEvent=true. The NiCE close/resolve event reentered the Flow after the AI Agent, and the router still had enough handover context to execute another handover. Evidence conversation: 713cf18c-7e17-43e8-9d48-49be3571dc46, with close event around 2026-07-11T14:37:40Z and repeated transfer text at 2026-07-11T14:37:43Z.
```

Solution:

```text
For the WhatsApp router handover node only, keep sendResolveEvent=false. Current node: Mercantil WhatsApp Handover Router - Execute NiCE Handover / 6a525202d9543729fbd4fd11. Do not delete the router or the original tool_build_handover_summary handover node.
Use scripts/patch_mercantil_whatsapp_handover_router.js to reapply the router safely; the script now preserves sendResolveEvent=false, repeatHandoverMessage=false, the NiCE provider, and transcript/custom attribute settings.
Snapshot before the anti-retransfer fix: pre-whatsapp-handover-no-resolve-event-20260711 / 6a5257ad5523ba360979f32e.
Snapshot after the anti-retransfer fix: post-whatsapp-handover-no-resolve-event-20260711 / 6a52586c5523ba360979f64a.
Validation must be done in real WhatsApp/NiCE: transfer, accept in NiCE, close in NiCE, and confirm the transfer text is not sent again.
```

### 2026-07-11 - NiCE completeHandover event reopens WhatsApp after customer last message

Symptom:

```text
If the customer sent the last message during a WhatsApp/NiCE handover and the NiCE agent closed without replying, Cognigy opened another interaction and sent the transfer text again. If the agent sent the last message and then closed, the conversation stayed closed.
```

Cause:

```text
Conversation 0579511f-fb3f-4761-bb0a-88472eb00f22 showed that NiCE injected a provider close event through endpoint-handleProviderEvent: _cognigy.controlCommands[0].type = completeHandover and messageContent.text = #__AGENT__ENDED__HANDOVER__#. This arrives with text="", but the WhatsApp router previously did not filter it and could re-trigger handover when customer activity was the last real message.
```

Solution:

```text
Use scripts/patch_mercantil_whatsapp_handover_router.js. The Detect node now treats completeHandover or #__AGENT__ENDED__HANDOVER__# as terminal close events: it clears context.mercantilHandoverReady, sets context.mercantilHandoverRouteWhatsApp=false, and sets input.mercantilHandoverCompleteEventIgnored=true. The handover node/provider/custom attributes are not changed.
Snapshot before the guard: pre-whatsapp-handover-complete-event-guard-20260711 / 6a525c2cd9543729fbd54918.
Snapshot after the guard: post-whatsapp-handover-complete-event-guard-20260711 / 6a525c97fac4430baed3222e.
Validation must be done in real WhatsApp/NiCE: customer-last-message then close should not reopen, agent-last-message then close should remain closed, and a normal WhatsApp transfer should still reach NiCE.
```

### 2026-07-11 - Resume/default handover data not visible in NiCE

Symptom:

```text
Transfers reached NiCE, but the resume/default data was not visible. OCR was not being used for transfer tests, so default customer/name/context fields still needed to arrive.
```

Cause:

```text
The Mercantil handover summary endpoint can return either a claim/OCR root object with operatorSummary/customAttributes or a default case object nested under handoverSummary. The Prepare NiCE Handover node only read root-level operatorSummary/customAttributes, so the nested default data did not populate context.mercantilHandover.
```

Attempted solution and rollback:

```text
An attempted fix normalized both root and nested handoverSummary formats and added mc_customerName, mc_customerId, mc_caseId, mc_resume, mc_actionRecommended, and mc_urgency to both NiCE handover nodes.
Snapshot before the fix: pre-handover-resume-defaults-20260711 / 6a52600c5523ba36097a4019.
Snapshot after the fix: post-handover-resume-defaults-20260711 / 6a5265c15523ba36097a4f7b.
The attempted fix broke handover execution and was reverted on 2026-07-11 using scripts/revert_mercantil_handover_resume_defaults.js. Current state is back to the previous Prepare NiCE Handover code and the original 11 mc_* custom attributes. The WhatsApp router still keeps sendResolveEvent=false.
Do not reapply the removed resume-defaults approach without first validating NiCE custom attribute limits/allowed fields and transfer behavior in a disposable snapshot.
```

### 2026-07-11 - Resume a NiCE por mensaje DFO desde backend

Symptom:

```text
El handover llega a NiCE, pero el agente no ve un resume estructurado con la data del reclamo. El transcript/custom attributes nativo de Cognigy no es suficiente o no se muestra como resume en la vista del agente.
```

Cause:

```text
Cognigy NiCE handover soporta transcript y custom attributes, pero no expone un campo nativo probado como "private resume" para el agente. Agregar nuevos custom attributes rompio el handover previamente, asi que el resume debe enviarse fuera de los atributos del nodo handover.
```

Solution:

```text
Mercantil backend ahora tiene endpoints protegidos para NiCE:
- POST /bank/mercantil/demo/nice/auth/token
- POST /bank/mercantil/demo/nice/auth/refresh
- POST /bank/mercantil/demo/nice/resume-message

El resume-message construye un mensaje DFO inbound hacia:
POST /dfo/3.0/channels/{NICE_DFO_CHANNEL_ID}/messages

El telefono WhatsApp se usa como:
- idOnExternalPlatform: <telefono>_<UTC ISO>
- thread.idOnExternalPlatform: <telefono>
- authorEndUserIdentity.idOnExternalPlatform: <telefono>
- createdAtWithMilliseconds: UTC ISO con milisegundos.

El Flow Cognigy ahora tiene un HTTP node antes del handover:
tool_build_handover_summary - Send NiCE Resume Message / 6a527b46fac4430baed3747d

Cadena verificada:
Pre-Process -> HTTP Request -> Post-Process -> Prepare NiCE Handover -> Send NiCE Resume Message -> Execute NiCE Handover -> Resolve.

El nuevo HTTP node tiene abortOnError=false para no bloquear el handover si NiCE rechaza el mensaje.
El backend tambien devuelve un `skipped` controlado con `reason=nice_auth_not_configured` cuando faltan credenciales NiCE, para que esa condicion no interrumpa la transferencia.
Script reusable: scripts/patch_mercantil_nice_resume_message.js.
Snapshot antes: Mercantil_Demo_snapshot_20260711T164506Z / 6a5273135523ba36097a79e7.
Snapshot despues del patch inicial: Mercantil_Demo_snapshot_20260711T172045Z / 6a527b6d5523ba36097a8e15.
Snapshot final con URL publica actualizada: Mercantil_Demo_snapshot_20260711T172555Z / 6a527ca4d9543729fbd5a060.
URL publica actual en Cognigy: https://fares-perhaps-participating-constraints.trycloudflare.com.

Variables requeridas en el proceso backend para envio real:
NICE_CLIENT_ID, NICE_CLIENT_SECRET, NICE_USERNAME, NICE_PASSWORD. Opcional: NICE_REFRESH_TOKEN.
En la ejecucion inicial esas variables no estaban cargadas. Luego se reinicio backend/tunel con credenciales en env.js y el endpoint /nice/resume-message respondio sent=true contra NiCE. Validacion local: npm run check OK, npm test 30 passed.
El backend registra diagnostico de resume en .run/mercantil-demo-server.log con scope mercantil_nice_resume y mercantil_nice_auth. Los logs incluyen phoneSource y telefono enmascarado, sin tokens.
Snapshot diagnostico phoneSource: Mercantil_Demo_snapshot_20260711T194843Z / 6a529e1bfac4430baed3fbf2.
```

### 2026-07-11 - WhatsApp input transformer robusto para texto e imagen

Symptom:

```text
Al activar el input transformer del endpoint WhatsApp, MIA podia dejar de responder incluso ante "Hola". Para pruebas OCR, las imagenes por WhatsApp tampoco llegaban de forma confiable como inputAttachments.
```

Cause:

```text
El transformer dependia de operaciones fragiles dentro de handleInput: getSessionStorage sin try/catch y la importacion de media solo cuando el payload ya traia media.url. Si Cognigy/WhatsApp entregaba solo media.id o fallaba session storage/import, el transformer podia caer al payload crudo o perder la normalizacion requerida.
```

Solution:

```text
Use scripts/update_mercantil_whatsapp_transformer.js con --backend-url <url-publica-actual>. El transformer ahora:
- mantiene el inputTransformerEnabled=true;
- conserva maskLogging/maskAnalytics/maskIPAddress=false;
- captura errores de getSessionStorage sin romper mensajes de texto;
- envia mediaId al backend ademas de url;
- no crea inputAttachments con url vacia;
- agrega whatsappMediaImportStatus, whatsappMediaImportError, whatsappMediaId y whatsappSessionStorageError para diagnostico.

El backend /bank/mercantil/demo/whatsapp-media/import ahora acepta mediaId. Si no llega url pero si mediaId y bearerToken, resuelve la URL via Graph API y luego guarda una copia temporal bajo /uploads. Los logs seguros salen con scope mercantil_whatsapp_media_import, enmascarando mediaId y sin imprimir bearerToken.

Validacion:
- node --check scripts/update_mercantil_whatsapp_transformer.js OK.
- node --check src/app.js OK.
- npm test OK, 31 passed.
- Endpoint WhatsApp verificado con URL publica actual: https://fares-perhaps-participating-constraints.trycloudflare.com/bank/mercantil/demo/whatsapp-media/import.
- Prueba publica mediaId sin bearerToken devuelve MISSING_WHATSAPP_BEARER_TOKEN, confirmando que el backend cargado es el nuevo.

Operational note:
En este sandbox, intentar arrancar el backend mediante el wrapper con redireccion a .run/mercantil-demo-server.log puede fallar con listen EPERM. El arranque directo `HOST=127.0.0.1 PORT=3001 npm start` si funciona. Si se usa el arranque directo, los logs activos quedan en la sesion del proceso; no asumir que .run/mercantil-demo-server.log tiene la ultima ejecucion.
```

### 2026-07-11 - NiCE resume sin [object Object] y con ticket OCR compacto

Symptom:

```text
El resume llegaba al agente, pero el primer bloque mostraba `Motivo: [object Object]` y `Ticket: sin_ticket`, aunque el OCR ya habia procesado informacion del comprobante.
```

Cause:

```text
El campo `Motivo` del primer bloque viene de `customAttributes.mc_issueType`, no del OCR. En algunos turnos Cognigy/AI Agent pasaba issueType como objeto. El builder de NiCE lo convertia con String(objeto), produciendo `[object Object]`. Ademas, si `mc_ticketSummary` venia vacio, el resume no reconstruia el ticket desde `mc_merchant`, `mc_amount`, `mc_currency`, `mc_date` y `mc_cardLast4`.
```

Solution:

```text
Backend:
- src/services/niceDfo.service.js ahora convierte objetos/arrays a texto legible antes de armar el resume.
- Si `mc_ticketSummary` esta vacio, reconstruye `Ticket` desde los atributos OCR disponibles.
- src/mercantilDemo.js normaliza issueType, ruleDecision y campos OCR object-like antes de producir customAttributes.

Cognigy:
- scripts/patch_mercantil_nice_resume_message.js actualiza tool_build_handover_summary - Pre-Process para pasar issueType y variantes de ticket/OCR: ticket, ticketExtract, ticketData, ocrTicket, ocr, receipt y claim.ticket.

Interpretacion:
- `Motivo` = tipo/motivo del reclamo.
- `Ticket` = resumen de datos OCR: comercio, monto, fecha y tarjeta.

Validacion:
- npm test OK, 33 passed.
- Snapshot antes: pre-fix-nice-resume-object-object-20260711 / 6a52b1f3fac4430baed45396.
- Snapshot despues: post-fix-nice-resume-object-object-20260711 / 6a52b249fac4430baed455fa.
```

### 2026-07-11 - Resume OCR sin claimId y transferencia automatica despues de OTP

Symptom:

```text
El resume de NiCE podia llegar con `Ticket: sin_ticket` y `Motivo: human_handover` aunque Cognigy ya hubiera leido OCR. Ademas, despues de validar OTP en un reclamo con comprobante, MIA podia esperar a que el usuario pidiera transferencia en vez de escalar automaticamente.
```

Cause:

```text
Habia dos causas combinadas:
1. /bank/mercantil/demo/handover-summary solo usaba el builder de reclamos cuando venia claimId o claim. Si Cognigy tenia ticket OCR pero aun no habia claimId, la ruta caia al resumen generico de casos y perdia customAttributes OCR.
2. El Pre-Process de tool_build_handover_summary podia aceptar objetos vacios enviados por el LLM, por ejemplo ticket: {} o claim: {}, y esos objetos bloqueaban el fallback a context.mercantilLastTicket/context.mercantilLastClaim.
```

Solution:

```text
Backend:
- src/controllers/cases.controller.js ahora usa el builder de reclamos si el payload trae ticket/ticketExtract/ticketData/ocrTicket/ocr/receipt, claim, claimId, issueType real, rules o ruleDecision.
- src/schemas/cases.schema.js acepta claimId/caseId/userId vacios como undefined para no romper payloads de Cognigy sin claimId creado.
- test/server.test.js valida el caso exacto: claimId vacio + ticket OCR + issueType devuelve customAttributes con mc_ticketSummary, mc_merchant, mc_amount, mc_cardLast4, etc.

Cognigy:
- scripts/patch_mercantil_ocr_otp_auto_handover.js actualiza URLs de tools a la URL publica actual.
- Los post-process de OCR/reglas/claim/OTP guardan context.mercantilLastTicket, context.mercantilLastRules, context.mercantilLastClaim y context.mercantilAutoTransferAfterOtp.
- Los pre-process de tool_evaluate_claim_rules y tool_create_claim usan context.mercantilLastTicket/context.mercantilLastRules/context.mercantilIdentity si el LLM no vuelve a pasar esos datos despues del OTP.
- tool_build_handover_summary - Pre-Process ahora usa firstContent y no deja que {} bloquee el fallback al ultimo OCR valido.
- Las instrucciones del AI Agent indican que, tras OTP valido en un reclamo con comprobante, debe evaluar reglas, crear reclamo y llamar tool_build_handover_summary sin pedir confirmacion adicional.

Validacion:
- npm test OK, 33 passed.
- Public /health OK en https://fares-perhaps-participating-constraints.trycloudflare.com.
- Public /bank/mercantil/demo/handover-summary con claimId vacio + ticket OCR devolvio:
  Ticket: CECOSESOLA LARA EL C | 87079.04 VES | 04/07/2026 | tarjeta ****9956
  y customAttributes mc_merchant, mc_amount, mc_date, mc_time, mc_cardLast4, mc_ocrConfidence.
- Snapshot despues del primer patch Cognigy: post-ocr-ticket-resume-auto-otp-handover-20260711 / 6a52b742d9543729fbd68802.
- El snapshot final despues de agregar fallback en evaluate_rules/create_claim no se pudo crear porque Cognigy devolvio: "Limit of allowed Snapshots for this project has been exceeded". No borrar snapshots sin autorizacion explicita.
```

### 2026-07-11 - Rotacion de sesion WhatsApp y handover NiCE unico

Symptom:

```text
(a) Con el input transformer activo, MIA dejaba de responder (incluso a "Hola") en numeros que ya habian pasado por un handover NiCE; al desactivar el transformer volvia a responder. (b) Cada transferencia WhatsApp creaba DOS interacciones en NiCE: una con el resume OCR y otra con el transcript.
```

Cause:

```text
(a) El transformer fijaba sessionId = telefono y la sesion nunca rotaba; las sesiones con handovers NiCE sin resolver quedaban envenenadas para siempre (el flow corria pero el AI Agent fallaba en silencio por errorHandling=continue + errorMessage vacio + logErrorToSystem=false). Desactivar el transformer solo funcionaba porque el endpoint rotaba a una sesion UUID nueva. Ademas un guardado desde la UI de Cognigy republico una version vieja del transformer desde un buffer obsoleto del editor.
(b) El provider NiCE (integrado) crea el hilo DFO con idOnExternalPlatform = sessionId de Cognigy, mientras el resume del backend usaba thread = telefono: hilos distintos = contactos distintos. Antes coincidian de casualidad (sessionId == telefono). El router WhatsApp ademas duplicaba la ejecucion del handover en el mismo turno.
```

Solution:

```text
1. scripts/update_mercantil_whatsapp_transformer.js: sessionId = telefono-sufijo con rotacion tras 15 min de inactividad (marcadores en getSessionStorage(userId, userId)); userId sigue siendo el telefono. Publicar el transformer SOLO con este script; nunca guardarlo desde la UI de Cognigy.
2. scripts/patch_mercantil_single_whatsapp_handover.js: duerme el router (Detect no rutea, conserva el guard de completeHandover y consume mercantilHandoverReady) y activa logErrorToSystem en el AI Agent. El unico ejecutor de handover WhatsApp es tool_build_handover_summary - Execute NiCE Handover / 6a50edecd9543729fbcd68d6.
3. Prepare NiCE Handover envia threadId = input.sessionId y el backend /nice/resume-message lo usa como thread.idOnExternalPlatform DFO (fallback: telefono). Asi resume y handover comparten UN solo contacto NiCE. Log backend: threadIdSource=cognigy_session.
PRECAUCION: patch_mercantil_nice_resume_message.js tambien reescribe el Pre-Process con codigo ANTERIOR al de patch_mercantil_ocr_otp_auto_handover.js; no ejecutarlo completo sin reconciliar ese nodo (el parche en vivo se aplico solo al nodo Prepare).
Snapshots: pre 6a52d2205523ba36097bc19c / post 6a52da465523ba36097bfe92.
Validacion: npm test 34 OK; numeros antes atascados responden; transferencia OCR+OTP crea UNA interaccion NiCE.
Canal resuelto: el LLM inventaba channel="chat" como argumento del tool y args.channel tenia prioridad sobre la deteccion real en el Pre-Process, contaminando mc_channel/context. Ahora la prioridad es: isWhatsApp (payload real) > context.mercantilDetectedChannel > input.channel > args.channel > "webchat" (scripts/patch_mercantil_ocr_otp_auto_handover.js > handoverPreCode, aplicado en vivo al nodo 6a4e854ffac4430baeb3a582).
Nota de seguridad: credenciales NiCE hardcodeadas como defaults en src/config/env.js (commiteadas); se acepta por ser demo (decision 2026-07-11).
```
