Overview
All customer email links follow the same pattern:- Every link is
{storefrontBaseUrl}/link/{token} - Your app implements one route:
GET /link/{token} - The route calls
Auth.resolveLink(token)to get the customer’s action and redirect target - You sign the customer in (if tokens are provided) and redirect them
Setup
Step 1: Set the storefrontBaseUrl Setting
In the Opencals dashboard, go to Settings → API → Storefront Base URL and enter your app’s base URL:
Step 2: Implement the /link/{token} Route
Your app must handle GET /link/{token}. Here’s an example with Next.js:
app/link/[token]/page.tsx
app/api/auth/link/route.ts
Purpose → Redirect Table
This table shows what each link type does and where the customer is redirected:Special Case: Password Reset
Password reset links do not return customer tokens. Instead, the token is passed as a query parameter so your app can verify it when the customer submits a new password.Example: Password Reset Handler
app/auth/reset-password/page.tsx
Passwordless Email Login (Optional)
In addition to email links, you can offer passwordless login with 6-digit codes:SDK Methods
The following SDK methods are used for email links and passwordless auth:
See the API reference for full request and response schemas.
Security Checklist
- Store API keys in environment variables — never in client code.
- Call
resolveLinkon the server, not the client. - Store customer tokens in httpOnly cookies (not localStorage) when possible.
- Redirect after storing tokens to avoid exposing them in the URL.
- For password reset, store the token as a query parameter only (it does not grant authentication).
- Tokens expire according to their purpose (1 hour for verify-email/reset-password, 7 days for leave-feedback, 24 hours default).
Troubleshooting
“Invalid or expired link”- The token may have been used already (one-time use only).
- The token may have expired (check the purpose expiry in the earlier table).
- The store context may not match the API key’s store.
- Verify tokens were stored (httpOnly cookies, session, etc.).
- Check that
AuthService.resolveLinkreturnedaccessTokenandrefreshToken. - For
reset-passwordlinks, no sign-in occurs — the customer must reset their password and sign in manually.
- The token is included in the URL query string only, not in the API call.
- Pass it to
AuthService.resetPassword({ body: { token, newPassword } })exactly as received.