Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
307 changes: 149 additions & 158 deletions src/content/docs/developer-tools/sdks/backend/python-sdk.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1386,239 +1386,230 @@ You don't need to manually manage tokens or sessions - the SDK handles this auto

## Management API

The Kinde Python SDK provides a Management API client for interacting with Kinde's management endpoints. This allows you to programmatically manage users, organizations, and other resources. The Management API supports both sync and async patterns.
The Kinde Python SDK includes a separate Management API client for interacting with Kinde's management endpoints. This allows you to programmatically manage users, organizations, roles, permissions, feature flags, and other resources.

### Getting started
The Management API client is independent of the OAuth authentication clients (`OAuth`, `AsyncOAuth`, `SmartOAuth`). You initialize it directly with your management API credentials.

#### With OAuth client (Framework-based)
### Getting started

```python
from kinde_sdk.auth.oauth import OAuth
from flask import Flask
import asyncio
from kinde_sdk.management.management_client import ManagementClient

app = Flask(__name__)
oauth = OAuth(framework="flask", app=app)
management = ManagementClient(
domain="your-domain.kinde.com",
client_id="your-management-client-id",
client_secret="your-management-client-secret"
)
```

# Get the management client
management = oauth.get_management()
You can also load credentials from a `.env` file. This requires the `python-dotenv` package:

# Use with asyncio in Flask
def list_users_sync():
loop = asyncio.get_event_loop()
users = loop.run_until_complete(management.get_users())
return users
```bash
pip install python-dotenv
```

#### With AsyncOAuth client (Native async)
Call `load_dotenv()` before initializing `ManagementClient` so the environment variables are available:

```python
from kinde_sdk.auth.async_oauth import AsyncOAuth

oauth = AsyncOAuth()
import os
from dotenv import load_dotenv
from kinde_sdk.management.management_client import ManagementClient

# Get the management client (native async)
management = await oauth.get_management()
load_dotenv()

# All methods are async
users = await management.get_users()
management = ManagementClient(
domain=os.getenv("KINDE_HOST", "").replace("https://", ""),
client_id=os.getenv("MGMT_API_CLIENT_ID"),
client_secret=os.getenv("MGMT_API_CLIENT_SECRET")
)
```

#### With SmartOAuth client (Context-aware)
### Available endpoints

The `ManagementClient` dynamically discovers all auto-generated API classes and exposes them as snake_case properties. Access endpoints through these API class properties:

**User management:**

```python
from kinde_sdk.auth.smart_oauth import SmartOAuth
# List users
users = management.users_api.get_users(page_size=10)

# Get a specific user
user = management.users_api.get_user_data(id="user_123")

# Create a new user
new_user = management.users_api.create_user(
create_user_request={
"profile": {
"given_name": "John",
"family_name": "Doe"
},
"identities": [{
"type": "email",
"details": {"email": "user@example.com"}
}]
}
)

oauth = SmartOAuth()
# Update a user
management.users_api.update_user(
id="user_123",
update_user_request={"given_name": "Johnny"}
)

# Works in async context
async def async_get_users():
management = await oauth.get_management()
return await management.get_users()

# Works in sync context (if supported)
def sync_get_users():
management = oauth.get_management()
return management.get_users()
# Delete a user
management.users_api.delete_user(id="user_123")
```

### Available endpoints
**Other common API classes:**

The Management API provides methods for common operations on resources. All examples use async patterns:
```python
management.organizations_api # Organization management
management.roles_api # Role management
management.permissions_api # Permission management
management.feature_flags_api # Feature flag management
management.applications_api # Application management
management.subscribers_api # Subscriber management
management.connections_api # Connection management
management.webhooks_api # Webhook management
```

**User management:**
When new API classes are added to the generated module, they are automatically available on the client without any code changes.

### Organization management

```python
# List users (async)
users = await management.get_users()
from kinde_sdk.management.management_client import ManagementClient

# Get a specific user (async)
user = await management.get_user(user_id="user_123")
management = ManagementClient(
domain="your-domain.kinde.com",
client_id="your-management-client-id",
client_secret="your-management-client-secret"
)

# List organizations
orgs = management.organizations_api.get_organizations(page_size=10)

# Get a specific organization
org = management.organizations_api.get_organization(code="org_1234")

# Create a new user (async)
new_user = await management.create_user(
email="user@example.com",
given_name="John",
family_name="Doe"
# Create a new organization
new_org = management.organizations_api.create_organization(
create_organization_request={"name": "My Organization"}
)

# Update a user (async)
updated_user = await management.update_user(
user_id="user_123",
given_name="Johnny"
# Update an organization
management.organizations_api.update_organization(
org_code="org_1234",
update_organization_request={"name": "Updated Name"}
)

# Delete a user (async)
await management.delete_user(user_id="user_123")
# Delete an organization
management.organizations_api.delete_organization(org_code="org_1234")
```

### Organization management

**Using Management API with FastAPI (OAuth client):**
**Using the Management API in a FastAPI application:**

```python
from fastapi import FastAPI, HTTPException
from kinde_sdk.auth.oauth import OAuth
from kinde_sdk.management.management_client import ManagementClient

app = FastAPI()
oauth = OAuth(framework="fastapi", app=app)
management = ManagementClient(
domain="your-domain.kinde.com",
client_id="your-management-client-id",
client_secret="your-management-client-secret"
)

@app.get("/organizations")
async def list_organizations():
management = oauth.get_management()
orgs = await management.get_organizations()
def list_organizations():
orgs = management.organizations_api.get_organizations()
return orgs

@app.get("/organizations/{org_id}")
async def get_organization(org_id: str):
management = oauth.get_management()
org = await management.get_organization(org_id=org_id)
@app.get("/organizations/{org_code}")
def get_organization(org_code: str):
org = management.organizations_api.get_organization(code=org_code)
return org

@app.post("/organizations")
async def create_organization(name: str):
management = oauth.get_management()
new_org = await management.create_organization(name=name)
return new_org

@app.put("/organizations/{org_id}")
async def update_organization(org_id: str, name: str):
management = oauth.get_management()
updated_org = await management.update_organization(
org_id=org_id,
name=name
def create_organization(name: str):
new_org = management.organizations_api.create_organization(
create_organization_request={"name": name}
)
return updated_org
return new_org

@app.delete("/organizations/{org_id}")
async def delete_organization(org_id: str):
management = oauth.get_management()
await management.delete_organization(org_id=org_id)
@app.delete("/organizations/{org_code}")
def delete_organization(org_code: str):
management.organizations_api.delete_organization(org_code=org_code)
return {"message": "Organization deleted"}
```

**Using Management API with AsyncOAuth client:**
### Error handling

The Management API methods raise exceptions for HTTP errors. Wrap calls in try/except blocks:

```python
from kinde_sdk.auth.async_oauth import AsyncOAuth
from kinde_sdk.management.management_client import ManagementClient

oauth = AsyncOAuth()
management = ManagementClient(
domain="your-domain.kinde.com",
client_id="your-management-client-id",
client_secret="your-management-client-secret"
)

async def manage_organizations():
# Get management client
management = await oauth.get_management()

# List organizations
orgs = await management.get_organizations()

# Get a specific organization
org = await management.get_organization(org_id="org_123")

# Create a new organization
new_org = await management.create_organization(
name="My Organization"
)

# Update an organization
updated_org = await management.update_organization(
org_id="org_123",
name="Updated Name"
)

# Delete an organization
await management.delete_organization(org_id="org_123")

return orgs
try:
user = management.users_api.get_user_data(id="user_123")
except Exception as e:
print(f"Error: {e}")
```

### Error handling

The Management API methods will raise exceptions for API errors. It's recommended to handle these appropriately:

**Example with OAuth client (FastAPI):**
**In a FastAPI application:**

```python
import logging
from fastapi import FastAPI, HTTPException
from kinde_sdk.auth.oauth import OAuth
from kinde_sdk.exceptions import KindeAPIException
from kinde_sdk.management.management_client import ManagementClient
from kinde_sdk.management.exceptions import ApiException

logger = logging.getLogger(__name__)

app = FastAPI()
oauth = OAuth(framework="fastapi", app=app)
management = ManagementClient(
domain="your-domain.kinde.com",
client_id="your-management-client-id",
client_secret="your-management-client-secret"
)

@app.get("/users/{user_id}")
async def get_user(user_id: str):
management = oauth.get_management()
try:
user = await management.get_user(user_id=user_id)
return user
except KindeAPIException as e:
raise HTTPException(status_code=e.status_code, detail=str(e))
except Exception as e:
raise HTTPException(status_code=500, detail=f"Internal error: {str(e)}")
```

**Example with AsyncOAuth client:**

```python
from kinde_sdk.auth.async_oauth import AsyncOAuth
from kinde_sdk.exceptions import KindeAPIException

oauth = AsyncOAuth()

async def get_user_safely(user_id: str):
management = await oauth.get_management()
def get_user(user_id: str):
try:
user = await management.get_user(user_id=user_id)
return user
except KindeAPIException as e:
print(f"API Error {e.status_code}: {e.message}")
return None
return management.users_api.get_user_data(id=user_id)
except ApiException as e:
logger.error("Kinde API error: %s %s", e.status, e.body)
raise HTTPException(status_code=e.status, detail="Failed to fetch user")
except Exception as e:
print(f"Unexpected error: {str(e)}")
return None
logger.exception("Unexpected error fetching user %s", user_id)
raise HTTPException(status_code=500, detail="Internal server error")
```

### Token management

The Management API client has its own token management system for API authentication, which is separate from the core SDK's user session token management. The Management API client automatically handles:
The `ManagementClient` has its own token management system that is separate from the OAuth authentication clients. It uses the OAuth2 client credentials flow and automatically handles:

- **accessing Kinde Management API endpoints**: Obtains tokens for accessing Kinde's management endpoints
- **Token refresh**: Automatically refreshes management API tokens when they expire
- **Token storage**: Securely stores management API tokens
- **Thread safety**: Ensures thread-safe token handling for concurrent requests
- **Token acquisition**: Obtains tokens using a client credentials grant on the first API call
- **Token caching**: Tokens are cached and reused to avoid unnecessary requests
- **Automatic re-acquisition**: Requests new tokens via a client credentials grant when existing ones near expiry (60 seconds before expiration)
- **Shared tokens**: Multiple `ManagementClient` instances with the same domain and client ID share the same token

You don't need to manually manage Management API tokens - the client handles this for you. This is different from the core SDK's user session token management, which handles user authentication tokens automatically.
You don't need to manually manage tokens -- the client handles this for you. Note that credentials are validated on the first API call, not during client initialization. If your credentials are incorrect, the error will surface when you make your first request.
Comment on lines +1597 to +1604
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's find if there's a kinde-python-sdk directory or reference in the repository
find . -type d -name "*python*" -o -type d -name "*kinde*" 2>/dev/null | head -20

Repository: kinde-oss/documentation

Length of output: 280


🏁 Script executed:

# Search for any references to ManagementClient and token refresh in the current repo
rg -i "managementclient" --type=md -A 3 -B 3 | head -50

Repository: kinde-oss/documentation

Length of output: 49


🏁 Script executed:

# Look for the specific documentation file mentioned
find . -path "*python-sdk.mdx" 2>/dev/null

Repository: kinde-oss/documentation

Length of output: 129


🏁 Script executed:

# Read the specific lines mentioned in the review
sed -n '1590,1610p' ./src/content/docs/developer-tools/sdks/backend/python-sdk.mdx

Repository: kinde-oss/documentation

Length of output: 1565


🌐 Web query:

kinde-python-sdk ManagementClient token refresh 60 seconds

💡 Result:

In kinde-python-sdk, the ManagementClient / Management API client uses the OAuth2 client credentials flow, so it doesn’t use a refresh token; instead it caches the management access token and automatically fetches a new one when it expires. Multiple client instances with the same domain + client id can share the cached token. [1]

If you configure your Kinde application’s access token lifetime to 60 seconds, then the management access token will naturally expire every ~60 seconds and the SDK will renew it that often. Token lifetimes are configured in the Kinde portal per application (Settings → Environment → Applications → View details → Tokens → set expiry in seconds). [2]

Kinde’s Python SDK docs also note the Management API client’s token handling is separate from user-session token management and that it auto-refreshes management API tokens on expiry. [3]

Sources:
[1] Kinde Python SDK repo (Token Management section) (github.com)
[2] Kinde docs: Configure token and session expiry (docs.kinde.com)
[3] Kinde docs: Python SDK → Management API → Token management (docs.kinde.com)

Citations:


🌐 Web query:

kinde-python-sdk github repository

💡 Result:

The kinde-python-sdk GitHub repository is: https://github.com/kinde-oss/kinde-python-sdk [1].

(Starter kit repo: https://github.com/kinde-starter-kits/python-starter-kit [2].)


🏁 Script executed:

# Let's look at the kinde-python-sdk repository to verify the actual token refresh logic
# Search for token refresh/renewal code references
curl -s "https://api.github.com/repos/kinde-oss/kinde-python-sdk/contents/" | jq -r '.[].name' | head -20

Repository: kinde-oss/documentation

Length of output: 131


🏁 Script executed:

# Search within the documentation repo for any other token-related discussions
rg -i "60 seconds|token.*expir" ./src/content/docs/developer-tools/sdks/ -A 2 -B 2 --type=md | head -60

Repository: kinde-oss/documentation

Length of output: 49


Clarify ManagementClient token refresh behavior — it's not a hardcoded 60-second buffer.

The statement "Requests new tokens via a client credentials grant when existing ones near expiry (60 seconds before expiration)" is misleading. The SDK doesn't have a hardcoded 60-second pre-expiry refresh. Instead, token lifetime is configurable in the Kinde portal per application, and the SDK automatically refreshes when tokens expire. The "60 seconds" only applies if the application's token lifetime is configured to 60 seconds.

Rephrase to: "Requests new tokens via a client credentials grant when existing tokens expire" and remove the specific timeout reference. The token caching and shared-token-across-instances claims are accurate.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/content/docs/developer-tools/sdks/backend/python-sdk.mdx` around lines
1597 - 1604, Update the documentation text describing ManagementClient's refresh
behavior: remove the hardcoded "60 seconds before expiration" claim and instead
state that the client requests new tokens via the client credentials grant when
existing tokens expire, and that token lifetime is configurable in the Kinde
portal per application; adjust the sentence referencing "Automatic
re-acquisition" to reflect this and keep the rest of the token
acquisition/caching/shared-token wording unchanged (refer to the
ManagementClient description block).


### Best practices

1. **Always use async/await when calling Management API methods**: The Management API is async-native for better performance
2. **Handle API errors appropriately**: Use try/except blocks and handle `KindeAPIException` specifically
1. **Use the API class properties**: Always use `management.users_api`, `management.organizations_api`, etc. The top-level convenience methods (e.g. `management.get_users()`) are deprecated
2. **Handle API errors appropriately**: Use try/except blocks for all Management API calls
3. **Cache results when appropriate**: Reduce API calls by caching user data, organizations, and permissions
4. **Use appropriate error handling for production**: Implement logging, monitoring, and graceful error recovery
5. **Keep your client credentials secure**: Use environment variables, never commit secrets to version control
6. **Use connection pooling**: For high-traffic applications, configure HTTP connection pooling
7. **Implement retry logic**: Add retry logic with exponential backoff for transient failures
8. **Monitor token expiration**: Handle token refresh gracefully to avoid authentication failures
4. **Keep your client credentials secure**: Use environment variables, never commit secrets to version control
5. **Implement retry logic**: Add retry logic with exponential backoff for transient failures

For more information about the Management API endpoints and capabilities, see the [Kinde Management API documentation](https://docs.kinde.com/kinde-apis/management/).

Expand Down