Stoat Notificationsfor WHMCS · Documentation
v1.0.0

Security & Permissions

A webhook URL is a posting credential. The module treats it that way.


The webhook is a secret

The token in https://stoat.chat/api/webhooks/{id}/{token} authorises anyone who holds it to post into that channel. Stoat’s own documentation compares it to a password.

  • Store it only in the WHMCS provider settings.
  • Do not paste it into tickets, screenshots, chat or source control.
  • If it is exposed, delete the webhook in Stoat and create a new one, then update WHMCS.
  • Use one webhook per destination channel so a leak has a limited blast radius.

Log redaction

Every request is recorded in the WHMCS module log. The URL is rewritten before it is written:

https://stoat.chat/api/webhooks/01ARZ3NDEKTSV4RRFFQ69G5FAV/[redacted]

The webhook ID is kept because it is not secret and helps identify which channel a failure belongs to. The token never appears in the log, in exception messages or in the documentation.

Payloads are logged — the module log also stores the message content and the HTTP status so failures can be diagnosed. The log is admin-only; if your policy treats notification text as personal data, apply your normal module-log retention and access rules.

Transport security

ControlBehaviour
SchemeHTTPS only. Plain http:// URLs are rejected before any request.
TLS verificationPeer and host verification are always enabled.
RedirectsNot followed, so a redirect cannot move the token to another host.
ProtocolscURL is restricted to HTTPS.
TimeoutsConnection timeout 5 seconds; total request timeout 1–30 seconds, default 10.
Credentials in URLRejected.
Query string / fragmentRejected.

URL validation

Before a request is made, the webhook must satisfy all of these:

  • https:// scheme.
  • A host of stoat.chat or a subdomain of it, unless Allow Self-Hosted Host is ticked.
  • A path ending in /api/webhooks/{id}/{token}.
  • Port 443, unless self-hosted is allowed.
  • No userinfo, query string or fragment.
  • 2048 characters or fewer.

This blocks accidental plain-HTTP sends, token smuggling through redirects or userinfo, and arbitrary endpoints being reached through the provider setting. The host allowlist is deliberately opt-out rather than open by default.

Permissions

PermissionUsed forIf missing
SendMessagesEvery requestThe notification fails; nothing can be delivered.
SendEmbedsFormats embed and bothFails, or degrades to Markdown in both mode when strict delivery is off.
MasqueradeSender name or avatarFails, or degrades to the webhook’s identity when strict delivery is off.
ManageRoleNot usedMasquerade colour is never set by this module.
MentionEveryoneNot usedMentions are neutralised, and no mention flags are sent.

Mention safety

Notification text can contain client-supplied strings. @everyone, @here and @online are replaced with a zero-width-space variant before sending, so the words still read naturally but cannot ping the server.

Content safety

  • Only http and https URLs are turned into Markdown links; other schemes stay as text.
  • Link labels have Markdown control characters escaped.
  • URLs are percent-encoded for spaces and parentheses so a crafted URL cannot break out of the link target.
  • Attribute labels and values are flattened to one line, preventing injected layout or fake headings.
  • The module never sets mention_everyone, mention_online or role mentions.

Self-hosted Stoat

Self-hosted instances are supported by ticking Allow Self-Hosted Host. HTTPS is still mandatory, and the path must still end in /api/webhooks/{id}/{token}. Use a certificate the WHMCS server trusts; a self-signed certificate will fail TLS verification by design.

Rotation checklist

  1. Create a new webhook in the same channel.
  2. Update the provider webhook, or every rule override that used the old one.
  3. Save the provider settings and confirm the test message arrives.
  4. Delete the old webhook in Stoat.
  5. Check the module log for failures from any rule still pointing at the old URL.