Developer sign-in required
The Developer & Integration section is restricted to signed-in developer accounts. Sign in with your AI2N.ai developer credentials to continue.
Authentication
This selector is illustrative β there is no live public API to switch against yet. All requests/responses below use non-production example data.
API version: v1 Β· Status: Illustrative β pre-release
The API uses OIDC/OAuth 2.1. Interactive users authenticate through Microsoft Entra External ID; server-to-server integrations use a service account client-credentials flow. Every token carries role, tenant and privilege claims that the API enforces on every request.
Never commit real client secrets. Every example uses placeholder values read from environment variables β treat any credential shown as an example as compromised, not usable.
Service account token exchange
Server-to-server integrations exchange a client ID and secret for a short-lived access token.
curl -X POST https://auth.ai2n.ai/oauth2/token \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=client_credentials" \ -d "client_id=$AI2N_CLIENT_ID" \ -d "client_secret=$AI2N_CLIENT_SECRET" \ -d "scope=curriculum:read enrollment:read"
const res = await fetch("https://auth.ai2n.ai/oauth2/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: process.env.AI2N_CLIENT_ID!,
client_secret: process.env.AI2N_CLIENT_SECRET!,
scope: "curriculum:read enrollment:read",
}),
});
const { access_token, expires_in } = await res.json();
var client = new HttpClient();
var form = new Dictionary<string, string> {
["grant_type"] = "client_credentials",
["client_id"] = Environment.GetEnvironmentVariable("AI2N_CLIENT_ID"),
["client_secret"] = Environment.GetEnvironmentVariable("AI2N_CLIENT_SECRET"),
["scope"] = "curriculum:read enrollment:read",
};
var resp = await client.PostAsync(
"https://auth.ai2n.ai/oauth2/token",
new FormUrlEncodedContent(form));
var json = await resp.Content.ReadAsStringAsync();
import os, requests
resp = requests.post(
"https://auth.ai2n.ai/oauth2/token",
data={
"grant_type": "client_credentials",
"client_id": os.environ["AI2N_CLIENT_ID"],
"client_secret": os.environ["AI2N_CLIENT_SECRET"],
"scope": "curriculum:read enrollment:read",
},
)
access_token = resp.json()["access_token"]
π AI2N_CLIENT_ID / AI2N_CLIENT_SECRET are read from environment variables in every example β never hardcoded.
SSO / Microsoft Entra integration
Interactive admin sign-in also supports Microsoft Entra External ID as an alternative to a platform username/password β currently scoped to admin and global_admin accounts whose email is already on file, not every role.
- Request an authorization URL, then send the user to it. It carries a one-time, short-lived
statevalue used to prevent replay. - Microsoft redirects back with an authorization code.
- Exchange that code (plus the original
state) for tokens. The state is validated and immediately invalidated β a code/state pair can't be reused. - The signed-in Microsoft account's email is matched against an existing
admin/global_adminaccount by email. There is no auto-provisioning β Entra sign-in only works for an account that already exists with a matching email.
curl https://api.ai2n.ai/api/auth/entra/config
A configured: false response means Microsoft Entra sign-in isn't set up for this environment β fall back to username/password.
Scopes
| Scope | Grants |
|---|---|
curriculum:read | Read published curriculum content and metadata. |
enrollment:read | Read enrollment and progress records within the caller's tenant scope. |
webhooks:manage | Create and manage webhook subscriptions. |
content:publish | Publish approved content revisions β privileged, requires an approver-scoped service account. |
Step-up authentication
Interactive privileged operations (role assignment, publication, impersonation) require MFA/step-up regardless of the base session's authentication strength β the API rejects a privileged write from a token that hasn't stepped up with 403 insufficient_auth_strength.