Troubleshooting
Fixes for the problems self-hosters run into most.
Read the logs
Most problems are explained in the app's log: the setup token, Stripe webhook rejections, events that failed and sales that earned nothing. The dashboard does not show these.
cd /opt/supaffi && docker compose logs app --tail 200Add -f to follow new lines, and docker compose ps lists which services are running.
The dashboard address does not load
Your hosting provider is blocking the dashboard port, 3443 by default. Allow it in their firewall (the security group or network rules in their control panel), not on the server. The same goes for ports 80 and 443, which the affiliate portals need.
If the port is open, check that caddy-dashboard is running with docker compose ps, and that you used https:// with the port. The browser warning about the certificate is expected: continue past it.
Setup is closed
The setup token is held in memory and printed at startup. If /setup says Setup is closed and no owner exists yet, restart the app to issue a new token, then read it from the log:
cd /opt/supaffi && docker compose restart app
docker compose logs app | grep "setup token"Use the last token printed. Once an owner exists, no token is ever issued again.
The dashboard shows 404 on a product address
The owner pages never answer on a product hostname such as affiliates.yourapp.com, so affiliates cannot reach them. Open the dashboard on https://<server-ip>:3443, or on SUPAFFI_DOMAIN if you set one.
The subdomain check fails
The DNS step and Settings check that the product's subdomain points at your server and has a certificate.
With your own proxy, Supaffi issues no certificates: serve one for the hostname yourself. See Reverse proxy and HTTPS.
Stripe events are not arriving
In Settings, under Connections, the webhook row stays on Waiting for the first event from Stripe. Check the log for:
[webhook] rejected a request for merchant …: its signature did not verify against the signing secret on file.- Signature rejected. The signing secret on file does not match the endpoint, often after it was rolled in Stripe. Press Replace on the webhook row, open Stripe webhooks, and paste the endpoint's current signing secret (
whsec_…). The line is logged at most once every 15 minutes per product. - No line at all. In Stripe, open the endpoint Supaffi created (
Supaffi: <product>). Check it is enabled and that its URL ishttps://affiliates.yourapp.com/api/webhooks/stripewith your current subdomain. Changing the subdomain in Settings does not update the endpoint, so edit the URL in Stripe. - Behind your own proxy, check it passes the
Hostheader. The webhook finds its product by hostname.
Stripe rejected the key
A revoked, expired or deleted key shows as Stripe rejected the key in Settings, under Connections. Press Replace, then Create key in Stripe to open a pre-filled restricted key, and paste it. Events that failed in the meantime keep retrying, so replace it within two days. See Connect Stripe.
The script shows Not found
Supaffi looks for the tracking script on your website's home page. If you only put it on landing pages, the check reads Not found until the first click comes in. A click recorded in the past 7 days also counts as Live.
Open one of your affiliate links
For exampleyourapp.com/?via=yourcode, in a private window.Check for the cookie
In the browser's developer tools, the site should now have a__supaffi_referralcookie.Check the site again
On the Tracking page, press Check the site again.
More in Tracking script and links and Test your setup.
A sale earned no commission
When a referred sale is skipped on purpose, the log says why. Common lines:
A sale also earns nothing when it did not carry a referral, came after the attribution window, or was excluded with metadata. See What earns a commission.
Stripe was down or slow
Nothing is lost. Supaffi stores each event as it arrives, then processes it. When a call to Stripe fails, the event is retried after 30 seconds, then at doubling intervals up to every 4 hours, 20 times in all, which is about two days. A restart resumes where it stopped, and duplicate or late events never count twice.
Each failed attempt is logged. After the last one, the event is marked failed and not retried again:
[worker] event … (evt_…) failed: …