Sari la conținutul principal
Dezvoltatori Webhook-uri

Postează mesaje cu un webhook

Un webhook este un URL care postează în comunitatea ta. Trimite-i JSON din orice poate face o cerere HTTP, de exemplu CI, monitorizare, un cron job sau un script, iar mesajul apare în canal.

Ce poți face

  • Postează text sau un cardText simplu sau un card cu titlu, culoare, markdown, câmpuri și imagini.
  • Atașează fișierePână la cinci fișiere per mesaj: loguri, rapoarte, capturi de ecran.
  • Adaugă butoaneLinkuri sau butoane care schimbă cardul ori ajung la serviciul tău.
  • Modifică-l mai târziuRăspunsul conține un URL de callback pentru a actualiza sau șterge mesajul.

În aplicație

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

O cerere din CI, un card în #deploys. Numele din partea de sus este numele pe care i l-ai dat webhook-ului.

Start rapid

  1. Creează webhook-ul

    În aplicația desktop, deschide în comunitatea ta Manage Server → Webhooks, creează un webhook, alege canalele în care are voie să posteze și copiază URL-ul pentru un canal. Arată așa:

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

    Semnează JSON-ul cu secretul webhook-ului și trimite-l cu POST. Un webhook creat în aplicația desktop are întotdeauna unul: copiază-l din Webhook Secret, în setările webhook-ului.

    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. Citește răspunsul

    Răspunsul îți dă id-ul mesajului și un callback_url cu care poți modifica mesajul mai târziu.

    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-..."
    }
Oricine are URL-ul și secretul poate posta în acel canal. Ține-le pe amândouă departe de repository-urile publice și de codul de pe partea clientului.

Ce poți trimite

Un mesaj este fie forma scurtă (text cu un titlu și o culoare), fie un card complet, iar oricare dintre ele poate avea butoane și fișiere. Numele din partea de sus a cardului este întotdeauna numele webhook-ului. Tot în setările webhook-ului decizi dacă poate posta imagini și menționa oameni.

Forma scurtă

Suficientă pentru majoritatea alertelor.

json
{
  "content": "Server backup completed at 03:00 UTC",
  "color": "green",
  "title": "Backup Bot"
}
CâmpTipCe face
contentstringTextul mesajului. Obligatoriu, cu excepția cazului în care trimiți un card sau fișiere.
colorstringblue (implicit), green, orange, red, yellow sau purple.
titlestringUn titlu deasupra textului. Implicit, numele webhook-ului.

Un card complet

Trimite un message_container pentru un card cu titlu cu link, subtitlu, markdown, câmpuri și imagini. Printr-un webhook, un card acceptă type, color, title, title_url, sub_title, description, fields, avatar_url, image_url, image_base64, images și câmpurile de loader. Eticheta de status, badge-ul, statisticile de diff și raționamentul pliat sunt pentru răspunsurile la comenzi. Toate câmpurile sunt descrise la carduri de mesaj.

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
Message from CI

Build-ul #1847 a trecut

Toate cele 212 teste sunt verzi pe main.
Duration
2m 34s
Commit
1a2b3c4

Butoane

Adaugă un array actions ca să pui butoane sub mesaj. Cum funcționează afli la butoane.

Fișiere

Postează fișiere reale împreună cu un mesaj: un log, un raport, o captură de ecran. Apar ca orice alt atașament, ca rând de descărcare sau direct în mesaj pentru imagini, video și audio. Un mesaj doar cu fișiere este în regulă: lasă deoparte content și cardul.

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"
    }
  ]
}
CâmpTipCe face
namestringNumele cu care se descarcă fișierul. Obligatoriu. O cale este redusă la ultima ei parte.
content_base64stringOcteții fișierului în base64, simplu sau ca URI data:. mssgs stochează fișierul și păstrează doar un link în mesaj.
mime_typestringTipul de conținut al content_base64. Implicit text/plain.
urlstringUn fișier găzduit deja pe mssgs: o cale /static/... sau un URL https://mss.gs/....
Trimite exact unul dintre content_base64 și url pentru fiecare fișier. Amândouă sau niciunul este o eroare.
LimităValoare
Fișiere per mesaj5
Dimensiune per fișier, după decodare8 MB
Numele fișierului200 de caractere
Întreaga cerereAproximativ 10 MB. Base64 face un fișier cu o treime mai mare, așa că un singur fișier de peste aproximativ 7 MB nu încape.

De ce url acceptă doar adrese mssgs

Un URL de webhook ajunge adesea lipit în alte dashboard-uri. Dacă scapă, nu trebuie să-i permită cuiva să facă aplicația fiecărui membru să descarce un fișier de pe un server ales de el. Dacă fișierul tău se află în altă parte, trimite-l ca content_base64, iar mssgs îl găzduiește.

O încărcare eșuată nu blochează mesajul

Fișierele sunt verificate de la început, dar încărcate după aceea. Dacă o încărcare eșuează, fișierul respectiv este omis, iar restul mesajului se postează oricum, fără eroare: e mai bine să pierzi fișierul decât raportul. Dacă un fișier contează, verifică dacă a ajuns.

Un fișier din linia de comandă

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 @-

Semnarea cererilor

Un webhook cu secret acceptă doar cereri care dovedesc că îl cunosc, iar un webhook creat în aplicația desktop are întotdeauna unul (Webhook Secret în setările lui). Semnează corpul brut al cererii cu HMAC-SHA256 folosind secretul și trimite digestul hex cu litere mici în antetul X-Mssgs-Signature, sub forma 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
});
O cerere fără o semnătură validă primește 401 și {"error": "INVALID_SIGNATURE"}. Doar un webhook fără secret, de exemplu unul creat prin MCP fără webhook_secret, acceptă cereri nesemnate.

Este acceptat și antetul propriu al GitHub, X-Hub-Signature-256, așa că un webhook GitHub cu același secret funcționează ca atare. Semnătura nu are timestamp, deci nu împiedică retrimiterea unei cereri interceptate: URL-ul rămâne secretul care contează.

Răspunsuri și erori

Un mesaj postat se întoarce cu id-ul lui și un callback_url cu care îl poți actualiza sau șterge timp de 30 de minute.

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-..."
}
Citește corpul răspunsului, nu doar statusul. Un payload respins își dă motivul în corp, ca un cod error. Tratează orice corp cu o cheie error ca pe un eșec, indiferent de codul de status.
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}`);
}

Când mesajul are butoane, răspunsul conține și un stream_url: un flux live cu răspunsurile, reacțiile și apăsările de butoane de pe acel mesaj, deschis 10 minute sau o oră dacă trimiți "sse_event_extended_timeout": true. Vezi actualizări live.

Coduri de status

StatusCând
401Webhook-ul are un secret, iar semnătura lipsește sau este greșită.
403Acest webhook nu are voie să posteze în acel canal.
404Nu există niciun webhook la acest URL.
413Cererea este prea mare.
429Prea multe cereri. Încetinește și încearcă din nou.
502Mesajul nu a putut fi livrat. Încearcă din nou.

Coduri de eroare

CodSemnificație
MISSING_CONTENTNimic de postat: niciun text, niciun card și niciun fișier.
INVALID_MESSAGE_CONTAINERmessage_container nu este un obiect.
INVALID_MESSAGE_CONTAINER_TYPETipul cardului nu este embed_message sau system_message.
MISSING_MESSAGE_CONTAINER_DESCRIPTIONUn card are nevoie de o descriere, cu excepția cazului în care este un loader.
MISSING_MESSAGE_CONTAINER_LOADER_TEXTUn card de tip loader are nevoie de loader_text.
INVALID_WEBHOOK_BINDINGAcest webhook nu are voie să posteze în acel canal.
INVALID_SIGNATUREAntetul de semnătură lipsește sau este greșit.
REQUEST_BODY_TOO_LARGECererea depășește limita de dimensiune.
PUBLISH_FAILEDMesajul nu a putut fi livrat.
INVALID_ACTIONS_FORMAT, INVALID_ACTION_MISSING_FIELDS, DUPLICATE_ACTION_ID, INVALID_TRIGGERS_FORMAT, INVALID_TRIGGER_MISSING_ACTION, INVALID_TRIGGER_ACTION_NOT_ALLOWEDCeva nu este în regulă cu un buton. Vezi butoane.

Erori de fișiere

CodSemnificație
INVALID_ATTACHMENTS_FORMATattachments nu este o listă sau o intrare nu este un obiect.
TOO_MANY_ATTACHMENTSMai mult de cinci fișiere.
MISSING_ATTACHMENT_NAMEUn fișier nu are nume.
INVALID_ATTACHMENT_NAMEDin nume nu rămâne nimic utilizabil, de exemplu ...
MISSING_ATTACHMENT_SOURCENici url, nici content_base64.
AMBIGUOUS_ATTACHMENT_SOURCEAtât url, cât și content_base64.
INVALID_ATTACHMENT_BASE64Base64-ul nu poate fi decodat.
ATTACHMENT_TOO_LARGEUn fișier are peste 8 MB după decodare.
INVALID_ATTACHMENT_URLurl nu este o adresă mssgs.

Limite

LimităValoare
Dimensiunea cereriiAproximativ 10 MB
Fișiere per mesaj5, de până la 8 MB fiecare
Descrierea carduluiPână la 50.000 de octeți. Peste 1.000 de octeți, membrii văd începutul și un buton Show more.
Actualizarea mesajului după postare30 de minute, prin callback_url
Fluxul live al unui mesaj cu butoane10 minute sau o oră, la cerere

Cererile au o limită de frecvență. Când primești un 429, așteaptă înainte să trimiți din nou și grupează alertele care vin în rafale într-un singur mesaj.

GitHub, UniFi și App Store Connect

Îndreaptă unul dintre aceste servicii spre un URL de webhook, iar mssgs îl recunoaște și postează un card ca lumea, fără să scrii vreun payload. Vezi integrări. Acestea primesc ca răspuns {"success": true} și niciun URL de callback.

SursăRecunoscut dupăCe postează
GitHubAntetul x-github-eventPush-uri, pull request-uri și review-uri, issue-uri și comentarii, branch-uri și taguri, release-uri. O rafală de modificări la același issue sau pull request este adunată într-un singur card.
UniFi ProtectUser agent-ul protect-alarm-managerSonerii, mișcare, precum și persoane, vehicule sau colete detectate de camerele tale.
App Store ConnectCorpul notificărilor lui sau antetul x-apple-signatureNotificări App Store Connect.

Construiește mai departe