Zum Hauptinhalt springen
Entwickler Webhooks

Nachrichten per Webhook posten

Ein Webhook ist eine URL, die in deine Community postet. Schick ihr JSON aus allem, was einen HTTP-Request senden kann, etwa CI, Monitoring, ein Cronjob oder ein Skript, und die Nachricht erscheint im Kanal.

Was du damit machen kannst

  • Text oder eine Karte postenReiner Text oder eine Karte mit Titel, Farbe, Markdown, Feldern und Bildern.
  • Dateien anhängenBis zu fünf Dateien pro Nachricht: Logs, Berichte, Screenshots.
  • Buttons hinzufügenLinks oder Buttons, die die Karte ändern oder deinen Dienst erreichen.
  • Später ändernDie Antwort enthält eine Callback-URL, mit der du die Nachricht aktualisierst oder löschst.

In der App

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

Ein Request aus CI, eine Karte in #deploys. Der Name oben ist der Name, den du dem Webhook gegeben hast.

Schnellstart

  1. Webhook anlegen

    Öffne in der Desktop-App bei deiner Community Server verwalten → Webhooks, leg einen Webhook an, wähl die Kanäle, in die er posten darf, und kopier die URL für einen Kanal. Sie sieht so aus:

    url
    https://mss.gs/api/v1/webhook/{webhook_guid}/{token}/{channel_guid}
  2. Eine Nachricht senden

    Signiere das JSON mit dem Secret des Webhooks und sende es per POST. Ein Webhook aus der Desktop-App hat immer eins: Kopier es unter Webhook-Secret in den Einstellungen des Webhooks.

    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. Die Antwort lesen

    Die Antwort liefert dir die Nachrichten-ID und eine callback_url, mit der du die Nachricht später änderst.

    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-..."
    }
Wer URL und Secret hat, kann in diesen Kanal posten. Halte beides aus öffentlichen Repositorys und clientseitigem Code heraus.

Was du senden kannst

Eine Nachricht ist entweder die Kurzform (Text mit Titel und Farbe) oder eine vollständige Karte, und beide können Buttons und Dateien tragen. Der Name oben auf der Karte ist immer der Name des Webhooks selbst. In den Einstellungen des Webhooks legst du außerdem fest, ob er Bilder posten und Leute erwähnen darf.

Kurzform

Reicht für die meisten Alarme.

json
{
  "content": "Server backup completed at 03:00 UTC",
  "color": "green",
  "title": "Backup Bot"
}
FeldTypWas es tut
contentstringDer Nachrichtentext. Pflicht, außer du sendest eine Karte oder Dateien.
colorstringblue (Standard), green, orange, red, yellow oder purple.
titlestringEin Titel über dem Text. Standardmäßig der Name des Webhooks.

Eine vollständige Karte

Sende einen message_container für eine Karte mit verlinktem Titel, Untertitel, Markdown, Feldern und Bildern. Über einen Webhook nimmt eine Karte type, color, title, title_url, sub_title, description, fields, avatar_url, image_url, image_base64, images und die Loader-Felder an. Status-Pille, Badge, Diff-Statistik und eingeklappter Denkprozess sind Befehlsantworten vorbehalten. Alle Felder stehen unter Nachrichtenkarten.

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
Nachricht von CI

Build #1847 erfolgreich

Alle 212 Tests auf main grün.
Duration
2m 34s
Commit
1a2b3c4

Buttons

Füg ein actions-Array hinzu, um Buttons unter die Nachricht zu setzen. Wie sie funktionieren, steht unter Buttons.

Dateien

Poste echte Dateien mit einer Nachricht: ein Log, einen Bericht, einen Screenshot. Sie erscheinen wie jeder andere Anhang, als Download-Zeile oder, bei Bildern, Video und Audio, direkt in der Nachricht. Eine Nachricht nur mit Dateien ist in Ordnung: Lass content und die Karte 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"
    }
  ]
}
FeldTypWas es tut
namestringDer Dateiname beim Herunterladen. Pflicht. Ein Pfad wird auf seinen letzten Teil gekürzt.
content_base64stringDie Bytes der Datei als Base64, roh oder als data:-URI. mssgs speichert die Datei und behält an der Nachricht nur einen Link.
mime_typestringDer Content-Type von content_base64. Standard ist text/plain.
urlstringEine Datei, die schon bei mssgs liegt: ein /static/...-Pfad oder eine https://mss.gs/...-URL.
Sende pro Datei genau eines von content_base64 oder url. Beides oder keins von beiden ist ein Fehler.
LimitWert
Dateien pro Nachricht5
Größe pro Datei, nach dem Dekodieren8 MB
Dateiname200 Zeichen
Gesamter RequestEtwa 10 MB. Base64 macht eine Datei um ein Drittel größer, eine einzelne Datei über etwa 7 MB passt also nicht hinein.

Warum url nur mssgs-Adressen annimmt

Eine Webhook-URL landet oft in den Dashboards anderer Dienste. Wird sie geleakt, darf niemand damit die App jedes Mitglieds eine Datei von einem Server laden lassen, den er selbst ausgesucht hat. Liegt deine Datei woanders, sende sie als content_base64, und mssgs hostet sie.

Ein fehlgeschlagener Upload bringt die Nachricht nicht zum Scheitern

Dateien werden vorab geprüft, aber erst danach hochgeladen. Schlägt ein Upload fehl, fällt diese Datei weg, und der Rest der Nachricht wird trotzdem gepostet, ohne Fehler: Lieber die Datei verlieren als den Bericht. Ist eine Datei wichtig, prüf, ob sie angekommen ist.

Eine Datei von der Kommandozeile

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 signieren

Ein Webhook mit Secret nimmt nur Requests an, die beweisen, dass sie es kennen, und ein Webhook aus der Desktop-App hat immer eins (Webhook-Secret in seinen Einstellungen). Signiere den unveränderten Request-Body mit HMAC-SHA256 und dem Secret, und sende den Hex-Digest in Kleinbuchstaben im 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
});
Ein Request ohne gültige Signatur bekommt 401 und {"error": "INVALID_SIGNATURE"}. Nur ein Webhook ohne Secret, etwa einer, der über MCP ohne webhook_secret angelegt wurde, nimmt unsignierte Requests an.

Auch GitHubs eigener Header X-Hub-Signature-256 wird angenommen, ein GitHub-Webhook mit demselben Secret funktioniert also unverändert. Die Signatur hat keinen Zeitstempel und verhindert daher nicht, dass ein abgefangener Request noch einmal gesendet wird: Die URL bleibt das Geheimnis, auf das es ankommt.

Antworten und Fehler

Eine gepostete Nachricht kommt mit ihrer ID und einer callback_url zurück, mit der du sie 30 Minuten lang aktualisieren oder löschen kannst.

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-..."
}
Lies den Body, nicht nur den Status. Eine abgelehnte Payload trägt ihren Grund als error-Code im Body. Behandle jeden Body mit einem error-Schlüssel als Fehlschlag, egal welcher Statuscode.
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}`);
}

Hat die Nachricht Buttons, enthält die Antwort außerdem eine stream_url: einen Live-Stream der Antworten, Reaktionen und Button-Drücke auf diese Nachricht, 10 Minuten lang offen, oder eine Stunde, wenn du "sse_event_extended_timeout": true mitsendest. Siehe Live-Updates.

Statuscodes

StatusWann
401Der Webhook hat ein Secret, und die Signatur fehlt oder ist falsch.
403Dieser Webhook darf nicht in diesen Kanal posten.
404Unter dieser URL gibt es keinen Webhook.
413Der Request ist zu groß.
429Zu viele Requests. Mach langsamer und versuch es erneut.
502Die Nachricht konnte nicht zugestellt werden. Versuch es erneut.

Fehlercodes

CodeBedeutung
MISSING_CONTENTNichts zu posten: kein Text, keine Karte und keine Dateien.
INVALID_MESSAGE_CONTAINERmessage_container ist kein Objekt.
INVALID_MESSAGE_CONTAINER_TYPEDer Kartentyp ist weder embed_message noch system_message.
MISSING_MESSAGE_CONTAINER_DESCRIPTIONEine Karte braucht eine Beschreibung, außer sie ist ein Loader.
MISSING_MESSAGE_CONTAINER_LOADER_TEXTEine Loader-Karte braucht loader_text.
INVALID_WEBHOOK_BINDINGDieser Webhook darf nicht in diesen Kanal posten.
INVALID_SIGNATUREDer Signatur-Header fehlt oder ist falsch.
REQUEST_BODY_TOO_LARGEDer Request überschreitet das Größenlimit.
PUBLISH_FAILEDDie Nachricht konnte nicht zugestellt werden.
INVALID_ACTIONS_FORMAT, INVALID_ACTION_MISSING_FIELDS, DUPLICATE_ACTION_ID, INVALID_TRIGGERS_FORMAT, INVALID_TRIGGER_MISSING_ACTION, INVALID_TRIGGER_ACTION_NOT_ALLOWEDMit einem Button stimmt etwas nicht. Siehe Buttons.

Dateifehler

CodeBedeutung
INVALID_ATTACHMENTS_FORMATattachments ist keine Liste, oder ein Eintrag ist kein Objekt.
TOO_MANY_ATTACHMENTSMehr als fünf Dateien.
MISSING_ATTACHMENT_NAMEEine Datei hat keinen Namen.
INVALID_ATTACHMENT_NAMEVom Namen bleibt nichts Brauchbares übrig, etwa bei ...
MISSING_ATTACHMENT_SOURCEWeder url noch content_base64.
AMBIGUOUS_ATTACHMENT_SOURCESowohl url als auch content_base64.
INVALID_ATTACHMENT_BASE64Das Base64 lässt sich nicht dekodieren.
ATTACHMENT_TOO_LARGEEine Datei ist nach dem Dekodieren größer als 8 MB.
INVALID_ATTACHMENT_URLDie url ist keine mssgs-Adresse.

Limits

LimitWert
Request-GrößeEtwa 10 MB
Dateien pro Nachricht5, je bis zu 8 MB
KartenbeschreibungBis zu 50.000 Bytes. Ab 1.000 Bytes sehen Mitglieder den Anfang und einen Button Mehr anzeigen.
Die Nachricht nachträglich aktualisieren30 Minuten, über callback_url
Live-Stream einer Nachricht mit Buttons10 Minuten, auf Wunsch eine Stunde

Requests sind rate-limitiert. Bekommst du eine 429, warte, bevor du erneut sendest, und fass Alarme, die schubweise kommen, in einer Nachricht zusammen.

GitHub, UniFi und App Store Connect

Richte einen dieser Dienste auf eine Webhook-URL, und mssgs erkennt ihn und postet eine passende Karte, ohne dass du eine Payload schreiben musst. Siehe Integrationen. Diese Dienste bekommen {"success": true} als Antwort und keine Callback-URL.

QuelleErkannt anWas gepostet wird
GitHubDer Header x-github-eventPushes, Pull Requests und Reviews, Issues und Kommentare, Branches und Tags, Releases. Viele Änderungen kurz hintereinander an einem Issue oder Pull Request werden in einer Karte zusammengefasst.
UniFi ProtectDer User-Agent protect-alarm-managerKlingeln an der Tür, Bewegung sowie Personen, Fahrzeuge oder Pakete, die deine Kameras erkennen.
App Store ConnectDer Body seiner Benachrichtigungen oder der Header x-apple-signatureBenachrichtigungen von App Store Connect.

Weiterbauen