Authentication
The Platform API has two kinds of caller, and they never share a credential.
| Caller | Credential | Where it works |
|---|---|---|
| Your server | Tenant API key → ten-minute machine token | /platform/v1/* only |
| A learner’s browser | Their own MTP portal session, opened by a sign-in ticket | The portal and the session gateway |
A machine token is refused by every route outside /platform/v1; a portal session is refused
by /platform/v1. Neither can be turned into the other.
API keys
A tenant administrator with the MANAGE_API_KEYS permission creates keys at
Organization admin → API keys. Each key has:
- a
client_idof the formmtp_followed by 24 hex characters, - a
client_secret, shown once at creation and never recoverable, - a fixed set of scopes, and
- an optional expiry.
Revoking a key refuses its outstanding tokens on their next use. Scopes cannot be edited; make a new key.
Scopes
| Scope | Grants |
|---|---|
content.read | List published content items (summaries; never documents) |
learners.read | Look up learners in the organization by email or id |
enrollments.read | Read a learner’s course enrollments and progress |
enrollments.write | Enroll a learner in a published course |
learners.sign_in | Mint single-use sign-in tickets that open a learner’s own portal session |
Grant the narrowest set that does the job. A call the key is not scoped for answers 403.
Getting a machine token
POST /oauth/token implements the OAuth 2.0 client credentials grant (RFC 6749 §4.4).
POST /oauth/token
Content-Type: application/json
{
"grant_type": "client_credentials",
"client_id": "mtp_0123456789abcdef01234567",
"client_secret": "…"
}{
"access_token": "eyJ…",
"token_type": "Bearer",
"expires_in": 600,
"scope": "content.read enrollments.read"
}Present the token as Authorization: Bearer …. Tokens live ten minutes; cache one and refresh
it shortly before it expires. A 401 from any Platform API route means the token has expired
or its key was revoked: fetch a new token once and retry, then treat a second 401 as final.
Unknown keys, wrong secrets, revoked and expired keys all answer the same 401 from the token
endpoint, and each attempt is recorded in the tenant’s audit trail.
The client secret belongs on a server. Never ship it to a browser or a mobile app; give those a learner session through a sign-in ticket instead.
Tenant scope
A key belongs to exactly one organization. Every Platform API call acts in that organization,
so there is no organization id to pass and no way to address another tenant. Resources outside
the tenant answer 404, never 403.