Webhook recipes
Copy-paste examples for posting to an AbabilX flock from Node.js, Python, Go, PHP, shell, GitHub Actions, a website contact form, Zapier, Make and n8n.
Last updated
Every sample reads the URL from the environment variable ABABILX_WEBHOOK_URL, sends an event_id so a retry can never post twice, retries only 429 and 5xx, and gives up on 4xx. That is the policy in Responses and errors.
Node.js
flock.mjs
// Node 18+ (built-in fetch). Retries 429 / 5xx / network errors, never 4xx.
export async function postToFlock(message, eventId) {
const url = process.env.ABABILX_WEBHOOK_URL;
for (let attempt = 0; attempt < 5; attempt++) {
let res;
try {
res = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message, event_id: eventId }),
signal: AbortSignal.timeout(10_000),
});
} catch {
await sleep(2 ** attempt * 1000); // network error or timeout
continue;
}
if (res.ok) return res.json(); // 200 (duplicate) or 201
if (res.status === 429) {
await sleep(Number(res.headers.get("Retry-After") ?? 1) * 1000);
continue;
}
if (res.status >= 500) {
await sleep(2 ** attempt * 1000);
continue;
}
const body = await res.text();
throw new Error(`Flock rejected the post: ${res.status} ${body}`);
}
throw new Error("Flock unavailable after 5 tries");
}
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));Python
flock.py
import os, time, requests
def post_to_flock(message: str, event_id: str | None = None) -> dict:
url = os.environ["ABABILX_WEBHOOK_URL"]
body = {"message": message}
if event_id:
body["event_id"] = event_id
for attempt in range(5):
try:
res = requests.post(url, json=body, timeout=10)
except requests.RequestException:
time.sleep(2 ** attempt)
continue
if res.status_code in (200, 201):
return res.json()
if res.status_code == 429:
time.sleep(int(res.headers.get("Retry-After", "1")))
continue
if res.status_code >= 500:
time.sleep(2 ** attempt)
continue
raise RuntimeError(f"Flock rejected the post: {res.status_code} {res.text}")
raise RuntimeError("Flock unavailable after 5 tries")
# With a file:
# requests.post(url, data={"payload_json": '{"message":"Report"}'},
# files={"files[0]": ("report.pdf", open("report.pdf", "rb"), "application/pdf")})Go
flock.go
package flock
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
"time"
)
func Post(message, eventID string) error {
body, _ := json.Marshal(map[string]string{"message": message, "event_id": eventID})
client := &http.Client{Timeout: 10 * time.Second}
for attempt := 0; attempt < 5; attempt++ {
res, err := client.Post(os.Getenv("ABABILX_WEBHOOK_URL"), "application/json", bytes.NewReader(body))
if err != nil {
time.Sleep(time.Duration(1<<attempt) * time.Second)
continue
}
res.Body.Close()
switch {
case res.StatusCode == 200 || res.StatusCode == 201:
return nil
case res.StatusCode == 429 || res.StatusCode >= 500:
time.Sleep(time.Duration(1<<attempt) * time.Second)
default:
return fmt.Errorf("flock rejected the post: %d", res.StatusCode)
}
}
return fmt.Errorf("flock unavailable after 5 tries")
}PHP
notify.php
<?php
$ch = curl_init(getenv('ABABILX_WEBHOOK_URL'));
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode(['message' => 'New sign-up: ' . $email]),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status !== 200 && $status !== 201) {
error_log("Flock post failed: $status $body");
}Shell and cron
notify.sh
#!/bin/sh
# Cron-safe: retries 429 and 5xx, quiet on success.
# Example: 0 3 * * * /opt/backup.sh && /opt/notify.sh "Backup finished"
jq -n --arg m "$1" --arg id "cron-$(hostname)-$(date +%Y%m%d%H%M)" \
'{message: $m, event_id: $id}' |
curl -fsS --retry 4 --retry-connrefused --max-time 10 \
-X POST "$ABABILX_WEBHOOK_URL" -H 'Content-Type: application/json' -d @- >/dev/nullGitHub Actions
deploy.yml
# .github/workflows/deploy.yml (last step of the job)
# Add the URL as a repository secret named ABABILX_WEBHOOK_URL.
- name: Tell the flock
if: always()
env:
ABABILX_WEBHOOK_URL: ${{ secrets.ABABILX_WEBHOOK_URL }}
run: |
STATUS="${{ job.status }}"
jq -n --arg m "Deploy of \`${GITHUB_SHA::7}\` on ${GITHUB_REF_NAME}: **$STATUS**" \
--arg id "gh-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" \
'{message: $m, event_id: $id}' |
curl -sS --retry 4 --retry-connrefused -X POST "$ABABILX_WEBHOOK_URL" \
-H 'Content-Type: application/json' -d @-Using run id plus attempt as the event_id means a re-run posts again, but a curl retry inside one run does not.
Website contact form (browser)
The URL is visible to every visitor. They can post to the flock too. Use a flock made just for this form, and replace its URL if it gets abused. To keep the URL private, post from your own server instead.
contact.html
<form id="contact">
<input name="name" required placeholder="Name" />
<input name="email" type="email" required placeholder="Email" />
<textarea name="text" required placeholder="Message"></textarea>
<button>Send</button>
</form>
<script>
// Anyone who opens this page can read this URL and post to the flock.
const FLOCK_URL = "https://<host>/hooks/chat/<flock-id>/<secret>";
document.getElementById("contact").addEventListener("submit", async (e) => {
e.preventDefault();
const f = new FormData(e.target);
const res = await fetch(FLOCK_URL, {
method: "POST",
// text/plain skips the CORS preflight; the server still reads JSON.
headers: { "Content-Type": "text/plain" },
body: JSON.stringify({
message: `**New contact**\nName: ${f.get("name")}\nEmail: ${f.get("email")}\n\n${f.get("text")}`,
event_id: crypto.randomUUID(),
}),
});
alert(res.ok ? "Thanks, we got it." : "Could not send, please try again.");
});
</script>Zapier, Make, n8n and other tools
| Tool | Step to use | Settings |
|---|---|---|
| Zapier | Webhooks by Zapier → POST | URL: the flock URL. Payload type: JSON. Data: message = your text (and event_id = a unique field from the trigger). |
| Make | HTTP → Make a request | Method POST, body type Raw, content type JSON, request content {"message":"…"}. |
| n8n | HTTP Request node | Method POST, Send Body on, Body Content Type JSON, field message. |
| Uptime monitors, alerting tools | Custom or generic webhook | POST, JSON body with a message field. If the tool only sends Slack format ({"text":…}), add a transform step that renames text to message. |
Test without spamming the flock
- Make a second flock for testing and add only yourself.
- Send the same event_id twice: the second answers 200 with duplicate: true and nothing new appears.
- Mute the test flock if you do not want notifications while testing.