---
title: "Actualizaciones en directo: cambia mensajes de mssgs publicados"
description: "Actualiza o borra un mensaje de mssgs ya publicado con su callback URL, muestra el progreso con un loader y escucha en directo respuestas, reacciones y botones."
canonical: https://docs.mss.gs/es/live-updates
language: es
---

# Actualiza mensajes en directo

Un mensaje no tiene por qué quedarse como se publicó. Muestra el progreso mientras se ejecuta un trabajo, cambia un loader por el resultado, quita los botones cuando alguien ya ha decidido, o bórralo. Todos en el canal ven el cambio al instante.

## Qué puedes hacer

- **Actualiza la tarjeta** Cambia el texto, el color y los botones, en el mismo sitio.

- **Muestra el progreso** Un loader que avanza por los pasos y después el resultado.

- **Bórralo** Quita un mensaje cuando ya no es cierto.

- **Escúchalo** Respuestas, reacciones y pulsaciones de botón en tu mensaje, en directo.

En la app

Publicada con un loader

Actualizada: paso 2 de 3

#### Despliegue completado

Actualizada: hecho, con un botón

Un mensaje, actualizado dos veces a través de su callback URL. Nadie ve tres mensajes, solo uno que cambia.

- [Inicio rápido](#quick-start)

- [Actualizar](#update)

- [Mostrar el progreso](#progress)

- [Borrar](#delete)

- [Escuchar](#stream)

## Inicio rápido

### Guarda la callback URL

Cada publicación de [webhook](https://docs.mss.gs/es/webhooks) y cada petición de [comando](https://docs.mss.gs/es/commands) trae una callback_url para ese mensaje.

```bash
BODY='{"message_container": {"color": "blue", "loader": true, "loader_text": "Deploying…"}}'
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$MSSGS_WEBHOOK_SECRET" | sed 's/^.* //')

curl -X POST "$MSSGS_WEBHOOK_URL" -H "Content-Type: application/json" \
  -H "X-Mssgs-Signature: sha256=$SIG" -d "$BODY"

# {"success": true, "message_id": "...", "callback_url": "https://mss.gs/api/v1/trigger-callback/8f14e45f-..."}
```

### PUT para actualizar

Envía el nuevo estado. La tarjeta cambia en el mismo sitio para todos.

```bash
curl -X PUT "$CALLBACK_URL" -H "Content-Type: application/json" \
  -d '{"message_container": {"color": "green", "title": "Deploy complete", "description": "v2.1 is live."}}'
```

### DELETE para borrar

No hace falta cuerpo.

```bash
curl -X DELETE "$CALLBACK_URL"
```

## Actualizar un mensaje

Envía JSON con PUT a la callback_url . Envía solo lo que quieras cambiar.

| Campo | Tipo | Qué hace |
| --- | --- | --- |
| message_container | object | La nueva tarjeta. Consulta [tarjetas de mensaje](https://docs.mss.gs/es/bots). |
| actions | array | Botones nuevos. "actions": [] los quita todos; si omites el campo, se mantienen. |
| content | string | Texto nuevo. |
| title , description , color , loader , ... | string | Atajo: los campos de tarjeta en el nivel superior se envuelven en una tarjeta por ti. |

### Respuestas

| Estado | Código | Significado |
| --- | --- | --- |
| 200 | {"success": true} | Aceptado. La actualización llega justo después. |
| 400 | MISSING_FIELDS | No hay nada que actualizar en el cuerpo. |
| 400 | INVALID_BODY | El cuerpo no es JSON válido. |
| 400 | un código de botón | Algo falla en un botón, consulta [botones](https://docs.mss.gs/es/buttons). |
| 401 | INVALID_TOKEN | La URL no es válida. |
| 404 | TOKEN_NOT_FOUND | La URL ha caducado o se ha usado para borrar el mensaje. |
| 502 | PUBLISH_FAILED | No se ha podido entregar la actualización. Vuelve a intentarlo. |

### Cuánto tiempo funciona

30 minutos desde el momento en que se publicó el mensaje o se usó el comando. Actualizar no lo alarga. Borrar el mensaje agota la URL. No se pueden añadir archivos con una actualización.

## Mostrar el progreso

Publica una tarjeta con un loader, actualiza su texto secundario a medida que avanza el trabajo y termina con el resultado. El loader es un spinner con una línea y otra más pequeña debajo ( loader_text , loader_sub_text ).

```javascript
const { callback_url } = await post({
  message_container: { color: 'blue', loader: true, loader_text: 'Deploying…', loader_sub_text: 'Step 1 of 3: building' }
});

const update = (body) => {
  return fetch(callback_url, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
};

await build();
await update({ message_container: { color: 'blue', loader: true, loader_text: 'Deploying…', loader_sub_text: 'Step 2 of 3: running migrations' } });

await migrate();
await update({
  message_container: { color: 'green', title: 'Deploy complete', description: 'v2.1 is live on production.' },
  actions: [{ type: 'url:https://ci.example.com/deploys/218', text: 'View logs', color: 'green' }]
});
```

## Borrar un mensaje

Envía DELETE a la callback_url y el mensaje desaparece para todos. Después, la URL ya no se puede volver a usar.

## Escuchar un mensaje

Una stream_url es un stream en directo (Server-Sent Events) de lo que pasa en tu mensaje. Ábrelo y los eventos llegan en cuanto ocurren, cada uno como una línea JSON data: cuyo type dice qué es:

```sse
curl -N "$STREAM_URL"

data: {"type": "reaction", "message_id": "...", "emoji": ":tada:", "action": "add", "member_guid": "...", "member": {...}, "ts": 1790000000000}

data: {"type": "action", "message_id": "...", "action_id": "approve", "action": {"id": "approve", "payload": {"deploy": 218}, ...}, "member_guid": "...", "member": {...}, "ts": 1790000004200}

data: {"type": "reply", "message_id": "...", "content": "Ship it!", "member_guid": "...", "member": {...}, "ts": 1790000009800}

event: expired
data: {}
```

| Evento | Cuándo | Datos |
| --- | --- | --- |
| action | Se ha pulsado un botón. | action_id , y el botón guardado en action con su payload |
| reaction | Se ha añadido o quitado una reacción. | emoji , y action : add, remove o removeall |
| reply | Alguien ha respondido al mensaje. | El content de la respuesta |
| expired | El stream se está cerrando. Se envía como evento con nombre. | Ninguno |

### Escuchar en JavaScript

```javascript
const events = new EventSource(streamUrl);

// Every event arrives as a plain message; its kind is in "type".
events.onmessage = (e) => {
  const ev = JSON.parse(e.data);
  if ((ev.type === 'action') && (ev.action_id === 'approve')) {
    startDeploy(ev.action.payload.deploy);
  }
};

// The one named event: the stream is closing.
events.addEventListener('expired', () => {
  events.close();
});
```

### De dónde sacas una

| De dónde sale | Abierto durante |
| --- | --- |
| Una publicación de webhook con botones | 10 minutos, o una hora con "sse_event_extended_timeout": true |
| Cada petición de comando | 10 minutos |

### Errores

| Estado | Código | Significado |
| --- | --- | --- |
| 401 | INVALID_TOKEN | El token de la URL es incorrecto. |
| 404 | NOT_FOUND | El stream ha caducado o nunca existió. |

## Sigue construyendo
