Auf Eingaben reagieren
Webhook einrichten
So erfährt dein Tool, was du in der App tust: Empfänger bauen, Signatur prüfen, testen.
Bisher hat nur dein Tool mit Samy gesprochen. Mit einem Webhook geht es auch zurück: Tippst du einen Knopf, legst einen Schalter um oder schreibst im Chat, ruft Samy dein Tool auf.
Am Ende dieser Seite hast du einen kleinen Empfänger, der Ereignisse annimmt, ihre Echtheit prüft und darauf reagiert. Auf ihm bauen Knöpfe, Elemente in Nachrichten und Antworten auf.
Du brauchst: ein Packlet mit Schlüssel und einen Ort, an dem dein Tool unter einer öffentlichen https-Adresse erreichbar ist.
1. Einen Empfänger bauen
Ein Webhook-Empfänger ist ein kleiner Webserver mit einer einzigen Adresse, die POST-Anfragen annimmt. Dieser hier gibt jedes Ereignis aus:
# pip install flask
from flask import Flask, request
app = Flask(__name__)
@app.post("/samy")
def samy_webhook():
event = request.get_json()
print(event)
return "", 204
app.run(port=8080)2. Öffentlich erreichbar machen
Samy muss deinen Empfänger aus dem Internet erreichen. Die Adresse muss mit https:// beginnen. Adressen wie localhost oder 192.168.… lehnt Samy ab.
- Auf einem Server mit eigener Domain ist das schon so.
- Auf deinem Rechner hilft zum Testen ein Tunnel, zum Beispiel
cloudflared tunnel --url http://localhost:8080oderngrok http 8080. Beide geben dir eine öffentlichehttps-Adresse, die auf deinen Rechner zeigt.
3. Die Adresse in Samy eintragen
Die Webhook-URL trägst du beim Anlegen des Packlets ein oder später in seinen Einstellungen: im Kanal oben auf Avatar oder Namen tippen. Im Beispiel wäre das https://deine-adresse.example/samy.
Zum Testen schickst du dir eine Nachricht mit einem Aktions-Knopf und tippst ihn an:
curl https://api.packlets.app/v1/push \
-H "Authorization: Bearer $SAMY_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "Kommt der Webhook an?", "buttons": [{"id": "ja", "label": "Ja"}]}'Dein Empfänger gibt jetzt das Ereignis aus:
{
"type": "button",
"packlet_id": "9d2f7c10-3a5b-4c8e-9f21-6b7d8e9f0a12",
"message_id": "5b0c1a52-7c1e-4a43-9d0b-2f6f0c7d1e11",
"button_id": "ja",
"pressed_at": "2026-11-09T18:00:00.000Z"
}Kommt nichts an? Im Protokoll des Packlets siehst du jeden Versuch mit dem Status deiner Antwort. Mehr unter Fehlersuche.
4. Signatur prüfen
Deine Adresse ist öffentlich. Jeder, der sie kennt, könnte dir gefälschte Ereignisse schicken. Deshalb unterschreibt Samy jede Anfrage im Header X-Samy-Signature, und dein Tool prüft die Unterschrift, bevor es etwas tut.
So wird gerechnet:
- Das Geheimnis ist der SHA-256-Hash deines Schlüssels, als Hex-Text.
- Mit diesem Geheimnis bildest du den HMAC-SHA256 über den Body, genau so, wie er angekommen ist.
- Das Ergebnis als Hex-Text, mit
sha256=davor, muss dem Header gleichen.
import hashlib, hmac, os
from flask import Flask, request
app = Flask(__name__)
SECRET = hashlib.sha256(os.environ["SAMY_KEY"].encode()).hexdigest().encode()
def is_from_samy(raw_body: bytes, signature: str) -> bool:
expected = "sha256=" + hmac.new(SECRET, raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature or "")
@app.post("/samy")
def samy_webhook():
if not is_from_samy(request.get_data(), request.headers.get("X-Samy-Signature", "")):
return "", 401
event = request.get_json()
print(event)
return "", 204
app.run(port=8080)Achtung: Rechne über den Body, wie er angekommen ist, nicht über das JSON, das du daraus neu erzeugst. Schon ein anderes Leerzeichen ändert die Signatur.
Erzeugst du in der App einen neuen Schlüssel, ändert sich auch das Geheimnis. Dein Tool braucht dann den neuen Schlüssel.
Löst dein Tool etwas aus, das nicht doppelt passieren darf, prüf zusätzlich timestamp und event_id aus dem Body: Schutz vor Wiederholung.
5. Auf Ereignisse reagieren
Es gibt drei Ereignisse. Das Feld type sagt, welches es ist:
type | Wann | Wichtige Felder |
|---|---|---|
button | Du tippst einen Aktions-Knopf. | message_id, button_id |
ui | Du bedienst ein Element in einer Nachricht. | message_id, element_id, value |
message | Du schreibst im Chat. | text |
Alle Felder stehen in der Referenz Webhook-Ereignisse. Ersetz im Empfänger die Ausgabe durch deine Logik:
event = request.get_json()
if event["type"] == "button" and event["button_id"] == "restart":
starte_neu(event["message_id"]) # deine Funktion
elif event["type"] == "ui" and event["element_id"] == "rollback":
rollback() # deine Funktion
elif event["type"] == "message":
antworte(event["text"]) # deine Funktion
return "", 204Ereignisse, die dein Tool nicht kennt, ignoriert es am besten. So läuft es weiter, wenn Samy neue bekommt.
Schnell antworten
Samy wartet höchstens 10 Sekunden auf deine Antwort. Jeder Status 2xx gilt als angekommen, der Inhalt deiner Antwort wird nicht gelesen.
Dauert die Arbeit länger, zum Beispiel eine KI-Antwort, dann bestätige sofort und arbeite danach. Das Ergebnis schickst du mit POST /v1/push, sobald es fertig ist.
Doppelte Ereignisse
Antwortet dein Tool nicht, zu spät oder mit einem Serverfehler, versucht Samy es bis zu fünfmal erneut, über etwa zehn Minuten verteilt. Dasselbe Ereignis kann also mehrfach ankommen, auch wenn dein Tool es beim ersten Mal schon verarbeitet hat.
Darf eine Aktion nur einmal laufen, merk dir, was du schon gesehen hast:
| Ereignis | Eindeutig durch |
|---|---|
button | message_id und button_id |
message | message_id |
ui | message_id, element_id und changed_at |
Bei Schaltern ist das meist egal: Denselben Wert zweimal zu setzen schadet nicht. Ein Neustart oder eine Zahlung dagegen darf nur einmal laufen.
Wie es weitergeht
- Mit deinem Tool schreiben: Chat-Bot bauen.
- Schalter, Eingabefelder und Buttons in Nachrichten: Elemente in Nachrichten.
- Alle Felder, Header und Regeln der Zustellung: Webhook-Ereignisse.