Help centre

How can we help?

Why my invite isn't working (and how to fix it)

Last updated 2026-05-04

The symptom

A client clicks the invite link you sent them and sees something like:

{ "error_code": "otp_expired",
  "msg": "Email link is invalid or has expired" }

Or a generic browser error like ERR_FAILED on a supabase.co URL. They tell you the link is broken — even though you sent it minutes ago.

What probably happened

The original Supabase invite system used single-use tokens in the URL. If anything visited that URL before the human clicked it, the token was burnt. The most common culprits, in rough order:

  1. Corporate email scanners — Mimecast, Proofpoint, Microsoft Defender, and similar services auto-visit every URL in inbound mail to "scan for safety." That visit consumes the token.
  2. Personal Gmail's link scanning — Google scans inbound links for phishing/malware on free @gmail.com accounts too. Less aggressive than corporate gateways, but still hits auth-shaped URLs deeply enough to consume them.
  3. iOS Mail Privacy Protection — proxies email through Apple's servers and pre-fetches remote content (sometimes including links) to obscure when mail is opened.
  4. Browser extensions — privacy / safe-browsing extensions like uBlock Origin, Brave Shields, Norton Safe Web sometimes block or pre-fetch authentication URLs.
  5. Mail-app link previews — iOS Mail and the Gmail iOS app render preview cards for URLs, which can trigger a fetch.
  6. Double-clicking — the client previewed the link on their phone, then clicked it again on desktop. First click consumed; second click fails.

Bumping the OTP TTL in Supabase doesn't fix any of these — single-use is what's getting violated, not expiry.

What we did about it

Frank's invite + password-reset flow no longer uses Supabase OTP magic links. Instead:

  • The link in the email points at app.financefrank.ai/accept-invite?token=ABC — your own domain, on a route nobody's privacy extension blocks
  • The token is multi-use until activated — scanners can hit the URL fifty times and the link still works when the human clicks
  • Account creation only happens on POST with the password the user types — scanners don't POST forms, so nothing they do can consume the invite
  • After activation, the link shows a friendly "you've already activated your account, sign in" message instead of an error

How to send a fresh invite

In /admin, find the user's row. Every row now has these buttons:

  • Resend invite (amber) — generates a new multi-use invite token and emails the user a fresh link. Use this if they never completed signup
  • Reset password (gray) — generates a multi-use password-reset token. Use this if they signed up before but can't access their account

Tell the recipient: click the link directly on the device they want to sign in on, in their normal browser. They don't need to rush — multi-use means even if their inbox scanner hits it first, the link still works for them. They don't need to avoid mobile previews either.

If email delivery itself is the issue (the recipient claims they never got the email), the success toast in /admin includes the link as a fallback — copy it and send via Slack / SMS / WhatsApp directly.

Token expiry

Multi-use, but not forever. Both invite and password-reset tokens expire after 7 days. After that the user sees a "this link has expired" message and you'll need to resend.

When to use which

| Situation | Button | |---|---| | New user — never signed up before | Send invite (in the +Add User panel) | | Existing invited user got otp_expired on the original Supabase link | Resend invite | | Existing user forgot their password | Reset password | | User signed up but can't access — and you're not sure why | Reset password (works for any existing account) |

What if it still doesn't work

The new flow is robust against email scanners but not against:

  • Wrong email address — typo when you invited them. Check the /admin row and re-invite if needed
  • Email blocked by their spam filter — copy the fallback link from the admin success toast and send via another channel
  • Their browser is failing to reach your domain — try incognito mode (rules out browser extensions). If a different network works, the issue is their ISP / DNS / corporate firewall

If none of the above resolve it, the "fallback link" pattern from the admin toast is your safety valve — that link works in any browser, on any network that can reach app.financefrank.ai.

Was this article helpful?