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, and both work for Federated Apps and Inbound Apps:
- The
/introspectendpoint (RFC 7662) is for a resource server that has its own client credentials. The resource server authenticates as a confidential app and gets back whether the token is active, plus the user's current roles and permissions. An inactive token returns{"active": false}rather than an error. - The
/userinfoendpoint needs no client credentials. 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 token to its matching endpoint:
| App type | Introspection | UserInfo | Reference |
|---|---|---|---|
| Federated Application (OIDC) | {baseUrl}/oauth2/v1/introspect | {baseUrl}/oauth2/v1/userinfo | OIDC endpoints |
| Inbound App and Agentic Client | {baseUrl}/oauth2/v1/apps/introspect | {baseUrl}/oauth2/v1/apps/userinfo | Get UserInfo, Post UserInfo |
| Inbound App (project-scoped) | {baseUrl}/oauth2/v1/apps/{projectId}/introspect | {baseUrl}/oauth2/v1/apps/{projectId}/userinfo | Same behavior as the routes above. Some MCP discovery documents advertise these paths. |
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 or /oauth2/v1/introspect, and a Federated Application access token won't work against the /oauth2/v1/apps routes. 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.
Inbound App /introspect Endpoint
Inbound Apps have their own RFC 7662 endpoint for tokens issued to Inbound Apps, MCP clients, and Agentic Clients. It works the same way as the Federated App endpoint, with a few differences that come from the Inbound App model.
curl -X POST "__BaseURL__/oauth2/v1/apps/introspect" \
-u "<CLIENT_ID>:<CLIENT_SECRET>" \
-d "token=<ACCESS_TOKEN>" \
-d "token_type_hint=access_token"The project-scoped route, POST {baseUrl}/oauth2/v1/apps/{projectId}/introspect, behaves the same way. Use it when your Inbound App has an imported client ID, because an imported client ID doesn't encode the project.
Who Can Introspect
The caller must authenticate as a confidential Inbound App in the same project, with a client secret over HTTP Basic or in the body, or with another client authentication method configured on the app. Public clients can't call the endpoint.
A caller only sees tokens that concern it. The caller must be the client the token was issued to, or be named in the token's aud. For any other token, Descope returns {"active": false}. A resource server therefore registers as its own confidential Inbound App, and introspects the tokens issued for its audience.
What the Response Contains
Descope accepts access tokens and refresh tokens. The optional token_type_hint is access_token or refresh_token.
An active token returns the standard claims plus the user's current claims for the token's scopes, the same as UserInfo. Descope also checks the user's consent again. When a user revokes consent, the token becomes inactive. When consent no longer covers some scopes, scope lists only the scopes that consent still grants.
A token from the client credentials grant has no user, so it returns only the standard claims.
Discovery
Newer projects advertise introspection_endpoint and introspection_endpoint_auth_methods_supported in the Inbound App discovery document. If yours doesn't list them, the endpoint still works, and you can contact Descope Support to have it advertised.
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
The UserInfo endpoint under the /apps path reads an Inbound App token's current claims without client credentials, using only the user's access token. 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 /introspect or /userinfo from the gateway or the service. Otherwise, validate locally and accept that claims can lag until the token expires.