Vai al contenuto principale
Sviluppatori Webhook

Pubblica messaggi con un webhook

Un webhook è un URL che pubblica nella tua community. Mandagli del JSON da qualsiasi cosa sappia fare una richiesta HTTP, come la CI, il monitoraggio, un cron job o uno script, e il messaggio compare nel canale.

Cosa puoi fare

  • Pubblica testo o una cardTesto semplice, oppure una card con titolo, colore, markdown, campi e immagini.
  • Allega fileFino a cinque file per messaggio: log, report, screenshot.
  • Aggiungi pulsantiLink, oppure pulsanti che cambiano la card o raggiungono il tuo servizio.
  • Cambialo dopoLa risposta contiene una callback URL per aggiornare o eliminare il messaggio.

Nell'app

$ curl -X POST "$MSSGS_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-H "X-Mssgs-Signature: sha256=$SIG" \
-d '{"message_container": {"title": "Build #1847 riuscita", ...}}'
{"success": true, "message_id": "aZZ1a2b-..."}

Una richiesta dalla CI, una card in #deploys. Il nome in alto è quello che hai dato al webhook.

Per iniziare

  1. Crea il webhook

    Nell’app desktop apri Gestisci il server → Webhook della tua community, crea un webhook, scegli i canali in cui può pubblicare e copia l’URL per un canale. Ha questa forma:

    url
    https://mss.gs/api/v1/webhook/{webhook_guid}/{token}/{channel_guid}
  2. Invia un messaggio

    Firma il JSON con il segreto del webhook e invialo in POST. Un webhook creato nell’app desktop ne ha sempre uno: copialo da Segreto del webhook nelle impostazioni del webhook.

    bash
    BODY='{"content": "Build #1847 passed on main"}'
    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"
  3. Leggi la risposta

    La risposta ti dà l’id del messaggio e una callback_url per cambiare il messaggio in seguito.

    json
    {
      "success": true,
      "message_id": "aZZ1a2b-...",
      "callback_url": "https://mss.gs/api/v1/trigger-callback/8f14e45f-...",
      "stream_url": "https://mss.gs/api/v1/instant/6b1e...c0?token=a1b2c3d4-..."
    }
Chiunque abbia l’URL e il segreto può pubblicare in quel canale. Tienili entrambi fuori dai repository pubblici e dal codice lato client.

Cosa puoi inviare

Un messaggio è la forma breve (testo con un titolo e un colore) oppure una card completa, e in entrambi i casi può avere pulsanti e file. Il nome in cima alla card è sempre il nome del webhook. Nelle impostazioni del webhook decidi anche se può pubblicare immagini e menzionare persone.

Forma breve

Basta per la maggior parte degli avvisi.

json
{
  "content": "Server backup completed at 03:00 UTC",
  "color": "green",
  "title": "Backup Bot"
}
CampoTipoCosa fa
contentstringIl testo del messaggio. Obbligatorio, a meno che tu non invii una card o dei file.
colorstringblue (predefinito), green, orange, red, yellow o purple.
titlestringUn titolo sopra il testo. Se manca, è il nome del webhook.

Una card completa

Invia un message_container per una card con titolo linkato, sottotitolo, markdown, campi e immagini. Tramite un webhook una card accetta type, color, title, title_url, sub_title, description, fields, avatar_url, image_url, image_base64, images e i campi del loader. La pillola di stato, il badge, le statistiche diff e il ragionamento ripiegato sono per le risposte ai comandi. Ogni campo è descritto in card dei messaggi.

json
{
  "message_container": {
    "type": "embed_message",
    "color": "green",
    "title": "Build #1847 passed",
    "title_url": "https://ci.example.com/builds/1847",
    "description": "All 212 tests green on **main**.",
    "fields": [
      { "field": "Duration", "value": "2m 34s" },
      { "field": "Commit", "value": "1a2b3c4" }
    ]
  }
}
deploys
System
Messaggio da CI

Build #1847 riuscita

Tutti i 212 test verdi su main.
Duration
2m 34s
Commit
1a2b3c4

Pulsanti

Aggiungi un array actions per mettere dei pulsanti sotto il messaggio. Come funzionano è spiegato in pulsanti.

File

Pubblica file veri con un messaggio: un log, un report, uno screenshot. Vengono mostrati come qualsiasi altro allegato, come riga da scaricare oppure inline per immagini, video e audio. Un messaggio con soli file va bene: ometti content e la card.

json
{
  "message_container": {
    "color": "orange",
    "title": "Log dump: ios",
    "description": "DMs stopped arriving after switching networks"
  },
  "attachments": [
    {
      "name": "mssgs-logs-20260803-141205.log",
      "content_base64": "MjAyNi0wOC0wMyAxNDoxMjowNSBbV1NdIGNvbm5lY3RlZAo=",
      "mime_type": "text/plain"
    }
  ]
}
CampoTipoCosa fa
namestringIl nome con cui il file viene scaricato. Obbligatorio. Un percorso viene ridotto alla sua ultima parte.
content_base64stringI byte del file in base64, grezzi o come URI data:. mssgs salva il file e sul messaggio tiene solo un link.
mime_typestringIl content type di content_base64. Predefinito: text/plain.
urlstringUn file già ospitato su mssgs: un percorso /static/... o un URL https://mss.gs/....
Invia esattamente uno tra content_base64 e url per ogni file. Entrambi, o nessuno dei due, è un errore.
LimiteValore
File per messaggio5
Dimensione per file, dopo la decodifica8 MB
Nome del file200 caratteri
Richiesta interaCirca 10 MB. Il base64 rende un file più grande di un terzo, quindi un singolo file oltre i 7 MB circa non ci sta.

Perché url accetta solo indirizzi mssgs

L’URL di un webhook finisce spesso incollato in altre dashboard. Se trapela, non deve permettere a qualcuno di far scaricare all’app di ogni membro un file da un server scelto da lui. Se il tuo file si trova altrove, invialo come content_base64 e lo ospita mssgs.

Un caricamento fallito non fa fallire il messaggio

I file vengono controllati subito ma caricati dopo. Se un caricamento fallisce, quel file viene tralasciato e il resto del messaggio viene comunque pubblicato, senza errori: meglio perdere il file che perdere il report. Se un file è importante, controlla che sia arrivato.

Un file dalla riga di comando

bash
BODY='{"attachments":[{"name":"report.csv","content_base64":"'"$(base64 < report.csv | tr -d '\n')"'","mime_type":"text/csv"}]}'
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$MSSGS_WEBHOOK_SECRET" | sed 's/^.* //')

printf '%s' "$BODY" | curl -sS -X POST "$MSSGS_WEBHOOK_URL" \
  -H 'Content-Type: application/json' \
  -H "X-Mssgs-Signature: sha256=$SIG" \
  --data-binary @-

Firmare le richieste

Un webhook con un segreto accetta solo le richieste che dimostrano di conoscerlo, e un webhook creato nell’app desktop ne ha sempre uno (Segreto del webhook nelle sue impostazioni). Firma il body grezzo della richiesta con HMAC-SHA256 usando il segreto e invia il digest esadecimale in minuscolo nell’header X-Mssgs-Signature, come sha256=<hex>.

javascript
import crypto from 'node:crypto';

const body = JSON.stringify({ content: 'Deploy finished' });
const signature = crypto.createHmac('sha256', process.env.MSSGS_WEBHOOK_SECRET)
  .update(body)
  .digest('hex');

await fetch(process.env.MSSGS_WEBHOOK_URL, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Mssgs-Signature': `sha256=${signature}`
  },
  body
});
Una richiesta senza una firma valida riceve 401 e {"error": "INVALID_SIGNATURE"}. Solo un webhook senza segreto, per esempio creato via MCP senza webhook_secret, accetta richieste non firmate.

Viene accettato anche l’header X-Hub-Signature-256 di GitHub, quindi un webhook GitHub con lo stesso segreto funziona così com’è. La firma non ha un timestamp, quindi non impedisce che una richiesta intercettata venga inviata di nuovo: il segreto che conta resta l’URL.

Risposte ed errori

Un messaggio pubblicato torna indietro con il suo id e una callback_url per aggiornarlo o eliminarlo per 30 minuti.

json
{
  "success": true,
  "message_id": "aZZ1a2b-...",
  "callback_url": "https://mss.gs/api/v1/trigger-callback/8f14e45f-...",
  "stream_url": "https://mss.gs/api/v1/instant/6b1e...c0?token=a1b2c3d4-..."
}
Leggi il body, non solo lo status. Un payload rifiutato riporta il motivo come codice error nel body. Considera un fallimento qualsiasi body con una chiave error, qualunque sia lo status code.
http
HTTP/1.1 200 OK
Content-Type: application/json

{ "error": "ATTACHMENT_TOO_LARGE" }
javascript
const res = await fetch(webhookUrl, { method: 'POST', headers, body });
const reply = await res.json().catch(() => null);

// A rejected payload still comes back as 200: read the body.
if (!res.ok || (reply && reply.error)) {
  throw new Error(`webhook rejected: ${reply?.error ?? res.status}`);
}

Quando il messaggio ha dei pulsanti, la risposta contiene anche uno stream_url: uno stream live delle risposte, delle reazioni e delle pressioni dei pulsanti su quel messaggio, aperto per 10 minuti, oppure per un’ora se invii "sse_event_extended_timeout": true. Vedi aggiornamenti live.

Status code

StatusQuando
401Il webhook ha un segreto e la firma manca o è sbagliata.
403Questo webhook non può pubblicare in quel canale.
404Non c’è nessun webhook a questo URL.
413La richiesta è troppo grande.
429Troppe richieste. Rallenta e riprova.
502Non è stato possibile consegnare il messaggio. Riprova.

Codici di errore

CodiceSignificato
MISSING_CONTENTNiente da pubblicare: né testo, né card, né file.
INVALID_MESSAGE_CONTAINERmessage_container non è un oggetto.
INVALID_MESSAGE_CONTAINER_TYPEIl tipo della card non è né embed_message né system_message.
MISSING_MESSAGE_CONTAINER_DESCRIPTIONUna card ha bisogno di una descrizione, a meno che non sia un loader.
MISSING_MESSAGE_CONTAINER_LOADER_TEXTUna card con loader ha bisogno di loader_text.
INVALID_WEBHOOK_BINDINGQuesto webhook non può pubblicare in quel canale.
INVALID_SIGNATUREL’header della firma manca o è sbagliato.
REQUEST_BODY_TOO_LARGELa richiesta supera il limite di dimensione.
PUBLISH_FAILEDNon è stato possibile consegnare il messaggio.
INVALID_ACTIONS_FORMAT, INVALID_ACTION_MISSING_FIELDS, DUPLICATE_ACTION_ID, INVALID_TRIGGERS_FORMAT, INVALID_TRIGGER_MISSING_ACTION, INVALID_TRIGGER_ACTION_NOT_ALLOWEDC’è qualcosa che non va in un pulsante. Vedi pulsanti.

Errori dei file

CodiceSignificato
INVALID_ATTACHMENTS_FORMATattachments non è una lista, oppure una voce non è un oggetto.
TOO_MANY_ATTACHMENTSPiù di cinque file.
MISSING_ATTACHMENT_NAMEUn file non ha un nome.
INVALID_ATTACHMENT_NAMEIl nome si riduce a niente di utilizzabile, come ...
MISSING_ATTACHMENT_SOURCENé url né content_base64.
AMBIGUOUS_ATTACHMENT_SOURCESia url sia content_base64.
INVALID_ATTACHMENT_BASE64Il base64 non si decodifica.
ATTACHMENT_TOO_LARGEUn file supera gli 8 MB dopo la decodifica.
INVALID_ATTACHMENT_URLL’url non è un indirizzo mssgs.

Limiti

LimiteValore
Dimensione della richiestaCirca 10 MB
File per messaggio5, fino a 8 MB ciascuno
Descrizione della cardFino a 50.000 byte. Oltre i 1.000 byte, i membri vedono l’inizio e un pulsante Mostra di più.
Aggiornare il messaggio in seguito30 minuti, tramite callback_url
Stream live di un messaggio con pulsanti10 minuti, oppure un’ora su richiesta

Le richieste hanno un rate limit. Quando ricevi un 429, aspetta prima di inviare di nuovo, e raggruppa in un solo messaggio gli avvisi che arrivano a raffica.

GitHub, UniFi e App Store Connect

Punta uno di questi servizi all’URL di un webhook e mssgs lo riconosce e pubblica una card fatta apposta, senza payload da scrivere. Vedi integrazioni. Questi rispondono con {"success": true} e senza callback URL.

FonteRiconosciuta daCosa pubblica
GitHubL’header x-github-eventPush, pull request e revisioni, issue e commenti, branch e tag, release. Una raffica di modifiche a una stessa issue o pull request viene raccolta in una sola card.
UniFi ProtectLo user agent protect-alarm-managerIl campanello che suona, il movimento, e persone, veicoli o pacchi rilevati dalle tue telecamere.
App Store ConnectIl body della sua notifica o l’header x-apple-signatureLe notifiche di App Store Connect.

Continua a costruire