Token Introspection
Validating an access token locally confirms that Descope issued it and that it hasn't expired. The claims inside it, such as roles, permissions, tenant, and custom attributes, reflect what was true when the token was issued. If an admin removes a role or revokes access afterward, a locally validated token keeps working until it expires.
Token introspection closes that gap. Instead of trusting the claims in the token, your server asks Descope about the token on each request: whether it's still active, and what the user's current roles and permissions are. You get live authorization data and revocations that take effect immediately, at the cost of a network call to Descope on every request you introspect.
For local JWT validation, see Session Validation.
When to Introspect
Introspection is optional, and most APIs and MCP servers don't need it. Validate tokens locally when:
- You run high-throughput microservices or internet-facing endpoints where every millisecond of latency matters
- You can accept role, permission, and revocation changes taking effect when the user's access token next expires and refreshes
Introspect when:
- You handle highly sensitive data, such as banking or healthcare records, and can't tolerate the 5 to 10 minutes it can take for a short-lived access token to expire after access is revoked
- Roles, permissions, or tenant assignments can change mid-session, and the change must apply on the next request
- You need custom attributes or JWT template output that isn't in the token
If you need fresher data than the token carries but can tolerate a short lag, cache introspection responses for a short TTL. That keeps most requests local while bounding how stale the authorization data can get.
The active tenant (dct) is fixed for the life of a token, so introspection won't help with switching tenants. To switch, the user disconnects and signs in again with a different organization, which issues a new token. See Users in More Than One Tenant for more details.
Note
The Management SDK or API can also read a user's current state, but it requires a management key, which is a privileged server credential. The endpoints on this page authenticate with the access token the client already sent, or with the app's own client credentials, which is why MCP servers and third-party APIs prefer them.
Choosing an Endpoint
Descope offers two ways to introspect a token:
- The
/introspectendpoint (RFC 7662) accepts Federated Application tokens only. Your resource server authenticates with its own client credentials and gets back whether the token is active, plus the user's current roles and permissions - The
/userinfoendpoint works for every app type, including Inbound Apps and Agentic Clients. Your server sends the user's access token, and Descope returns the user's current claims, or an error if the token is no longer valid
Federated Applications and Inbound Apps use different routes, and the routes aren't interchangeable. Send each app type's access token to its matching endpoint:
| App type | Endpoint | Issued by | API reference |
|---|---|---|---|
| Federated Application (OIDC) | {baseUrl}/oauth2/v1/introspect or {baseUrl}/oauth2/v1/userinfo | Standard OIDC authorization code, client credentials, or device flows against a Federated App | OIDC endpoints |
| Inbound App | {baseUrl}/oauth2/v1/apps/userinfo | Inbound App /oauth2/v1/apps/token flows | Get UserInfo, Post UserInfo |
| Inbound App (project-scoped) | {baseUrl}/oauth2/v1/apps/{projectId}/userinfo | Same Inbound App tokens; path advertised in some MCP discovery documents | Same behavior as /oauth2/v1/apps/userinfo |
Replace {baseUrl} with your project base URL, for example https://api.descope.com, a regional host, or a custom domain.
Note
An Inbound App access token won't work against /oauth2/v1/userinfo, and a Federated Application access token won't work against /oauth2/v1/apps/userinfo. If you're unsure which type you have, check the app's discovery document or the token's issuer.
Federated Application /introspect Endpoint
Use the RFC 7662 introspection endpoint when a resource server needs to ask Descope whether a Federated Application token is still active. The resource server authenticates with its own client credentials, not the user's token.
POST {baseUrl}/oauth2/v1/introspect
Content-Type: application/x-www-form-urlencoded
Authorization: Basic <base64(client_id:client_secret)>
token=<access_token>curl -X POST "__BaseURL__/oauth2/v1/introspect" \
-u "<CLIENT_ID>:<CLIENT_SECRET>" \
-d "token=<ACCESS_TOKEN>"To target a specific SSO application, use POST {baseUrl}/{ssoAppId}/oauth2/v1/introspect.
Authentication
The endpoint requires client authentication, so public clients can't call it. Send the app's client ID and secret with HTTP Basic (client_secret_basic) or in the request body (client_secret_post). Descope identifies the project and app from the caller's client_id, not from the token being introspected.
Response
An inactive, expired, revoked, or unknown token returns HTTP 200 with {"active": false}, not an error, so check the active field rather than the status code.
An active token returns the standard claims (active, scope, client_id, sub, aud, iss, exp, iat, nbf, jti, token_type) plus the user's current roles and permissions. Descope loads the roles and permissions the same way as /userinfo, so your resource server authorizes against live data rather than the claims from when the token was issued.
Discovery
The Federated App's discovery document advertises introspection_endpoint and introspection_endpoint_auth_methods_supported (RFC 8414), so standards-based resource servers and gateways can find the endpoint automatically.
Note
/introspect currently accepts Federated Application tokens only. For Inbound App tokens, including tokens issued to MCP clients and Agentic Clients, use the Inbound App /userinfo endpoint.
Federated Application /userinfo Endpoint
Federated Applications also support the standard OIDC UserInfo endpoint, which authenticates with the user's access token instead of the app's client credentials:
GET {baseUrl}/oauth2/v1/userinfo
Authorization: Bearer <access_token>curl -X GET "__BaseURL__/oauth2/v1/userinfo" \
-H "Authorization: Bearer <ACCESS_TOKEN>"The response includes standard OIDC profile claims, plus custom claims and tenant roles and permissions depending on the requested scopes. See Using OIDC Endpoints for the full flow.
Inbound App /userinfo Endpoint
Inbound Apps don't have an /introspect endpoint, so the UserInfo endpoint under the /apps path is how you introspect their tokens. It supports both GET and POST.
GET {baseUrl}/oauth2/v1/apps/userinfo
Authorization: Bearer <access_token>curl -X GET "__BaseURL__/oauth2/v1/apps/userinfo" \
-H "Authorization: Bearer <ACCESS_TOKEN>"A successful response means Descope still accepts the token for that app, and the returned claims are safe to use for authorization. Any other response means the token is invalid or revoked, so reject the request.
The claims reflect the user's current profile as Descope resolves it for that token, following your JWT template and the granted scopes.
Some clients, including MCP discovery metadata, advertise the project-scoped variant {baseUrl}/oauth2/v1/apps/{projectId}/userinfo. It accepts the same Inbound App access tokens.
Introspection at a Gateway
A gateway that only checks signatures, such as a JWT authorizer, validates tokens at the edge but doesn't return live claims. If the services behind the gateway need current authorization data, call /userinfo, or /introspect for Federated App tokens, from the gateway or the service. Otherwise, validate locally and accept that claims can lag until the token expires.