Ga naar hoofdinhoud
Developers Webhooks

Berichten posten met een webhook

Een webhook is een URL die in je community post. Stuur er JSON naartoe vanuit alles wat een HTTP-request kan doen, zoals CI, monitoring, een cronjob of een script, en het bericht verschijnt in het kanaal.

Wat je kunt doen

  • Tekst of een kaart postenPlatte tekst, of een kaart met een titel, een kleur, markdown, velden en afbeeldingen.
  • Bestanden bijvoegenTot vijf bestanden per bericht: logs, rapporten, screenshots.
  • Knoppen toevoegenLinks, of knoppen die de kaart veranderen of je service aanroepen.
  • Later aanpassenHet antwoord bevat een callback-URL om het bericht bij te werken of te verwijderen.

In de app

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

Eén request vanuit CI, één kaart in #deploys. De naam bovenaan is de naam die je de webhook hebt gegeven.

Snel aan de slag

  1. Maak de webhook aan

    Open in de desktop-app bij je community Server beheren → Webhooks, maak een webhook aan, kies de kanalen waarin hij mag posten en kopieer de URL voor een kanaal. Die ziet er zo uit:

    url
    https://mss.gs/api/v1/webhook/{webhook_guid}/{token}/{channel_guid}
  2. Stuur een bericht

    Onderteken de JSON met het secret van de webhook en POST hem. Een webhook die je in de desktop-app maakt, heeft er altijd een: kopieer hem bij Webhook Secret in de instellingen van de 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. Lees het antwoord

    Het antwoord geeft je het bericht-id, en een callback_url om het bericht later aan te passen.

    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-..."
    }
Iedereen die de URL en het secret heeft, kan in dat kanaal posten. Houd ze allebei uit publieke repositories en uit code die in de browser of app draait.

Wat je kunt sturen

Een bericht is de korte vorm (tekst met een titel en een kleur) of een volledige kaart, en allebei kunnen ze knoppen en bestanden meekrijgen. De naam bovenaan de kaart is altijd de eigen naam van de webhook. In de instellingen van de webhook bepaal je ook of hij afbeeldingen mag posten en mensen mag noemen.

Korte vorm

Genoeg voor de meeste meldingen.

json
{
  "content": "Server backup completed at 03:00 UTC",
  "color": "green",
  "title": "Backup Bot"
}
VeldTypeWat het doet
contentstringDe berichttekst. Verplicht, tenzij je een kaart of bestanden stuurt.
colorstringblue (standaard), green, orange, red, yellow of purple.
titlestringEen titel boven de tekst. Standaard de naam van de webhook.

Een volledige kaart

Stuur een message_container voor een kaart met een gelinkte titel, een subtitel, markdown, velden en afbeeldingen. Via een webhook kent een kaart type, color, title, title_url, sub_title, description, fields, avatar_url, image_url, image_base64, images en de loadervelden. De statuspill, de badge, de diff-stats en de ingeklapte redenering zijn voor antwoorden op commando's. Elk veld staat bij berichtkaarten.

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
Bericht van CI

Build #1847 geslaagd

Alle 212 tests groen op main.
Duration
2m 34s
Commit
1a2b3c4

Knoppen

Voeg een actions-array toe om knoppen onder het bericht te zetten. Hoe ze werken lees je bij knoppen.

Bestanden

Post echte bestanden bij een bericht: een log, een rapport, een screenshot. Ze verschijnen zoals elke andere bijlage, als downloadregel of, bij afbeeldingen, video en audio, direct in het bericht. Een bericht met alleen bestanden mag ook: laat content en de kaart weg.

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"
    }
  ]
}
VeldTypeWat het doet
namestringDe bestandsnaam waaronder het wordt gedownload. Verplicht. Van een pad blijft alleen het laatste deel over.
content_base64stringDe bytes van het bestand als base64, kaal of als data:-URI. mssgs slaat het bestand op en bewaart alleen een link op het bericht.
mime_typestringHet content type van content_base64. Standaard text/plain.
urlstringEen bestand dat al op mssgs staat: een /static/...-pad of een https://mss.gs/...-URL.
Stuur per bestand precies één van content_base64 of url. Allebei, of geen van beide, is een fout.
LimietWaarde
Bestanden per bericht5
Grootte per bestand, na decoderen8 MB
Bestandsnaam200 tekens
Het hele requestOngeveer 10 MB. Base64 maakt een bestand een derde groter, dus één bestand van meer dan zo'n 7 MB past er niet in.

Waarom url alleen mssgs-adressen accepteert

Een webhook-URL wordt vaak in andere dashboards geplakt. Een gelekte URL mag iemand niet de kans geven om de app van elk lid een bestand te laten ophalen van een server die hij zelf kiest. Staat je bestand ergens anders, stuur het dan als content_base64 en mssgs host het voor je.

Een mislukte upload laat het bericht niet mislukken

Bestanden worden vooraf gecontroleerd, maar pas daarna geüpload. Mislukt een upload, dan valt dat bestand weg en wordt de rest van het bericht gewoon gepost, zonder foutmelding: het bestand kwijtraken is beter dan het hele rapport kwijtraken. Is een bestand belangrijk, controleer dan of het is aangekomen.

Een bestand vanaf de commandline

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

Requests ondertekenen

Een webhook met een secret accepteert alleen requests die bewijzen dat ze dat secret kennen, en een webhook die je in de desktop-app maakt, heeft er altijd een (Webhook Secret in zijn instellingen). Onderteken de ruwe request body met HMAC-SHA256 en het secret, en stuur de hex-digest in kleine letters mee in de header X-Mssgs-Signature, als 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
});
Een request zonder geldige handtekening krijgt 401 en {"error": "INVALID_SIGNATURE"}. Alleen een webhook zonder secret, zoals een die via MCP is aangemaakt zonder webhook_secret, accepteert requests zonder handtekening.

De eigen header X-Hub-Signature-256 van GitHub wordt ook geaccepteerd, dus een GitHub-webhook met hetzelfde secret werkt meteen. De handtekening bevat geen tijdstempel en houdt dus niet tegen dat een onderschept request opnieuw wordt verstuurd: de URL blijft het geheim waar het om draait.

Antwoorden en fouten

Een bericht dat is gepost, komt terug met zijn id en een callback_url om het 30 minuten lang bij te werken of te verwijderen.

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-..."
}
Lees de body, niet alleen de status. Een geweigerde payload geeft de reden als error-code in de body. Behandel elke body met een error-sleutel als een mislukking, wat de statuscode ook is.
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}`);
}

Heeft het bericht knoppen, dan bevat het antwoord ook een stream_url: een live stream van de antwoorden, reacties en knopdrukken op dat bericht, 10 minuten open, of een uur als je "sse_event_extended_timeout": true meestuurt. Zie live-updates.

Statuscodes

StatusWanneer
401De webhook heeft een secret en de handtekening ontbreekt of klopt niet.
403Deze webhook mag niet in dat kanaal posten.
404Er is geen webhook op deze URL.
413Het request is te groot.
429Te veel requests. Doe het rustiger aan en probeer het opnieuw.
502Het bericht kon niet worden afgeleverd. Probeer het opnieuw.

Foutcodes

CodeBetekenis
MISSING_CONTENTNiets om te posten: geen tekst, geen kaart en geen bestanden.
INVALID_MESSAGE_CONTAINERmessage_container is geen object.
INVALID_MESSAGE_CONTAINER_TYPEHet kaarttype is niet embed_message of system_message.
MISSING_MESSAGE_CONTAINER_DESCRIPTIONEen kaart heeft een beschrijving nodig, tenzij het een loader is.
MISSING_MESSAGE_CONTAINER_LOADER_TEXTEen loaderkaart heeft loader_text nodig.
INVALID_WEBHOOK_BINDINGDeze webhook mag niet in dat kanaal posten.
INVALID_SIGNATUREDe handtekening-header ontbreekt of klopt niet.
REQUEST_BODY_TOO_LARGEHet request is groter dan de limiet.
PUBLISH_FAILEDHet bericht kon niet worden afgeleverd.
INVALID_ACTIONS_FORMAT, INVALID_ACTION_MISSING_FIELDS, DUPLICATE_ACTION_ID, INVALID_TRIGGERS_FORMAT, INVALID_TRIGGER_MISSING_ACTION, INVALID_TRIGGER_ACTION_NOT_ALLOWEDEr klopt iets niet aan een knop. Zie knoppen.

Fouten bij bestanden

CodeBetekenis
INVALID_ATTACHMENTS_FORMATattachments is geen lijst, of een item is geen object.
TOO_MANY_ATTACHMENTSMeer dan vijf bestanden.
MISSING_ATTACHMENT_NAMEEen bestand heeft geen naam.
INVALID_ATTACHMENT_NAMEVan de naam blijft niets bruikbaars over, zoals bij ...
MISSING_ATTACHMENT_SOURCEGeen url en geen content_base64.
AMBIGUOUS_ATTACHMENT_SOURCEZowel url als content_base64.
INVALID_ATTACHMENT_BASE64De base64 is niet te decoderen.
ATTACHMENT_TOO_LARGEEen bestand is na decoderen groter dan 8 MB.
INVALID_ATTACHMENT_URLDe url is geen mssgs-adres.

Limieten

LimietWaarde
Grootte van een requestOngeveer 10 MB
Bestanden per bericht5, van maximaal 8 MB per stuk
Beschrijving van een kaartTot 50.000 bytes. Voorbij 1.000 bytes zien leden het begin en een knop Meer tonen.
Het bericht achteraf bijwerken30 minuten, via callback_url
Live stream van een bericht met knoppen10 minuten, of een uur op verzoek

Requests hebben een rate limit. Krijg je een 429, wacht dan voordat je opnieuw stuurt, en bundel meldingen die in golven binnenkomen tot één bericht.

GitHub, UniFi en App Store Connect

Wijs een van deze services naar een webhook-URL en mssgs herkent hem en post een nette kaart, zonder dat je zelf een payload hoeft te schrijven. Zie integraties. Deze antwoorden met {"success": true} en zonder callback-URL.

BronHerkend aanWat het post
GitHubDe header x-github-eventPushes, pull requests en reviews, issues en reacties, branches en tags, releases. Een reeks wijzigingen aan één issue of pull request wordt samengevoegd tot één kaart.
UniFi ProtectDe user agent protect-alarm-managerAanbellen, beweging, en mensen, voertuigen of pakketten die je camera's zien.
App Store ConnectDe body van de melding of de header x-apple-signatureMeldingen van App Store Connect.

Verder bouwen