Skip to main content

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:
Call it on every non-2xx:
Write to a file, not only to stdout. A coding agent working in your project can read a file in the repo; it cannot read your hosting provider’s log stream.
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.
This file keeps growing. Nothing depends on old entries, so delete it whenever it gets large, or add it to .gitignore and clear it between runs.

2. Set your alert phone

HookMyApp texts this number when your integration breaks, so you find out without watching the logs.

3. Never retry a usage-limit rejection

A 429 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:
Log it, stop the work, and pass message straight through to whoever can act on it:
Over the limit, your access token is refused for every call including read-only GETs, so the integration looks entirely dead. Surface message and the reason is obvious; swallow it and it looks like your app broke.

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 gets 401 or 403 and every inbound message is lost.
Authenticate it with the HMAC signature instead: verify 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.

Health check when something breaks

Then read your own log from step 1 for the calls your app made.