curl, PowerShell and Python
All three examples send the same card in the GGspeak format, with a key: running it again with state set to ok updates the card. The link comes from an environment variable, GGSPEAK_WEBHOOK.
curl -fsS -X POST "$GGSPEAK_WEBHOOK" \ -H "Content-Type: application/json" \ -d '{ "key": "nightly-backup", "card": { "title": "Nightly backup", "state": "alert", "body": "pg_dump failed: no space left on device", "fields": [{ "name": "Server", "value": "sp-1" }] } }'-f makes curl exit with an error on 4xx and 5xx responses, and -S shows the message even with -s. To see the error body, drop the -f.
# Windows PowerShell 5.1 may default to an old TLS version.[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$body = @{ key = 'nightly-backup' card = @{ title = 'Nightly backup' state = 'alert' body = 'pg_dump failed: no space left on device' fields = @(@{ name = 'Server'; value = 'sp-1' }) }} | ConvertTo-Json -Depth 5
try { # Send UTF-8 bytes: without this, 5.1 mangles accented characters. Invoke-RestMethod -Method Post -Uri $env:GGSPEAK_WEBHOOK ` -ContentType 'application/json; charset=utf-8' ` -Body ([System.Text.Encoding]::UTF8.GetBytes($body))} catch { # In 5.1 the error body lives in ErrorDetails. Write-Error $_.ErrorDetails.Message}On success the response is 204 and Invoke-RestMethod returns nothing. With ?wait=true in the URL it returns the message as an object.
import osimport requests
payload = { "key": "nightly-backup", "card": { "title": "Nightly backup", "state": "alert", "body": "pg_dump failed: no space left on device", "fields": [{"name": "Server", "value": "sp-1"}], },}
r = requests.post(os.environ["GGSPEAK_WEBHOOK"], json=payload, timeout=10)
if r.status_code == 429: try: wait = r.json()["retry_after"] # seconds, with decimals except (ValueError, KeyError, TypeError): # a 429 from a proxy, without the GGspeak body wait = r.headers.get("Retry-After", "1") print(f"Rate limited. Try again in {wait} s.")elif not r.ok: try: err = r.json() print(f"{err['code']}: {err['message']} ({err['docs']})") except (ValueError, KeyError, TypeError): # gateway error, not JSON print(f"HTTP {r.status_code}: {r.text[:200]}")requests already sends Content-Type: application/json and encodes UTF-8 when you use json=. r.ok is true on 204.
To mark it ok when the script finishes fine, send another POST with the same key and "state": "ok" (the card turns resolved). Error codes are in Errors.