1. Log every failed call to a file
hookmyapp channels logs records what HookMyApp forwarded to your webhook URL and what your endpoint answered. It does not record the calls your app makes to us, so your app has to record those itself.
Already using pino, winston, or Sentry? Use it, and make sure four fields land in the record: HTTP status, the HookMyApp error code, the x-request-id response header, and which direction the call went.
Nothing in place yet? One function, no dependency:
The gateway sets
x-request-id on every response and honors one you send. A support ticket that names a request id is faster to answer..gitignore and clear it between runs.
2. Set your alert phone
3. Never retry a usage-limit rejection
A429 with code CHANNEL_USAGE_LIMIT_EXCEEDED means your organization is over its plan allowance. Retrying cannot lift it and neither can waiting out the current period. Only an upgrade or a top-up does.
Every rejected call tells you so. The response body is:
message straight through to whoever can act on it:
4. Keep the webhook route out of your auth middleware
HookMyApp is not a logged-in user of your app. If your webhook path sits behind session auth, an API-key gate, or a WAF rule, HookMyApp gets401 or 403 and every inbound message is lost.
X-HookMyApp-Signature-256 against WEBHOOK_HMAC_SECRET.
Answer fast, too. Return 200, then do the work. A slow handler becomes a 504 on our side and the message is recorded as undelivered.
5. Use the CLI tunnel, not a hand-rolled one
An always-on self-hosted deployment is a supported pattern: a personal server, a Raspberry Pi, a long-running agent.hookmyapp channels listen is built for it, with a per-channel access-controlled tunnel on a stable hostname.
A hand-rolled tunnel is not. Free tunnel services hand out a new random hostname on every restart, so the URL stored by webhook set stops working the moment the tunnel restarts, with nothing to announce it.
Use channels listen for a self-hosted or local destination, and a real HTTPS URL for a deployed backend.