Help

Troubleshooting

Fix the common OpenPromoter problems: tracking not recording, sales not credited, webhook signature errors, failed payouts, partner sign-in, custom domains and email.

Use this page when something in your program doesn't work as expected. Each entry starts with what you see and follows with the likely cause and the fix. Messages in quotes are the exact words the app shows.

Tracking is not recording clicks

What you see: Settings > Install shows Not seen yet, or First click recorded stays unticked after you test a partner link.

Why: op.js isn't on the page, its data-program doesn't match your program, or an ad blocker stopped it. A consent banner can also hold it back with data-consent="required".

Fix:

  1. Open your site and view the page source. Check that the op.js tag is in the <head> with your program slug.
  2. Load the page once in a private window with ad blockers off. Then click Check again on Settings > Install.
  3. If ad blockers are common among your visitors, proxy op.js through your own domain with data-api="/op". See Ad blockers.
  4. With consent mode on, call openpromoter("consent", true) after the visitor accepts.
  5. Test with an approved partner's link. Links of pending, paused or banned partners don't record clicks.

op.js reports the install once per browser session. The status turns to Script seen on the first report.

A sale was not credited to the partner

What you see: the customer paid, but no commission appears under Commissions.

Why: the sale needs a signal: a coupon code, the click id, a partner code in metadata, the visitor's clicks or the lead email. If none of these reach OpenPromoter, nobody gets the sale. See Tracking for the full order.

Fix:

  1. On Settings > Install, check that Billing webhook received is ticked. If not, reconnect billing in Settings > Integrations.
  2. With your own Stripe Checkout, pass the click id as client_reference_id and as subscription_data.metadata.op_click. Payment Links and the Pricing Table get it from op.js by default.
  3. For Paddle, Lemon Squeezy or Polar, pass op_click as custom data. See Billing providers.
  4. Check the purchase happened inside the cookie window (60 days by default) and in the same browser as the click.
  5. If you record signups, call openpromoter.signup({ email }) before payment. A later payment by that email is then credited.

A payment that arrives before its partner is known is kept for two days. If the lead or click arrives in that time, the commission is booked then.

On a self-hosted instance on localhost, Stripe can't reach you. Run stripe listen and paste the signing secret it prints. See Self-hosting.

Webhook signature checks fail

What you see: your endpoint rejects OpenPromoter's webhooks, or the SDK throws "Invalid signature" or "Signature timestamp outside tolerance".

Why: the signature covers the exact raw body. Parsing the JSON first, using another endpoint's secret or a server clock that is off all break the check.

Fix:

  1. Verify the raw body before you parse it. In Express use express.raw({ type: "application/json" }) on that route. In Next.js use await req.text().
  2. Copy the signing secret shown under that endpoint in Settings > API & webhooks. Each endpoint has its own whsec_… secret.
  3. Sync your server clock. Signatures older than 5 minutes are refused.
  4. With openpromoter listen, verify with the same --secret you passed to the CLI.

Click Test on the endpoint to send a sample event after each change. See Webhooks.

Incoming billing webhooks fail the same way. If Stripe shows "Invalid signature" for its deliveries, paste the endpoint's current signing secret into Settings > Integrations.

A payout failed

What you see: a payout on the Payouts page has the Failed status with a reason beside it.

Why and fix:

Reason shown Fix
Partner has no PayPal email Ask the partner to save their PayPal email under Payouts in their portal
Partner has no Wise email Ask the partner to save their Wise email in their portal
Partner has not finished Stripe onboarding Ask the partner to click Finish Stripe setup in their portal
PayPal does not pay out in this currency Pay that partner with Wise or bank transfer
This partner needs to submit a W-9 (with SSN/EIN) A US partner paid outside Stripe passed the IRS 1099 limit. They add a W-9 under Settings in their portal
Wise could not fund it Add money to your Wise balance
stripe, paypal or wise is not connected Connect that provider in Settings > Integrations

After you fix the cause, click Retry on the payout. A failed payout releases its commissions to the partner's balance. They also go out in the next run.

A payout that shows "Checking with paypal" (or Wise) is still processing. The provider took the request and OpenPromoter checks the result later. Don't pay it again by hand.

If your Stripe account is registered in a country such as India, Stripe can't pay partners through Connect. Use Wise or bank transfer for those partners.

A partner can't sign in

What you see: a partner says sign-in fails or they land on the application form.

Why and fix:

  • Wrong address. Partners sign in on your program's address: your verified custom domain or <slug>.openpromoter.com. Send them the Partner login link from your program page.
  • Forgotten password. They click Forgot password? and then Send reset link. The new password needs at least 8 characters.
  • Google or GitHub fails. If they see "This account uses two-factor authentication", they sign in with email and password instead. "That email already has an account" means they first sign in with their password.
  • No partner record yet. A signed-in person who isn't a partner in that program is sent to the application form. Check Partners for their email.
  • Account paused, rejected or banned. The portal shows "Your partner account is paused" (or the other status). Change their status on their partner page.

A custom domain stays pending

What you see: Settings > Portal & domain keeps showing Check DNS and HTTPS (or Verify DNS when self-hosting) instead of Verified.

Why and fix:

Message Fix
Waiting for DNS. Add a CNAME from your domain to domains.openpromoter.com Add the CNAME record at your DNS provider. Use the exact Name and Value shown on the page
No CNAME to the target found for your domain yet Self-hosting: the CNAME must point at your APP_URL host or CUSTOM_DOMAIN_TARGET
DNS is set. Cloudflare is issuing the certificate Wait a few minutes and check again
This domain isn't registered yet. Save it again Click Save next to the domain once more
Enter a domain like partners.yourapp.com Use a subdomain you own. A CNAME can't sit on a root domain such as yourapp.com
Another program already uses this domain Each domain serves one program. Pick another subdomain

DNS changes can take a few minutes to reach everyone. See Custom domains.

Emails are not arriving

What you see: partners or teammates don't get invitations, approvals or password reset emails.

Why and fix:

  1. Ask them to check their spam folder and to add your sending address to their contacts.
  2. Check the email address. Addresses ending in .test, .example, .invalid or .localhost never receive mail. The demo and test data use them on purpose.
  3. Self-hosting: set SMTP_URL or RESEND_API_KEY. Without either, emails are only logged and never sent. Set EMAIL_FROM to an address your mail provider has verified.
  4. With docker compose, every email lands in Mailpit at http://localhost:8025 instead of a real inbox.

The Stripe key is refused

What you see: connecting a restricted key in Settings > Integrations fails, or the webhook isn't registered.

Why and fix:

  • "That doesn't look like a Stripe secret or restricted key." Paste a key that starts with rk_live_, rk_test_, sk_live_ or sk_test_.
  • A permission error from Stripe. The restricted key is missing a permission. Add the ones in the Stripe permissions table and create the key again.
  • The webhook isn't registered. The key needs Webhook Endpoints with Write access. Or add the endpoint in Stripe yourself and paste its signing secret.
  • Partner payouts through Stripe fail. Add Accounts, Account Links and Transfers with Write access.

The API returns an error

Status Fix
401 unauthorized Send Authorization: Bearer <key>. Check that the key wasn't revoked
403 forbidden The message names the scope the key needs. Create a key with that scope
404 not_found Check the program id and that the key belongs to the same workspace
429 rate_limited Each key allows 600 requests a minute. Back off and retry

See the API reference for every code.

Next: Quick start