Portal troubleshooting

Last updated July 7, 2026 · Operators

The common ones first, with the fix you can apply yourself.

Customer can’t see their invoices

Symptoms: customer logs in fine but the Invoices tab is missing or empty.

Check, in order:

  1. Section toggle: Settings → Portal → “Show Invoices” is on?
  2. Customer access: Customer record → “Portal access enabled” is on?
  3. Customer has invoices in the admin app? If you can’t see invoices for them in your Sales view, the portal can’t either — invoices are pulled from the same underlying data.
  4. Customer logged in as the right person? They need to have used the email address that’s on the customer record. If they typed a different email, they’re logged in as no one.

Stripe Connect onboarding failed (or stuck on “pending”)

Symptoms: you went through the Stripe onboarding but Charges enabled never turned green.

Fix:

  • Stripe is still reviewing. Check your Stripe Dashboard — they often need an extra document (proof of address, business license, etc.) and post the request in the Dashboard.
  • If the account.updated webhook didn’t arrive, our status badge stays stale. Disconnect and reconnect — that will pull fresh status. (You won’t lose any payment history; only the connection itself is reset.)
  • If you never see the green checkmark at all and Stripe says everything is approved, contact {{SUPPORT_EMAIL}} — there may be a webhook config issue on our end.

Stripe webhook isn’t firing

  • In your Stripe Dashboard, go to Developers → Webhooks. There should be an entry pointing at our platform endpoint.
  • Look at recent events. A successful delivery shows 200. Anything else means we rejected it or didn’t reach.
  • A 400 usually means a signature mismatch — contact us. We may need to rotate the webhook secret.

Branding isn’t updating

Symptoms: you saved a new logo or color and the portal still shows the old one.

Fix:

  1. Wait ~10 minutes. Branding assets are cached with a 10-minute TTL to keep portal pages fast. The cache invalidates automatically on save, but if you have multiple servers running, it may take a moment to propagate.
  2. Hard-refresh the browser (Ctrl+Shift+R / Cmd+Shift+R). Browsers also cache images.
  3. Re-save the form. This forces a fresh cache invalidation.

If a logo upload shows a broken-image icon in Settings, see the next item.

“Logo failed to load” warning in Settings

Symptoms: Settings shows a warning that the logo failed to load, and the portal falls back to text-only branding.

Fix:

  • The blob storage key is set but the file itself is missing — possibly because storage was rotated or there was an upload error.
  • Re-upload the asset. The portal falls back to a neutral (no logo, plain text name) look until you do, rather than showing a broken-image icon to your customer.

Temporary-password email not arriving

Symptoms: you clicked Send Temporary Password and the toast said it sent, but the customer never received the email.

Check, in order:

  1. Spam folder — even with our SPF/DKIM/DMARC setup, transactional emails sometimes get filtered, especially the first one to a new recipient.
  2. Mailgun outbox — if you have admin access, check Mailgun. A delivered status means it left our system; bounced means the recipient address rejected it.
  3. Customer’s portal access enabled? Make sure Portal access enabled is on. The button is disabled when it’s off.
  4. Email on the customer record is correct? Even one character off and the email won’t reach them. The customer detail page shows the email it’ll send to.
  5. 30-second cooldown? Re-clicking within 30 seconds is rate-limited (so you don’t accidentally rotate the password twice and confuse the customer). Wait and try again.
  6. Re-send the temporary password. Each click generates a fresh password and re-sends. If the customer already used a previous one, no harm — the latest password is the only valid one.

Forgot-password / reset email not arriving

Symptoms: a customer clicked Forgot password? and entered their email, but no reset email shows up.

Check, in order:

  1. Spam folder first.
  2. Was their email actually on file? The forgot-password page always shows the same generic confirmation (“If an account exists for that email…”) whether or not the email matched — by design, no enumeration. So a typo on the customer’s side means we silently don’t send. Confirm the email on the customer record matches what they typed.
  3. Customer’s portal access enabled? Same as above — if it’s off, no email is sent.
  4. Rate limit hit — 3 reset requests per minute per IP+email. Past that the endpoint returns 429.
  5. Last resort: click Send Temporary Password from the customer record to give them a fresh password directly.

Estimate “Pay” or “Approve” button doesn’t appear

Symptoms: customer is on an estimate detail page and there’s no Approve / Pay action.

For Approve (estimates):

  • Settings → Portal → “Show Estimates” is on?
  • The estimate is in a status that allows approval (not already approved, declined, or expired)?

For Pay (invoices):

  • Settings → Portal → “Allow online payments” is on?
  • Settings → Stripe Connect → status is fully green (charges_enabled = true)?
  • The customer has at least one allowed method? (If you’ve turned off both “Allow card” and “Allow ACH”, there’s nothing to use.)
  • The invoice balance is greater than the minimum online payment (default 100 cents = $1.00)?

Customer paid but the invoice still shows unpaid

Symptoms: customer’s bank statement shows a charge, but the invoice in your admin app is still marked unpaid.

Check:

  • ACH is slow. Bank payments take 3–5 business days to settle. The invoice stays “Pending” until Stripe confirms via webhook. This is normal — don’t refund the customer.
  • Webhook missed: if it’s been days for a card payment, look at the Stripe Dashboard for the customer’s PaymentIntent. If it says succeeded but we haven’t recorded a payment, the webhook delivery may have failed. Contact {{SUPPORT_EMAIL}} with the PaymentIntent ID — we can replay the event.
  • The nightly reconciliation job runs every night and flags any drift between Stripe and the local Payment rows, so any missed payment will surface within 24 hours even if a webhook is lost.

Customer can’t sign in

Symptoms: customer says their password doesn’t work, never set one, or lost the temporary password you sent.

How portal sign-in works: each customer signs in with their email address and a password. When you first grant access you send them a temporary password; they’re prompted to set their own on first sign-in.

Fix:

  • Lost the temporary password (or never used it)? Re-send it from the customer’s Portal tab — Send Temporary Password issues a fresh one and invalidates the old.
  • Set a password and forgot it? They can use Forgot password? on the portal login page to reset it by email — see Password reset.
  • Nothing arrives at all? Confirm Portal access enabled is on for that customer and that your email sending is configured.

Customer’s saved card was charged but they’re disputing it

This is a payment dispute, not a portal issue. Handle it in your Stripe Dashboard under Disputes. Our webhook listens to charge.dispute.created and surfaces it in your Sales view so you don’t miss it.

Still stuck?

  • Read the full PRD at .claude/plans/put-together-a-plan-fancy-orbit.md for design-level detail.
  • Email {{SUPPORT_EMAIL}} with: your company name, customer name (if customer-specific), screenshot, and the time of day the issue occurred (we use it to find logs).

Put it to work in your own account

14-day free trial · No credit card required