Access API user guide
1. How authentication works
RADKit Access does not maintain its own user database. All user identities come from external Identity Providers (IdPs) — Duo, Okta, or CDNA. When you authenticate, RADKit validates your identity against the IdP and issues its own short-lived session token on your behalf.
The only partial exception is certificate-based authentication: RADKit services can authenticate using client certificates issued by the RADKit CA, where the identity is derived from the certificate subject rather than an upstream IdP. API tokens and client credentials (4. Getting long-lived credentials) are also RADKit-managed, but they are always tied to an identity that was originally established through an IdP.
Every RADKit Access API call is authenticated via the Authorization header.
There are two ways to authenticate:
JWT credentials (API token, Duo/Okta/CDNA JWT) can be used directly on any endpoint with
Authorization: JWT <token>. The server validates the credential and internally mints a short-lived session token for that request. However, the recommended pattern is to exchange first (see 2. Getting a session token) so you get a reusableBearersession token that you can use across multiple calls without re-presenting the long-lived credential each time.Client credentials (
client_id/client_secret) cannot be used directly. They must always be exchanged for a session token atPOST /oauth2/token.
The recommended pattern for automation is therefore:
┌─────────────────────────────┐ exchange ┌─────────────────────┐
│ long-lived credential │ ──────────────────► │ session token │
│ (API token / client creds │ │ (radkit_access_ │
│ / Duo or Okta JWT) │ │ token, 1 h TTL) │
└─────────────────────────────┘ └──────────┬──────────┘
│
Authorization: Bearer <token>
│
▼
API calls (GET, POST, …)
Header formats used in this guide
Header value |
When used |
|---|---|
|
Presenting a JWT credential directly to any
endpoint, or exchanging it at
|
|
Exchanging client credentials at
|
|
Authenticating every API call after obtaining a session token. |
Note
The token exchange response contains two fields — access_token and
radkit_access_token. For all programmatic flows (API token, client credentials,
direct JWT exchange) both fields carry the same RADKit-signed JWT. Always use
radkit_access_token as the Bearer token; access_token exists for integrations
that expect a standard OAuth2 response shape.
2. Getting a session token
2.1 From an API token or an external JWT (Duo / Okta / CDNA)
Pass any JWT-based credential using the JWT scheme. This covers:
RADKit API tokens (see 4.1 API tokens (recommended for automation) for how to generate one)
Duo / Okta / CDNA JWTs obtained through your SSO provider
Method:
POSTURL:
/auth/tokenAuth:
Authorization: JWT <your_token>
Example:
curl -X POST 'https://{{baseURL}}/auth/token' \
-H 'Authorization: JWT eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9...'
Success response (HTTP 200):
{
"access_token": "eyJhbGciOi...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_remaining_lifetime": 600,
"radkit_access_token": "eyJhbGciOi...",
"identity": "automation@example.com",
"admin_level": 0
}
Error response (HTTP 401):
{
"success": false,
"details": "Invalid credentials",
"retry": false
}
2.2 From client credentials (OAuth2 client credentials grant)
Pass client_id:client_secret as HTTP Basic auth (Base64-encoded).
Method:
POSTURL:
/oauth2/tokenAuth:
Authorization: Basic <base64(client_id:client_secret)>Query / form param:
grant_type=client_credentials
Example:
curl -X POST 'https://{{baseURL}}/oauth2/token?grant_type=client_credentials' \
-H 'Authorization: Basic bXlfY2xpZW50X2lkOm15X3NlY3JldA=='
Success response (HTTP 200):
{
"access_token": "eyJhbGciOi...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": ""
}
Note
The /oauth2/token response uses the standard OAuth2 shape and does not include
a separate radkit_access_token field. The returned access_token is a
RADKit-signed JWT — use it directly as Authorization: Bearer <access_token>.
3. Using the session token
Include the session token in every API call:
Authorization: Bearer <radkit_access_token>
Example:
curl 'https://{{baseURL}}/public/admin/service/status/my-service-id' \
-H 'Authorization: Bearer eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9...'
Session tokens expire after 1 hour by default. Refresh (see 6.2 Refresh a session token) before expiry or re-exchange the original long-lived credential.
4. Getting long-lived credentials
You need a long-lived credential before you can obtain a session token (see 2. Getting a session token). Generating API tokens and client credentials requires elevated admin claims on your account.
Important
To obtain the required admin claims, contact the RADKit team. Without them the
POST /admin/api/token and POST /admin/client_credentials endpoints will return
HTTP 403.
Important
Never hard-code credentials or API tokens in source code. Store them in environment variables, a secrets manager, or a vault.
4.1 API tokens (recommended for automation)
API tokens are long-lived JWTs signed by RADKit Access. Default lifetime: 7 days; maximum: 180 days. Up to 50 active tokens per user.
Generate via the Python SDK:
from radkit_client.sync import sso_login
client = sso_login("someone@example.com")
api_token = client.admin_client().generate_api_token(lifetime=30) # 30 days
print(api_token.token.token) # the JWT string — store this securely
print(api_token.expires_at)
Generate via REST:
Method:
POSTURL:
/admin/api/tokenAuth:
Authorization: Bearer <radkit_access_token>
Request body:
{
"endpoint_id": "automation@example.com",
"lifetime": 30
}
endpoint_id — the identity (email) to associate the token with.
lifetime — lifetime in days (1–180).
Example:
curl -X POST 'https://{{baseURL}}/admin/api/token' \
-H 'Authorization: Bearer eyJhbGciOi...' \
-H 'Content-Type: application/json' \
-d '{"endpoint_id": "automation@example.com", "lifetime": 30}'
Success response (HTTP 200):
{
"token": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9...",
"expires_at": "2026-04-10T12:00:00Z"
}
List existing API tokens:
curl 'https://{{baseURL}}/admin/api/token' \
-H 'Authorization: Bearer eyJhbGciOi...'
4.2 Client credentials
Client credentials provide a standard OAuth2 client_id / client_secret pair.
Lifetime: 1–180 days.
Generate via the Python SDK:
from radkit_client.sync import sso_login
client = sso_login("someone@example.com")
creds = client.admin_client().generate_client_credentials()
print(creds.client_id)
print(creds.client_secret) # shown only once — store immediately
print(creds.expires_at)
Generate via REST:
Method:
POSTURL:
/admin/client_credentialsAuth:
Authorization: Bearer <radkit_access_token>
Request body:
{
"endpoint_id": "automation@example.com",
"lifetime": 7,
"description": "CI pipeline token"
}
Example:
curl -X POST 'https://{{baseURL}}/admin/client_credentials' \
-H 'Authorization: Bearer eyJhbGciOi...' \
-H 'Content-Type: application/json' \
-d '{"endpoint_id": "automation@example.com", "lifetime": 7, "description": "CI"}'
Success response (HTTP 200):
{
"client_id": "550e8400-e29b-41d4-a716-446655440000",
"client_secret": "plaintext-secret-shown-once",
"expires_at": "2026-03-18T12:00:00Z"
}
Important
client_secret is displayed only once at creation time and is never retrievable
afterwards. Store it immediately in a secure location.
Note
Tokens obtained via client credentials cannot generate further API tokens or client
credentials, and cannot access most /admin/* management endpoints.
5. Revoking tokens
5.1 Revoke a session token
Immediately invalidates the current session. Subsequent calls with the same token return HTTP 401.
Method:
DELETEURL:
/auth/tokenAuth:
Authorization: Bearer <radkit_access_token>
Example:
curl -X DELETE 'https://{{baseURL}}/auth/token' \
-H 'Authorization: Bearer eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9...'
Returns HTTP 204 No Content on success.
Note
Passing radkit_access_token removes only the RADKit session — the upstream Duo / Okta
token at the provider is not revoked. To revoke all active sessions including the
upstream provider token, use POST /auth/oauth/logout.
5.2 Revoke a client-credentials token
Revokes a token issued by POST /oauth2/token. Requires the original client credentials.
Method:
POSTURL:
/oauth2/revokeAuth:
Authorization: Basic <base64(client_id:client_secret)>Form param:
token=<access_token_to_revoke>
Example:
curl -X POST 'https://{{baseURL}}/oauth2/revoke' \
-H 'Authorization: Basic bXlfY2xpZW50X2lkOm15X3NlY3JldA==' \
--data-urlencode 'token=eyJhbGciOi...'
Returns HTTP 204 No Content on success.
5.3 Revoke API token(s)
Revoke one or more API tokens by JWT string, by jti claim, or in bulk by issuance date.
At least one of the three fields must be provided.
Method:
DELETEURL:
/admin/api/tokenAuth:
Authorization: Bearer <radkit_access_token>
Request body:
{
"tokens": ["eyJhbGciOi..."],
"jtis": ["a1b2c3d4e5f6..."],
"issued_before": "2026-01-01T00:00:00Z"
}
Field |
Effect |
|---|---|
|
Revoke by full JWT string |
|
Revoke by the |
|
Revoke all API tokens issued before this ISO 8601 date |
Example (revoke a single token by JWT):
curl -X DELETE 'https://{{baseURL}}/admin/api/token' \
-H 'Authorization: Bearer eyJhbGciOi...' \
-H 'Content-Type: application/json' \
-d '{"tokens": ["eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9..."]}'
Example (revoke all tokens issued before a date):
curl -X DELETE 'https://{{baseURL}}/admin/api/token' \
-H 'Authorization: Bearer eyJhbGciOi...' \
-H 'Content-Type: application/json' \
-d '{"issued_before": "2026-01-01T00:00:00Z"}'
Returns HTTP 204 No Content on success.
6. Token introspection and refresh
6.1 Inspect a session token
Returns the current state of the session token without decoding the JWT locally.
Method:
GETURL:
/auth/tokenAuth:
Authorization: Bearer <radkit_access_token>
Example:
curl 'https://{{baseURL}}/auth/token' \
-H 'Authorization: Bearer eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9...'
Response (HTTP 200):
{
"active": true,
"type": "client_access_token",
"expires_at": 1741996800,
"expires_in": 3542,
"user_info": {
"type": "OAuthUser",
"id": "automation@example.com",
"provider": "duo",
"max_admin_level": 51,
"claims": []
}
}
If the token is expired or not found, active will be false.
6.2 Refresh a session token
Extends an active session without re-presenting the original credential.
Method:
PUTURL:
/auth/tokenAuth:
Authorization: Bearer <radkit_access_token>
Example:
curl -X PUT 'https://{{baseURL}}/auth/token' \
-H 'Authorization: Bearer eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9...'
Returns the same AuthResponse shape as 2.1 From an API token or an external JWT (Duo / Okta / CDNA).
Behaviour by credential type
Session origin |
What refresh does |
|---|---|
API token |
Issues a new RADKit-signed |
Duo / Okta JWT (direct exchange, no SSO) |
Issues a new RADKit-signed |
Client credentials |
Not supported. Returns HTTP 403. Re-exchange
|
Note
Refresh is a no-op if more than 600 seconds remain on the current session — the
existing token is returned unchanged. The refresh_remaining_lifetime field in every
AuthResponse tells you this threshold so you can schedule refresh calls correctly.
7. JWKS and OIDC discovery
RADKit Access acts as an OIDC relying party — it delegates authentication to upstream providers (Duo, Okta) and issues its own short-lived JWTs signed with RS512. It is not a full OIDC identity provider: there is no authorization endpoint and no ID token.
OIDC-compatible discovery document (endpoint locations):
curl 'https://{{baseURL}}/.well-known/openid-configuration'
Response:
{
"issuer": "https://{{baseURL}}",
"jwks_uri": "https://{{baseURL}}/oauth2/keys",
"token_endpoint": "https://{{baseURL}}/oauth2/token",
"revocation_endpoint": "https://{{baseURL}}/oauth2/revoke",
"userinfo_endpoint": "https://{{baseURL}}/oauth2/user_info"
}
JWKS — public keys for offline token verification
External services that receive a radkit_access_token can verify its signature using the
public keys at /oauth2/keys without contacting RADKit Access:
curl 'https://{{baseURL}}/oauth2/keys'
Response:
{
"keys": [
{
"kty": "RSA",
"alg": "RS512",
"use": "sig",
"kid": "a3f2b1c4...",
"n": "0vx7agoebG...",
"e": "AQAB"
}
]
}
Keys are rotated every 30 days; up to 6 keys are retained to cover recently issued tokens.
8. Public API endpoints
8.1 Generate a service enrollment OTP
Method:
POSTURL:
/public/otp/certificateAuth:
Authorization: Bearer <radkit_access_token>
Request body:
{
"owner": "user@example.com",
"endpoint_id": "endpoint-identifier",
"description": "CI enrollment"
}
Example:
curl -X POST 'https://{{baseURL}}/public/otp/certificate' \
-H 'Authorization: Bearer eyJhbGciOi...' \
-H 'Content-Type: application/json' \
-d '{"owner": "user@example.com", "endpoint_id": "my-service", "description": "CI"}'
Success response (HTTP 200):
{ "otp": "abcd-1234-efgh-5678" }
8.2 Get service status
Method:
GETURL:
/public/admin/service/status/{service_id}Auth:
Authorization: Bearer <radkit_access_token>
Example:
curl 'https://{{baseURL}}/public/admin/service/status/123-123-123' \
-H 'Authorization: Bearer eyJhbGciOi...'
Success response (HTTP 200):
{
"service_id": "123-123-123",
"active": true,
"online": true
}
9. Common errors
Code |
Meaning |
Action |
|---|---|---|
400 |
Bad request parameters |
Check request body and query parameters |
401 |
Invalid or expired token |
Re-exchange your credential (see 2. Getting a session token) |
403 |
Forbidden |
Verify account claims / permissions |
404 |
Resource not found |
Check service ID or resource path |
500 |
Server error |
Contact RADKit Access Cloud support |
10. Appendix
Base URL: replace
{{baseURL}}with your environment URL, e.g.https://devel.radkit-cloud.cisco.com.Session token TTL: 3600 s (1 hour) by default.
Refresh window: session refreshed only if < 600 s remain (
PUT /auth/token).API token max lifetime: 180 days; max 50 active tokens per user.
Client credentials max lifetime: 180 days.
JWT signing algorithm: RS512 (RSA-2048) by default; ES256 also supported.
Content-Type:
application/jsonfor all request bodies.Discover endpoints at runtime: always prefer
/.well-known/openid-configurationover hard-coding URLs.Download Postman Collection: Download here