POST /students at signup). This guide wires that menu item so clicking it drops the user straight into their courses.
Every request uses your academy base URL and a live key:API access is a paid capability. On a preview trial the API returns
402 trial_api_unavailable until you upgrade.How it works
1
User clicks through in your app
Point your menu item at a route on your server (for example
/go/courses), not at the academy directly.2
Your server mints a link
That route calls
POST /api/v1/students/{studentId}/sign-in-link with your API key and the student’s ID (which you stored when you created them at signup).3
Redirect the browser
The response contains a one-time
url. Send a 302 redirect to it immediately.4
The academy signs them in
The academy verifies the token, sets a session cookie, and lands the user, signed in, on
/courses (or the next path you passed). If you have turned on welcome questions and this member has not answered them yet, the first landing is the welcome screen, which then continues to your next path.Prerequisites
- An API key for the academy: the same
fa_live_key you use to create students. Server-side only, never in browser or mobile code. See Authentication. - The student must already exist in the academy, and you need their
idfrom thePOST /studentsresponse (store it against your own user record at signup). - Create students with
send_welcome_email: false. The welcome email carries its own sign-in link, and the first sign-in link you mint invalidates it. If your app is the way in, skip the email. - Backfilling existing members:
POST /studentsreturns409 already_existswithout anidfor someone who is already a member. Page throughGET /studentsand match on email to collect their ids.
Example: a “Courses” menu handler
Any server stack works the same way; here it is as a Next.js route handler.app/go/courses/route.ts (your app)
/go/courses and you’re done.
To deep-link into a specific course instead, pass its path as next, for example { "next": "/courses/a1b2c3d4-e5f6-7890-abcd-ef1234567890" }. Course IDs come from GET /courses.
The rules
Errors
A successful call returns
201 with { "data": { "url", "single_use": true, "expires_in_seconds": 3600 } }.
FAQ
How long does the user stay signed in?
How long does the user stay signed in?
The link is one-time, but the session it creates is a normal academy session: it persists in that browser until the user signs out. With the handler above, every click still mints and redeems a fresh link, so each click also cancels any email login code the student requested in the meantime. That is harmless for a menu item, and one more reason to mint only on click.
What happens if the user re-opens an old link?
What happens if the user re-opens an old link?
A consumed or expired link sends them to the academy’s sign-in page. The page shows the message written for invitation links (“This invitation link has expired” or “is no longer valid”), but entering their email and the code still works, and afterwards they land on the
next path. If that browser is already signed in to the academy, they land on /courses instead, and your next path is dropped. Minting a fresh link on every click avoids both cases.Is this secure?
Is this secure?
The trust model is: your API key created the student, so your API key may sign the student in. The browser-visible token is single-use and expires in one hour, and the academy re-validates the landing path server-side, so the link can never redirect off-site. Treat the URL like a password until it is used: anyone who opens it first is signed in as that student. Guard the API key accordingly, because anyone holding it can act as any student in the academy.
Does this work with a custom domain?
Does this work with a custom domain?
Yes. The returned URL always points at the academy’s live host: your verified custom domain when one is configured, your fayneos subdomain otherwise.

