diff --git a/apps/docs/content/docs/meta.json b/apps/docs/content/docs/meta.json
index 909f9d4b..aeb2bfea 100644
--- a/apps/docs/content/docs/meta.json
+++ b/apps/docs/content/docs/meta.json
@@ -16,7 +16,10 @@
"community",
"organization",
"student-dashboard",
+
+ "---Enterprise---",
"enterprise-sso",
+ "token-auth",
"---Get started---",
"create-first-course",
diff --git a/apps/docs/content/docs/token-auth.mdx b/apps/docs/content/docs/token-auth.mdx
new file mode 100644
index 00000000..2d43ff8b
--- /dev/null
+++ b/apps/docs/content/docs/token-auth.mdx
@@ -0,0 +1,176 @@
+---
+title: Token Auth
+description: Sign users in to ClassroomIO from your own app with a short-lived JWT.
+---
+
+import { Callout } from 'fumadocs-ui/components/callout';
+import { Step, Steps } from 'fumadocs-ui/components/steps';
+
+# Token Auth
+
+Token Auth lets an external app sign a user in to ClassroomIO without asking for a ClassroomIO password. Your backend signs a short-lived JWT with your organization's Token Auth secret, then redirects the user's browser to ClassroomIO's token exchange URL.
+
+Use Token Auth when you already authenticate users in another product and want a trusted handoff into your ClassroomIO academy.
+
+
+ Token Auth requires an **Enterprise** plan.
+
+ **Cloud:** [Contact us](mailto:support@classroomio.com) to upgrade.
+
+ **Self-hosted:** Requires an Enterprise license key. Set `LICENSE_KEY` in the API service environment.
+
+
+## How It Works
+
+1. An organization admin generates a Token Auth signing secret in ClassroomIO.
+2. Your backend authenticates the user in your own app.
+3. Your backend signs a short-lived `HS256` JWT with the ClassroomIO signing secret.
+4. Your app redirects the browser to `/api/auth/token-exchange?token=...&redirect=...`.
+5. ClassroomIO verifies the token, finds or creates the user, adds them to the organization, creates a session, and redirects them to the requested path.
+
+## Set Up Token Auth
+
+
+
+ Sign in to ClassroomIO as an organization admin.
+
+
+ Open **Settings** → **Authentication** → **Token Auth**.
+
+
+ Click **Generate secret** and copy the secret immediately. The full secret is shown only once.
+
+
+ Store the secret in your backend secret manager or environment variables.
+
+
+ Enable Token Auth after your integration is ready.
+
+
+
+## Token Exchange URL
+
+Cloud:
+
+```text
+https://api.classroomio.com/api/auth/token-exchange
+```
+
+Self-hosted:
+
+```text
+{PUBLIC_SERVER_URL}/api/auth/token-exchange
+```
+
+Query parameters:
+
+| Parameter | Required | Description |
+| --- | --- | --- |
+| `token` | Yes | The signed JWT. |
+| `redirect` | No | A ClassroomIO path to open after login. Must start with `/`. Invalid external URLs are ignored and users are sent to `/`. |
+
+Example:
+
+```text
+https://api.classroomio.com/api/auth/token-exchange?token=eyJ...&redirect=/courses/my-course
+```
+
+## JWT Requirements
+
+Token Auth accepts `HS256` JWTs signed with your organization's Token Auth secret.
+
+Required claims:
+
+| Claim | Type | Description |
+| --- | --- | --- |
+| `sub` | string | Stable user identifier from your system. |
+| `email` | string | User email address. ClassroomIO lowercases it before lookup. |
+| `iat` | number | Issued-at timestamp. |
+| `exp` | number | Expiration timestamp. |
+
+Optional claims:
+
+| Claim | Type | Description |
+| --- | --- | --- |
+| `name` | string | Display name. If omitted, ClassroomIO uses the email username. |
+| `avatar` | string | Public image URL for the user's avatar. |
+
+Tokens must be fresh. ClassroomIO rejects tokens older than 5 minutes, so use a short expiration such as 2 minutes.
+
+## Node Example
+
+```js
+import { SignJWT } from 'jose';
+
+const tokenAuthSecret = process.env.CLASSROOMIO_TOKEN_AUTH_SECRET;
+const classroomioAuthUrl = 'https://api.classroomio.com/api/auth/token-exchange';
+
+export async function buildClassroomioLoginUrl(user) {
+ const secret = new TextEncoder().encode(tokenAuthSecret);
+
+ const token = await new SignJWT({
+ sub: user.id,
+ email: user.email,
+ name: user.name,
+ avatar: user.avatarUrl
+ })
+ .setProtectedHeader({ alg: 'HS256' })
+ .setIssuedAt()
+ .setExpirationTime('2m')
+ .sign(secret);
+
+ const url = new URL(classroomioAuthUrl);
+ url.searchParams.set('token', token);
+ url.searchParams.set('redirect', '/');
+
+ return url.toString();
+}
+```
+
+Redirect the browser to the returned URL after the user clicks "Open ClassroomIO" or any equivalent action in your app.
+
+## User Provisioning
+
+When the token is valid:
+
+- If a ClassroomIO user already exists with the email address, that user is signed in.
+- If no user exists, ClassroomIO creates one with a random password.
+- ClassroomIO adds the user to the organization tied to the signing secret.
+- If the email has a pending invite or pre-created audience row, ClassroomIO links that record first. Otherwise, the user joins with the organization's default role.
+- If `avatar` is present, ClassroomIO updates the user's profile avatar.
+
+## Rotating the Secret
+
+Rotate the secret from **Settings** → **Authentication** → **Token Auth** when a secret may have been exposed or as part of regular credential rotation.
+
+After rotation, update your backend to sign new tokens with the new secret. Old tokens signed with the previous secret stop working.
+
+## Troubleshooting
+
+### "No active token auth configured"
+
+Token Auth is not enabled for the organization. Generate a secret and enable Token Auth in the dashboard.
+
+### "Invalid token"
+
+Check that:
+
+- The token uses `HS256`.
+- The token is signed with the current Token Auth secret.
+- The token has not expired.
+- The token was issued less than 5 minutes ago.
+
+### "Invalid token payload"
+
+Verify that the JWT includes `sub`, `email`, `iat`, and `exp`, and that `email` is a valid email address.
+
+### Redirect sends users to `/`
+
+The `redirect` value must be a relative path that starts with `/`. External URLs are ignored for safety.
+
+## Security Notes
+
+- Sign tokens only on your backend. Never expose the Token Auth secret in browser code.
+- Keep token lifetimes short. Two minutes is usually enough for a browser redirect.
+- Rotate the secret immediately if it is logged, leaked, or committed.
+- Use HTTPS in production.