Sign-in from your product

People can sign in to your academy with their account in your product, two ways. Put your product's login in front of the academy: your Auth0, Okta or any other OpenID Connect provider, set up in Settings with no code on your side. Or have your server ask for single-use links: an Academy link in your app opens the lesson already signed in, with no email to wait for. Both are on every plan.

Put your product's login in front of the academy

The academy's sign-in page leads with Continue with your product, which opens your product's own login. Someone already signed in to your product comes straight back, signed in to the academy; someone else signs in there as they always do. A first sign-in makes them a learner.

  1. On Admin → Settings → Sign-in (owners and admins), give Product name its name, the one on the button, and Save.
  2. Press Set up on Your product's login. In your identity provider, register the academy as a web application that signs in with the authorization code flow, and give it the Callback URLs listed there: one for each address your academy answers on.
  3. Paste the Issuer URL, the Client ID and the Client secret, and Save. Saving reads the issuer's /.well-known/openid-configuration and says what's wrong if it can't be used.
  4. Press Test sign-in and sign in with your own account. It signs nobody in: back on Settings, the card says who your login said you are, or why it didn't work. A staff address passes too, though it signs in only with its email (below).
  5. Press Turn on. It appears once a test has passed.

Auth0: Applications → Create Application → Regular Web Applications. Paste the callback URLs into Allowed Callback URLs (under Application URIs on its Settings tab), and on its Connections tab turn on the ways your users sign in (your database, Google, …). The issuer URL is your domain with https:// in front and a / after (https://acme.us.auth0.com/); the client ID and secret are on the application's Settings tab. Someone in your Auth0 database gets in once they've clicked Auth0's verification email: until then Auth0 says email_verified: false.

Okta: Applications → Create App Integration → OIDC - OpenID Connect → Web Application, grant type Authorization Code. Paste the callback URLs into Sign-in redirect URIs, and under Assignments let in everyone who should reach the academy. The issuer URL is your Okta address (https://acme.okta.com), or your authorization server's (https://acme.okta.com/oauth2/default).

Any other OpenID Connect provider works the same way (Microsoft Entra, Google, Amazon Cognito, Keycloak, Clerk): the issuer URL is the address in front of its /.well-known/openid-configuration.

  • The academy asks for the openid email profile scopes, with PKCE, and sends the client secret with HTTP Basic (in the request body when that's the only way your login lists).
  • The person is their email address, as everywhere in the academy: the same address is the same learner whichever way they sign in. Their name fills a missing one and never renames anyone.
  • Your app's Academy link can point at https://learn.yourcompany.com/sign-in/oidc?next=/courses/sql-fundamentals: someone signed in to your product goes straight to the course, without the API call below. An Okta tile can use the same address as its initiate login URI.
  • Refused, with the reason on the sign-in page: an address your login says it hasn't verified (email_verified: false), an answer without an email address, and anyone with a staff seat (they sign in with their email, as below).
  • One network signs in through your login at most 100 times a minute and 1,000 an hour (a workshop fits); past that, the sign-in page says to try again in a few minutes and offers email.
  • Changing the issuer, client ID or secret turns the login off until a new test passes. Turn off brings the sign-in page back to your product's sign-in page (below) or email; people it signed in stay signed in.
  • Signing out of the academy doesn't sign anyone out of your product, so the next Continue with comes straight back.

Send people straight to your product

Once your login or sign-in links are on, The sign-in page (on the same Settings tab) chooses what people see when they sign in:

  • Continue with your product, with email under it (the default), for prospects without an account in your product.
  • Straight to your product: the academy's sign-in page opens your product's login, or its sign-in page, at once, with no email form. The email form still shows on the way to the admin, for a team invitation, and after a sign-in that didn't work (which says why). Choose it when anyone can create an account in your product.

If your login goes down, switch back to Continue with: every visit to the sign-in page goes to your login first, and only a sign-in that failed shows the email form.

It changes the sign-in page, not who can get in: course access stays with accounts and audiences.

Open Admin → Settings → Sign-in, press Set up on Sign-in links (owners and admins), then Turn on sign-in links. The card shows the request to make. Turned off, new links are refused and links already handed out stop signing anyone in; people signed in through one stay signed in.

Requests use the academy's API token, the one the metric ingest and the accounts API use (generated by an owner or admin on Admin → Settings → Integrations → API token). Keep it on your server: whoever holds it can sign people in to your academy.

Shell
curl -X POST https://mastria.dev/api/sign-in-links \
  -H "Authorization: Bearer $MASTRIA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"email": "ada@datacraft.io", "name": "Ada Lovelace", "next": "/courses/sql-fundamentals"}'
FieldValue
emailRequired. The person signed in to your product.
nameOptional. Their name, given to someone who doesn't have one yet; it never renames anyone.
nextOptional. A path on your academy to land on: a course, a lesson, a path. The home without it.

The answer is 201:

JSON
{ "url": "https://learn.yourcompany.com/sign-in/product?token=…", "expires_at": "2026-11-02T09:30:00.000Z" }

Send the browser to url. The link works once, only on your academy (on its custom domain when it has one), and for three days. It carries only its token: the address, name and destination stay on Mastria's side.

Ask for the link when the person clicks, not when your page renders: your app's Academy link points at an endpoint of your own, which asks for a link for whoever is signed in and redirects. Never email a link or put one in a message: mail scanners open links before people do, and the first open spends it.

JavaScript
// Express: link to /academy?next=/courses/sql-fundamentals from your app.
app.get("/academy", requireLogin, async (req, res) => {
  const next = req.query.next ?? "/";
  const answer = await fetch("https://mastria.dev/api/sign-in-links", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.MASTRIA_TOKEN}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ email: req.user.email, name: req.user.name, next }),
  });
  // No link (a staff address, say): the academy's own sign-in, same page.
  if (!answer.ok) {
    return res.redirect(`https://learn.yourcompany.com/login?next=${encodeURIComponent(next)}`);
  }
  const { url } = await answer.json();
  res.redirect(303, url);
});

Other answers: 401 for a missing or unknown token, 400 for invalid JSON, 422 for a malformed body (with the reason; unknown fields such as first_name count), 403 while sign-in from your product is off or for a staff member's address, and 429 past 300 requests a minute (counted together with the other API calls), with a Retry-After header in seconds.

Send the academy's sign-in to your product

Without an OpenID Connect login (above), people who open your academy directly can still sign in through your product. On Admin → Settings → Sign-in, give Product name its name and, under Sign-in links, Your product's sign-in page (owners and admins; it works while sign-in links are on). The academy's sign-in page then leads with Continue with your product, which opens that URL with next, the academy page to come back to:

https://app.yourcompany.com/academy?next=%2Fcourses%2Fsql-fundamentals

The endpoint above already is one: it signs the person in to your product (someone already signed in passes straight through), asks for a link with that next, and redirects. Give its address: https, with no #fragment. next is a path on your academy (/ for the home); pass it back unchanged.

  • Every sign-in door opens this page: the header's Sign in, a lab's sign-in, a private course's.
  • Email sign-in stays under the button, for prospects and anyone without an account in your product.
  • The page offers email only on the way to the admin (staff sign in with their email, and the API refuses their address), on a team invitation, and after Sign in with your email on a profile.
  • The button waits for the academy's API token, and a blank URL or turning sign-in links off leaves email only.
  • While your product's login is on, Continue with opens the login instead; the URL waits.

What the person gets

  • They're signed in and land on next. A first visit makes them a learner, attached to their company's account by email domain like anyone else (see Accounts & learners).
  • Someone already signed in as them stays signed in. Anyone else signed in on that browser is signed out first.
  • A link that no longer works (used, expired, or turned off) lands on your academy's sign-in page, which says so and offers to sign in again (an emailed link, or your product while its sign-in page is set). An expired link, or one refused while sign-in is off, still goes to next after it; a used one has forgotten where it was going.
  • People who open the academy directly sign in with an emailed link, or through your product once you give its login or its sign-in page (above). The same address is the same learner either way.

What a sign-in from your product can't do

A link, or your login's answer, carries your product's word that this is the person; it doesn't prove they own the inbox. So it reaches only as far as your academy, as a learner:

  • No admin. Anyone with a seat on Admin → Team signs in with their email; the API refuses their address, and so does the login.
  • No address changes. Someone who came in through your product can't change their email or add a recovery email on their profile. Sign in with your email on the profile is the way: a person's addresses are theirs at every academy they learn at, so they change only with proof of the inbox.
  • Their name, while it shows only here. They can change their name on their profile while they learn at your academy alone. Once they also learn at another academy on Mastria, or sit on an academy's team, the name shows there too, and changing it takes Sign in with your email.
  • Only this academy. The session works on your academy's addresses and nowhere else on Mastria.

Deleting a learner also erases the links you asked for them and haven't been used (see Deleting a learner).