chub-dev 0.1.0 → 0.1.2-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/README.md +55 -0
  2. package/bin/chub-mcp +2 -0
  3. package/dist/airtable/docs/database/javascript/DOC.md +1437 -0
  4. package/dist/airtable/docs/database/python/DOC.md +1735 -0
  5. package/dist/amplitude/docs/analytics/javascript/DOC.md +1282 -0
  6. package/dist/amplitude/docs/analytics/python/DOC.md +1199 -0
  7. package/dist/anthropic/docs/claude-api/javascript/DOC.md +503 -0
  8. package/dist/anthropic/docs/claude-api/python/DOC.md +389 -0
  9. package/dist/asana/docs/tasks/DOC.md +1396 -0
  10. package/dist/assemblyai/docs/transcription/DOC.md +1043 -0
  11. package/dist/atlassian/docs/confluence/javascript/DOC.md +1347 -0
  12. package/dist/atlassian/docs/confluence/python/DOC.md +1604 -0
  13. package/dist/auth0/docs/identity/javascript/DOC.md +968 -0
  14. package/dist/auth0/docs/identity/python/DOC.md +1199 -0
  15. package/dist/aws/docs/s3/javascript/DOC.md +1773 -0
  16. package/dist/aws/docs/s3/python/DOC.md +1807 -0
  17. package/dist/binance/docs/trading/javascript/DOC.md +1315 -0
  18. package/dist/binance/docs/trading/python/DOC.md +1454 -0
  19. package/dist/braintree/docs/gateway/javascript/DOC.md +1278 -0
  20. package/dist/braintree/docs/gateway/python/DOC.md +1179 -0
  21. package/dist/chromadb/docs/embeddings-db/javascript/DOC.md +1263 -0
  22. package/dist/chromadb/docs/embeddings-db/python/DOC.md +1707 -0
  23. package/dist/clerk/docs/auth/javascript/DOC.md +1220 -0
  24. package/dist/clerk/docs/auth/python/DOC.md +274 -0
  25. package/dist/cloudflare/docs/workers/javascript/DOC.md +918 -0
  26. package/dist/cloudflare/docs/workers/python/DOC.md +994 -0
  27. package/dist/cockroachdb/docs/distributed-db/DOC.md +1500 -0
  28. package/dist/cohere/docs/llm/DOC.md +1335 -0
  29. package/dist/datadog/docs/monitoring/javascript/DOC.md +1740 -0
  30. package/dist/datadog/docs/monitoring/python/DOC.md +1815 -0
  31. package/dist/deepgram/docs/speech/javascript/DOC.md +885 -0
  32. package/dist/deepgram/docs/speech/python/DOC.md +685 -0
  33. package/dist/deepl/docs/translation/javascript/DOC.md +887 -0
  34. package/dist/deepl/docs/translation/python/DOC.md +944 -0
  35. package/dist/deepseek/docs/llm/DOC.md +1220 -0
  36. package/dist/directus/docs/headless-cms/javascript/DOC.md +1128 -0
  37. package/dist/directus/docs/headless-cms/python/DOC.md +1276 -0
  38. package/dist/discord/docs/bot/javascript/DOC.md +1090 -0
  39. package/dist/discord/docs/bot/python/DOC.md +1130 -0
  40. package/dist/elasticsearch/docs/search/DOC.md +1634 -0
  41. package/dist/elevenlabs/docs/text-to-speech/javascript/DOC.md +336 -0
  42. package/dist/elevenlabs/docs/text-to-speech/python/DOC.md +552 -0
  43. package/dist/firebase/docs/auth/DOC.md +1015 -0
  44. package/dist/gemini/docs/genai/javascript/DOC.md +691 -0
  45. package/dist/gemini/docs/genai/python/DOC.md +555 -0
  46. package/dist/github/docs/octokit/DOC.md +1560 -0
  47. package/dist/google/docs/bigquery/javascript/DOC.md +1688 -0
  48. package/dist/google/docs/bigquery/python/DOC.md +1503 -0
  49. package/dist/hubspot/docs/crm/javascript/DOC.md +1805 -0
  50. package/dist/hubspot/docs/crm/python/DOC.md +2033 -0
  51. package/dist/huggingface/docs/transformers/DOC.md +948 -0
  52. package/dist/intercom/docs/messaging/javascript/DOC.md +1844 -0
  53. package/dist/intercom/docs/messaging/python/DOC.md +1797 -0
  54. package/dist/jira/docs/issues/javascript/DOC.md +1420 -0
  55. package/dist/jira/docs/issues/python/DOC.md +1492 -0
  56. package/dist/kafka/docs/streaming/javascript/DOC.md +1671 -0
  57. package/dist/kafka/docs/streaming/python/DOC.md +1464 -0
  58. package/dist/landingai-ade/docs/api/DOC.md +620 -0
  59. package/dist/landingai-ade/docs/sdk/python/DOC.md +489 -0
  60. package/dist/landingai-ade/docs/sdk/typescript/DOC.md +542 -0
  61. package/dist/landingai-ade/skills/SKILL.md +489 -0
  62. package/dist/launchdarkly/docs/feature-flags/javascript/DOC.md +1191 -0
  63. package/dist/launchdarkly/docs/feature-flags/python/DOC.md +1671 -0
  64. package/dist/linear/docs/tracker/DOC.md +1554 -0
  65. package/dist/livekit/docs/realtime/javascript/DOC.md +303 -0
  66. package/dist/livekit/docs/realtime/python/DOC.md +163 -0
  67. package/dist/mailchimp/docs/marketing/DOC.md +1420 -0
  68. package/dist/meilisearch/docs/search/DOC.md +1241 -0
  69. package/dist/microsoft/docs/onedrive/javascript/DOC.md +1421 -0
  70. package/dist/microsoft/docs/onedrive/python/DOC.md +1549 -0
  71. package/dist/mongodb/docs/atlas/DOC.md +2041 -0
  72. package/dist/notion/docs/workspace-api/javascript/DOC.md +1435 -0
  73. package/dist/notion/docs/workspace-api/python/DOC.md +1400 -0
  74. package/dist/okta/docs/identity/javascript/DOC.md +1171 -0
  75. package/dist/okta/docs/identity/python/DOC.md +1401 -0
  76. package/dist/openai/docs/chat/javascript/DOC.md +407 -0
  77. package/dist/openai/docs/chat/python/DOC.md +568 -0
  78. package/dist/paypal/docs/checkout/DOC.md +278 -0
  79. package/dist/pinecone/docs/sdk/javascript/DOC.md +984 -0
  80. package/dist/pinecone/docs/sdk/python/DOC.md +1395 -0
  81. package/dist/plaid/docs/banking/javascript/DOC.md +1163 -0
  82. package/dist/plaid/docs/banking/python/DOC.md +1203 -0
  83. package/dist/playwright-community/skills/login-flows/SKILL.md +108 -0
  84. package/dist/postmark/docs/transactional-email/DOC.md +1168 -0
  85. package/dist/prisma/docs/orm/javascript/DOC.md +1419 -0
  86. package/dist/prisma/docs/orm/python/DOC.md +1317 -0
  87. package/dist/qdrant/docs/vector-search/javascript/DOC.md +1221 -0
  88. package/dist/qdrant/docs/vector-search/python/DOC.md +1653 -0
  89. package/dist/rabbitmq/docs/message-queue/javascript/DOC.md +1193 -0
  90. package/dist/rabbitmq/docs/message-queue/python/DOC.md +1243 -0
  91. package/dist/razorpay/docs/payments/javascript/DOC.md +1219 -0
  92. package/dist/razorpay/docs/payments/python/DOC.md +1330 -0
  93. package/dist/redis/docs/key-value/javascript/DOC.md +1851 -0
  94. package/dist/redis/docs/key-value/python/DOC.md +2054 -0
  95. package/dist/registry.json +2817 -0
  96. package/dist/replicate/docs/model-hosting/DOC.md +1318 -0
  97. package/dist/resend/docs/email/DOC.md +1271 -0
  98. package/dist/salesforce/docs/crm/javascript/DOC.md +1241 -0
  99. package/dist/salesforce/docs/crm/python/DOC.md +1183 -0
  100. package/dist/search-index.json +1 -0
  101. package/dist/sendgrid/docs/email-api/javascript/DOC.md +371 -0
  102. package/dist/sendgrid/docs/email-api/python/DOC.md +656 -0
  103. package/dist/sentry/docs/error-tracking/javascript/DOC.md +1073 -0
  104. package/dist/sentry/docs/error-tracking/python/DOC.md +1309 -0
  105. package/dist/shopify/docs/storefront/DOC.md +457 -0
  106. package/dist/slack/docs/workspace/javascript/DOC.md +933 -0
  107. package/dist/slack/docs/workspace/python/DOC.md +271 -0
  108. package/dist/square/docs/payments/javascript/DOC.md +1855 -0
  109. package/dist/square/docs/payments/python/DOC.md +1728 -0
  110. package/dist/stripe/docs/api/DOC.md +1727 -0
  111. package/dist/stripe/docs/payments/DOC.md +1726 -0
  112. package/dist/stytch/docs/auth/javascript/DOC.md +1813 -0
  113. package/dist/stytch/docs/auth/python/DOC.md +1962 -0
  114. package/dist/supabase/docs/client/DOC.md +1606 -0
  115. package/dist/twilio/docs/messaging/python/DOC.md +469 -0
  116. package/dist/twilio/docs/messaging/typescript/DOC.md +946 -0
  117. package/dist/vercel/docs/platform/DOC.md +1940 -0
  118. package/dist/weaviate/docs/vector-db/javascript/DOC.md +1268 -0
  119. package/dist/weaviate/docs/vector-db/python/DOC.md +1388 -0
  120. package/dist/zendesk/docs/support/javascript/DOC.md +2150 -0
  121. package/dist/zendesk/docs/support/python/DOC.md +2297 -0
  122. package/package.json +22 -6
  123. package/skills/get-api-docs/SKILL.md +84 -0
  124. package/src/commands/annotate.js +83 -0
  125. package/src/commands/build.js +12 -1
  126. package/src/commands/feedback.js +150 -0
  127. package/src/commands/get.js +83 -42
  128. package/src/commands/search.js +7 -0
  129. package/src/index.js +43 -17
  130. package/src/lib/analytics.js +90 -0
  131. package/src/lib/annotations.js +57 -0
  132. package/src/lib/bm25.js +170 -0
  133. package/src/lib/cache.js +69 -6
  134. package/src/lib/config.js +8 -3
  135. package/src/lib/identity.js +99 -0
  136. package/src/lib/registry.js +103 -20
  137. package/src/lib/telemetry.js +86 -0
  138. package/src/mcp/server.js +177 -0
  139. package/src/mcp/tools.js +251 -0
@@ -0,0 +1,1401 @@
1
+ ---
2
+ name: identity
3
+ description: "Okta Python SDK coding guidelines for the Okta Management API using official libraries"
4
+ metadata:
5
+ languages: "python"
6
+ versions: "2.9.13"
7
+ updated-on: "2026-03-02"
8
+ source: maintainer
9
+ tags: "okta,identity,sso,oauth,authentication"
10
+ ---
11
+
12
+ # Okta Python SDK Coding Guidelines
13
+
14
+ You are an Okta API coding expert. Help me with writing code using the Okta Management API calling the official libraries and SDKs.
15
+
16
+ You can find the official SDK documentation and code samples here:
17
+ https://developer.okta.com/docs/reference/api/users/
18
+ https://github.com/okta/okta-sdk-python
19
+
20
+ ## Golden Rule: Use the Correct and Current SDK
21
+
22
+ Always use the official Okta Python SDK to interact with the Okta Management API. This is the standard library for all Okta Management API interactions. Do not construct manual HTTP requests or use unofficial libraries.
23
+
24
+ - **Library Name:** Okta Python SDK
25
+ - **PyPI Package:** `okta`
26
+ - **Current Version:** 2.9.13
27
+ - **Important:** This SDK is for the Management API only. For Authentication API, construct your own HTTP requests.
28
+
29
+ **Installation:**
30
+
31
+ - **Correct:** `pip install okta`
32
+
33
+ **APIs and Usage:**
34
+
35
+ - **Correct:** `from okta.client import Client as OktaClient`
36
+ - **Correct:** `client = OktaClient(config)`
37
+ - **Correct:** `await client.create_user(user_request)`
38
+ - **Correct:** `users, resp, err = await client.list_users()`
39
+ - **Incorrect:** Manual HTTP requests to Management API endpoints
40
+ - **Incorrect:** Using unofficial Okta Python packages
41
+
42
+ ## System Requirements
43
+
44
+ The Okta Python SDK requires:
45
+ - Python 3.9 or higher
46
+ - An Okta organization URL
47
+ - An API token or OAuth 2.0 credentials
48
+
49
+ ## Initialization and API Authentication
50
+
51
+ The Okta Python SDK uses asynchronous operations with `async`/`await` syntax throughout.
52
+
53
+ ### API Token Authentication (Simple)
54
+
55
+ ```python
56
+ from okta.client import Client as OktaClient
57
+ import asyncio
58
+ import os
59
+
60
+ config = {
61
+ 'orgUrl': os.getenv('OKTA_ORG_URL'),
62
+ 'token': os.getenv('OKTA_API_TOKEN')
63
+ }
64
+
65
+ client = OktaClient(config)
66
+ ```
67
+
68
+ ### OAuth 2.0 Private Key Authentication (Recommended for Service Apps)
69
+
70
+ When using OAuth 2.0 with private key authentication, you don't need an API token. The SDK automatically requests access tokens.
71
+
72
+ ```python
73
+ from okta.client import Client as OktaClient
74
+ import os
75
+
76
+ config = {
77
+ 'orgUrl': os.getenv('OKTA_ORG_URL'),
78
+ 'authorizationMode': 'PrivateKey',
79
+ 'clientId': os.getenv('OKTA_CLIENT_ID'),
80
+ 'scopes': ['okta.users.manage', 'okta.groups.manage'],
81
+ 'privateKey': os.getenv('OKTA_PRIVATE_KEY'), # JWK JSON string or PEM format
82
+ 'kid': os.getenv('OKTA_KEY_ID') # Optional if kid is in JWK
83
+ }
84
+
85
+ client = OktaClient(config)
86
+ ```
87
+
88
+ ### Async Event Loop Pattern
89
+
90
+ All SDK operations are asynchronous and must be executed within an event loop:
91
+
92
+ ```python
93
+ import asyncio
94
+ from okta.client import Client as OktaClient
95
+
96
+ async def main():
97
+ config = {
98
+ 'orgUrl': 'https://dev-1234567.okta.com',
99
+ 'token': 'your_api_token'
100
+ }
101
+ client = OktaClient(config)
102
+
103
+ users, resp, err = await client.list_users()
104
+ print(f"Total users: {len(users)}")
105
+
106
+ # Run the async function
107
+ loop = asyncio.get_event_loop()
108
+ loop.run_until_complete(main())
109
+ ```
110
+
111
+ ### Response Pattern
112
+
113
+ All SDK methods return a tuple of `(result, response, error)`:
114
+
115
+ ```python
116
+ users, resp, err = await client.list_users()
117
+
118
+ if err:
119
+ print(f"Error occurred: {err}")
120
+ else:
121
+ print(f"Retrieved {len(users)} users")
122
+ ```
123
+
124
+ ## User Management
125
+
126
+ ### Create a User
127
+
128
+ Create a new user with profile information and password:
129
+
130
+ ```python
131
+ from okta.client import Client as OktaClient
132
+ from okta.models import UserProfile, PasswordCredential, UserCredentials, CreateUserRequest
133
+ import asyncio
134
+
135
+ async def create_user():
136
+ config = {
137
+ 'orgUrl': 'https://dev-1234567.okta.com',
138
+ 'token': 'your_api_token'
139
+ }
140
+ client = OktaClient(config)
141
+
142
+ # Create user profile
143
+ user_profile = UserProfile({
144
+ 'firstName': 'John',
145
+ 'lastName': 'Doe',
146
+ 'email': 'john.doe@example.com',
147
+ 'login': 'john.doe@example.com'
148
+ })
149
+
150
+ # Create password credential
151
+ password_credential = PasswordCredential({
152
+ 'value': 'SecurePassword123!'
153
+ })
154
+
155
+ user_credentials = UserCredentials({
156
+ 'password': password_credential
157
+ })
158
+
159
+ # Create user request
160
+ create_user_request = CreateUserRequest({
161
+ 'profile': user_profile,
162
+ 'credentials': user_credentials
163
+ })
164
+
165
+ # Create the user
166
+ user, resp, err = await client.create_user(create_user_request)
167
+
168
+ if err:
169
+ print(f"Error creating user: {err}")
170
+ else:
171
+ print(f"Created user: {user.id}")
172
+ return user
173
+
174
+ asyncio.run(create_user())
175
+ ```
176
+
177
+ ### Create User with Activation
178
+
179
+ Create and automatically activate a user:
180
+
181
+ ```python
182
+ async def create_and_activate_user():
183
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
184
+ client = OktaClient(config)
185
+
186
+ user_profile = UserProfile({
187
+ 'firstName': 'Jane',
188
+ 'lastName': 'Smith',
189
+ 'email': 'jane.smith@example.com',
190
+ 'login': 'jane.smith@example.com',
191
+ 'mobilePhone': '555-123-4567'
192
+ })
193
+
194
+ password_credential = PasswordCredential({'value': 'SecurePassword123!'})
195
+ user_credentials = UserCredentials({'password': password_credential})
196
+
197
+ create_user_request = CreateUserRequest({
198
+ 'profile': user_profile,
199
+ 'credentials': user_credentials
200
+ })
201
+
202
+ # Activate on creation
203
+ user, resp, err = await client.create_user(
204
+ create_user_request,
205
+ activate=True,
206
+ provider=False
207
+ )
208
+
209
+ if err:
210
+ print(f"Error: {err}")
211
+ else:
212
+ print(f"Created and activated user: {user.id}")
213
+ return user
214
+
215
+ asyncio.run(create_and_activate_user())
216
+ ```
217
+
218
+ ### Get a User
219
+
220
+ Retrieve a user by ID or login:
221
+
222
+ ```python
223
+ async def get_user(user_id):
224
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
225
+ client = OktaClient(config)
226
+
227
+ # By user ID or login email
228
+ user, resp, err = await client.get_user(user_id)
229
+
230
+ if err:
231
+ print(f"Error: {err}")
232
+ else:
233
+ print(f"User: {user.profile.first_name} {user.profile.last_name}")
234
+ print(f"Email: {user.profile.email}")
235
+ print(f"Status: {user.status}")
236
+ return user
237
+
238
+ # Can use ID or email
239
+ asyncio.run(get_user('00u1234567890abcdef'))
240
+ asyncio.run(get_user('john.doe@example.com'))
241
+ ```
242
+
243
+ ### List All Users
244
+
245
+ Retrieve all users in your organization:
246
+
247
+ ```python
248
+ async def list_all_users():
249
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
250
+ client = OktaClient(config)
251
+
252
+ users, resp, err = await client.list_users()
253
+
254
+ if err:
255
+ print(f"Error: {err}")
256
+ else:
257
+ print(f"Total users: {len(users)}")
258
+ for user in users:
259
+ print(f" - {user.profile.first_name} {user.profile.last_name} ({user.profile.email})")
260
+ return users
261
+
262
+ asyncio.run(list_all_users())
263
+ ```
264
+
265
+ ### List Users with Query Parameters
266
+
267
+ Filter users with query parameters:
268
+
269
+ ```python
270
+ async def list_users_with_filters():
271
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
272
+ client = OktaClient(config)
273
+
274
+ # Search by query string
275
+ query_params = {'q': 'Robert'}
276
+ users, resp, err = await client.list_users(query_params)
277
+
278
+ if err:
279
+ print(f"Error: {err}")
280
+ else:
281
+ print(f"Found {len(users)} users matching 'Robert'")
282
+ for user in users:
283
+ print(f" - {user.profile.email}")
284
+
285
+ asyncio.run(list_users_with_filters())
286
+ ```
287
+
288
+ ### Search Users with SCIM Filter
289
+
290
+ Use SCIM expressions for precise filtering:
291
+
292
+ ```python
293
+ async def search_users_scim():
294
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
295
+ client = OktaClient(config)
296
+
297
+ # SCIM filter
298
+ query_params = {
299
+ 'search': 'profile.nickName eq "bobby"'
300
+ }
301
+ users, resp, err = await client.list_users(query_params)
302
+
303
+ if err:
304
+ print(f"Error: {err}")
305
+ else:
306
+ for user in users:
307
+ print(f"Found: {user.profile.email}")
308
+
309
+ asyncio.run(search_users_scim())
310
+ ```
311
+
312
+ ### Filter Users by Time
313
+
314
+ Find users updated after a specific time:
315
+
316
+ ```python
317
+ async def find_recently_updated_users():
318
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
319
+ client = OktaClient(config)
320
+
321
+ query_params = {
322
+ 'filter': 'lastUpdated gt "2025-01-01T00:00:00.000Z"'
323
+ }
324
+ users, resp, err = await client.list_users(query_params)
325
+
326
+ if err:
327
+ print(f"Error: {err}")
328
+ else:
329
+ print(f"Found {len(users)} recently updated users")
330
+ for user in users:
331
+ print(f" - {user.profile.email} (updated: {user.last_updated})")
332
+
333
+ asyncio.run(find_recently_updated_users())
334
+ ```
335
+
336
+ ### Update a User
337
+
338
+ Modify user profile information:
339
+
340
+ ```python
341
+ async def update_user(user_id):
342
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
343
+ client = OktaClient(config)
344
+
345
+ # Get the user first
346
+ user, resp, err = await client.get_user(user_id)
347
+
348
+ if err:
349
+ print(f"Error getting user: {err}")
350
+ return
351
+
352
+ # Update profile fields
353
+ user.profile.nick_name = 'Johnny'
354
+ user.profile.mobile_phone = '+1-555-123-4567'
355
+ user.profile.department = 'Engineering'
356
+
357
+ # Save changes
358
+ updated_user, resp, err = await client.update_user(user_id, user)
359
+
360
+ if err:
361
+ print(f"Error updating user: {err}")
362
+ else:
363
+ print(f"User updated successfully: {updated_user.id}")
364
+
365
+ asyncio.run(update_user('00u1234567890abcdef'))
366
+ ```
367
+
368
+ ### Partial Update a User
369
+
370
+ Update specific fields without retrieving the full user object:
371
+
372
+ ```python
373
+ from okta.models import User
374
+
375
+ async def partial_update_user(user_id):
376
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
377
+ client = OktaClient(config)
378
+
379
+ # Create a user object with only the fields to update
380
+ user = User({
381
+ 'profile': {
382
+ 'nickName': 'JD',
383
+ 'department': 'Engineering'
384
+ }
385
+ })
386
+
387
+ updated_user, resp, err = await client.update_user(user_id, user)
388
+
389
+ if err:
390
+ print(f"Error: {err}")
391
+ else:
392
+ print(f"User partially updated: {updated_user.id}")
393
+
394
+ asyncio.run(partial_update_user('00u1234567890abcdef'))
395
+ ```
396
+
397
+ ### Deactivate a User
398
+
399
+ Deactivate a user account:
400
+
401
+ ```python
402
+ async def deactivate_user(user_id):
403
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
404
+ client = OktaClient(config)
405
+
406
+ user, resp, err = await client.deactivate_user(user_id)
407
+
408
+ if err:
409
+ print(f"Error deactivating user: {err}")
410
+ else:
411
+ print(f"User deactivated: {user.id}")
412
+
413
+ asyncio.run(deactivate_user('00u1234567890abcdef'))
414
+ ```
415
+
416
+ ### Delete a User
417
+
418
+ Permanently delete a user (must be deactivated first):
419
+
420
+ ```python
421
+ async def delete_user(user_id):
422
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
423
+ client = OktaClient(config)
424
+
425
+ # First deactivate
426
+ _, resp, err = await client.deactivate_user(user_id)
427
+ if err:
428
+ print(f"Error deactivating: {err}")
429
+ return
430
+
431
+ # Then delete
432
+ resp, err = await client.delete_user(user_id)
433
+
434
+ if err:
435
+ print(f"Error deleting user: {err}")
436
+ else:
437
+ print(f"User deleted permanently")
438
+
439
+ asyncio.run(delete_user('00u1234567890abcdef'))
440
+ ```
441
+
442
+ ### Reactivate a User
443
+
444
+ Reactivate a previously deactivated user:
445
+
446
+ ```python
447
+ async def reactivate_user(user_id):
448
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
449
+ client = OktaClient(config)
450
+
451
+ query_params = {'sendEmail': False}
452
+ user, resp, err = await client.activate_user(user_id, query_params)
453
+
454
+ if err:
455
+ print(f"Error reactivating user: {err}")
456
+ else:
457
+ print(f"User reactivated: {user.id}")
458
+
459
+ asyncio.run(reactivate_user('00u1234567890abcdef'))
460
+ ```
461
+
462
+ ### Suspend and Unsuspend User
463
+
464
+ Temporarily suspend a user:
465
+
466
+ ```python
467
+ async def suspend_user(user_id):
468
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
469
+ client = OktaClient(config)
470
+
471
+ user, resp, err = await client.suspend_user(user_id)
472
+
473
+ if err:
474
+ print(f"Error suspending user: {err}")
475
+ else:
476
+ print(f"User suspended: {user.id}")
477
+
478
+ async def unsuspend_user(user_id):
479
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
480
+ client = OktaClient(config)
481
+
482
+ user, resp, err = await client.unsuspend_user(user_id)
483
+
484
+ if err:
485
+ print(f"Error unsuspending user: {err}")
486
+ else:
487
+ print(f"User unsuspended: {user.id}")
488
+
489
+ asyncio.run(suspend_user('00u1234567890abcdef'))
490
+ asyncio.run(unsuspend_user('00u1234567890abcdef'))
491
+ ```
492
+
493
+ ### Reset User Password
494
+
495
+ Send a password reset email:
496
+
497
+ ```python
498
+ async def reset_user_password(user_id):
499
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
500
+ client = OktaClient(config)
501
+
502
+ query_params = {'sendEmail': True}
503
+ reset_token, resp, err = await client.reset_password(user_id, query_params)
504
+
505
+ if err:
506
+ print(f"Error resetting password: {err}")
507
+ else:
508
+ print(f"Password reset email sent")
509
+
510
+ asyncio.run(reset_user_password('00u1234567890abcdef'))
511
+ ```
512
+
513
+ ### Expire User Password
514
+
515
+ Force a user to change password on next login:
516
+
517
+ ```python
518
+ async def expire_user_password(user_id):
519
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
520
+ client = OktaClient(config)
521
+
522
+ user, resp, err = await client.expire_password(user_id)
523
+
524
+ if err:
525
+ print(f"Error expiring password: {err}")
526
+ else:
527
+ print(f"Password expired for user: {user.id}")
528
+
529
+ asyncio.run(expire_user_password('00u1234567890abcdef'))
530
+ ```
531
+
532
+ ## Group Management
533
+
534
+ ### Create a Group
535
+
536
+ Create a new group:
537
+
538
+ ```python
539
+ from okta.models import Group, GroupProfile
540
+
541
+ async def create_group():
542
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
543
+ client = OktaClient(config)
544
+
545
+ group_profile = GroupProfile({
546
+ 'name': 'Engineering Team',
547
+ 'description': 'All engineering department members'
548
+ })
549
+
550
+ new_group = Group({'profile': group_profile})
551
+
552
+ group, resp, err = await client.create_group(new_group)
553
+
554
+ if err:
555
+ print(f"Error creating group: {err}")
556
+ else:
557
+ print(f"Created group: {group.id}")
558
+ return group
559
+
560
+ asyncio.run(create_group())
561
+ ```
562
+
563
+ ### Get a Group
564
+
565
+ Retrieve a group by ID:
566
+
567
+ ```python
568
+ async def get_group(group_id):
569
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
570
+ client = OktaClient(config)
571
+
572
+ group, resp, err = await client.get_group(group_id)
573
+
574
+ if err:
575
+ print(f"Error getting group: {err}")
576
+ else:
577
+ print(f"Group: {group.profile.name}")
578
+ print(f"Description: {group.profile.description}")
579
+ return group
580
+
581
+ asyncio.run(get_group('00g1234567890abcdef'))
582
+ ```
583
+
584
+ ### List All Groups
585
+
586
+ List all groups in the organization:
587
+
588
+ ```python
589
+ async def list_all_groups():
590
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
591
+ client = OktaClient(config)
592
+
593
+ groups, resp, err = await client.list_groups()
594
+
595
+ if err:
596
+ print(f"Error listing groups: {err}")
597
+ else:
598
+ print(f"Total groups: {len(groups)}")
599
+ for group in groups:
600
+ print(f" - {group.profile.name} ({group.id})")
601
+ return groups
602
+
603
+ asyncio.run(list_all_groups())
604
+ ```
605
+
606
+ ### Search Groups by Name
607
+
608
+ Search for groups matching a query:
609
+
610
+ ```python
611
+ async def search_groups(query):
612
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
613
+ client = OktaClient(config)
614
+
615
+ query_params = {'q': query}
616
+ groups, resp, err = await client.list_groups(query_params)
617
+
618
+ if err:
619
+ print(f"Error searching groups: {err}")
620
+ else:
621
+ print(f"Found {len(groups)} groups matching '{query}'")
622
+ for group in groups:
623
+ print(f" - {group.profile.name}")
624
+
625
+ asyncio.run(search_groups('Engineering'))
626
+ ```
627
+
628
+ ### Update a Group
629
+
630
+ Update group profile information:
631
+
632
+ ```python
633
+ async def update_group(group_id):
634
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
635
+ client = OktaClient(config)
636
+
637
+ # Get the group first
638
+ group, resp, err = await client.get_group(group_id)
639
+
640
+ if err:
641
+ print(f"Error getting group: {err}")
642
+ return
643
+
644
+ # Update profile
645
+ group.profile.description = 'Updated description for the team'
646
+
647
+ # Save changes
648
+ updated_group, resp, err = await client.update_group(group_id, group)
649
+
650
+ if err:
651
+ print(f"Error updating group: {err}")
652
+ else:
653
+ print(f"Group updated: {updated_group.id}")
654
+
655
+ asyncio.run(update_group('00g1234567890abcdef'))
656
+ ```
657
+
658
+ ### Delete a Group
659
+
660
+ Delete a group:
661
+
662
+ ```python
663
+ async def delete_group(group_id):
664
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
665
+ client = OktaClient(config)
666
+
667
+ resp, err = await client.delete_group(group_id)
668
+
669
+ if err:
670
+ print(f"Error deleting group: {err}")
671
+ else:
672
+ print(f"Group deleted successfully")
673
+
674
+ asyncio.run(delete_group('00g1234567890abcdef'))
675
+ ```
676
+
677
+ ### Assign User to Group
678
+
679
+ Add a user to a group:
680
+
681
+ ```python
682
+ async def assign_user_to_group(group_id, user_id):
683
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
684
+ client = OktaClient(config)
685
+
686
+ resp, err = await client.add_user_to_group(group_id, user_id)
687
+
688
+ if err:
689
+ print(f"Error adding user to group: {err}")
690
+ else:
691
+ print(f"User {user_id} added to group {group_id}")
692
+
693
+ asyncio.run(assign_user_to_group('00g1234567890abcdef', '00u1234567890abcdef'))
694
+ ```
695
+
696
+ ### Remove User from Group
697
+
698
+ Remove a user from a group:
699
+
700
+ ```python
701
+ async def remove_user_from_group(group_id, user_id):
702
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
703
+ client = OktaClient(config)
704
+
705
+ resp, err = await client.remove_user_from_group(group_id, user_id)
706
+
707
+ if err:
708
+ print(f"Error removing user from group: {err}")
709
+ else:
710
+ print(f"User {user_id} removed from group {group_id}")
711
+
712
+ asyncio.run(remove_user_from_group('00g1234567890abcdef', '00u1234567890abcdef'))
713
+ ```
714
+
715
+ ### List Group Members
716
+
717
+ Get all users in a group:
718
+
719
+ ```python
720
+ async def list_group_members(group_id):
721
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
722
+ client = OktaClient(config)
723
+
724
+ users, resp, err = await client.list_group_users(group_id)
725
+
726
+ if err:
727
+ print(f"Error listing group members: {err}")
728
+ else:
729
+ print(f"Group has {len(users)} members:")
730
+ for user in users:
731
+ print(f" - {user.profile.first_name} {user.profile.last_name}")
732
+
733
+ asyncio.run(list_group_members('00g1234567890abcdef'))
734
+ ```
735
+
736
+ ### List User's Groups
737
+
738
+ Get all groups a user belongs to:
739
+
740
+ ```python
741
+ async def list_user_groups(user_id):
742
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
743
+ client = OktaClient(config)
744
+
745
+ groups, resp, err = await client.list_user_groups(user_id)
746
+
747
+ if err:
748
+ print(f"Error listing user groups: {err}")
749
+ else:
750
+ print(f"User belongs to {len(groups)} groups:")
751
+ for group in groups:
752
+ print(f" - {group.profile.name}")
753
+
754
+ asyncio.run(list_user_groups('00u1234567890abcdef'))
755
+ ```
756
+
757
+ ## Application Management
758
+
759
+ ### Create an Application
760
+
761
+ Create a basic authentication application:
762
+
763
+ ```python
764
+ from okta.models import Application
765
+
766
+ async def create_application():
767
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
768
+ client = OktaClient(config)
769
+
770
+ application = Application({
771
+ 'name': 'template_basic_auth',
772
+ 'label': 'Sample Basic Auth App',
773
+ 'signOnMode': 'BASIC_AUTH',
774
+ 'settings': {
775
+ 'app': {
776
+ 'url': 'https://example.com/auth.htm',
777
+ 'authURL': 'https://example.com/login.html'
778
+ }
779
+ }
780
+ })
781
+
782
+ app, resp, err = await client.create_application(application)
783
+
784
+ if err:
785
+ print(f"Error creating application: {err}")
786
+ else:
787
+ print(f"Created application: {app.id}")
788
+ return app
789
+
790
+ asyncio.run(create_application())
791
+ ```
792
+
793
+ ### Create an OAuth 2.0 Application
794
+
795
+ ```python
796
+ async def create_oauth_application():
797
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
798
+ client = OktaClient(config)
799
+
800
+ application = Application({
801
+ 'name': 'oidc_client',
802
+ 'label': 'OAuth 2.0 App',
803
+ 'signOnMode': 'OPENID_CONNECT',
804
+ 'credentials': {
805
+ 'oauthClient': {
806
+ 'token_endpoint_auth_method': 'client_secret_post'
807
+ }
808
+ },
809
+ 'settings': {
810
+ 'oauthClient': {
811
+ 'client_uri': 'https://example.com',
812
+ 'logo_uri': 'https://example.com/logo.png',
813
+ 'redirect_uris': ['https://example.com/oauth/callback'],
814
+ 'response_types': ['code'],
815
+ 'grant_types': ['authorization_code', 'refresh_token'],
816
+ 'application_type': 'web'
817
+ }
818
+ }
819
+ })
820
+
821
+ app, resp, err = await client.create_application(application)
822
+
823
+ if err:
824
+ print(f"Error creating OAuth app: {err}")
825
+ else:
826
+ print(f"Created OAuth app: {app.id}")
827
+ if hasattr(app.credentials, 'oauthClient'):
828
+ print(f"Client ID: {app.credentials.oauthClient.client_id}")
829
+ print(f"Client Secret: {app.credentials.oauthClient.client_secret}")
830
+ return app
831
+
832
+ asyncio.run(create_oauth_application())
833
+ ```
834
+
835
+ ### Get an Application
836
+
837
+ Retrieve an application by ID:
838
+
839
+ ```python
840
+ async def get_application(app_id):
841
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
842
+ client = OktaClient(config)
843
+
844
+ app, resp, err = await client.get_application(app_id)
845
+
846
+ if err:
847
+ print(f"Error getting application: {err}")
848
+ else:
849
+ print(f"Application: {app.label}")
850
+ print(f"Sign-on mode: {app.sign_on_mode}")
851
+ return app
852
+
853
+ asyncio.run(get_application('0oa1234567890abcdef'))
854
+ ```
855
+
856
+ ### List All Applications
857
+
858
+ ```python
859
+ async def list_all_applications():
860
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
861
+ client = OktaClient(config)
862
+
863
+ apps, resp, err = await client.list_applications()
864
+
865
+ if err:
866
+ print(f"Error listing applications: {err}")
867
+ else:
868
+ print(f"Total applications: {len(apps)}")
869
+ for app in apps:
870
+ print(f" - {app.label} ({app.id})")
871
+ return apps
872
+
873
+ asyncio.run(list_all_applications())
874
+ ```
875
+
876
+ ### Update an Application
877
+
878
+ ```python
879
+ async def update_application(app_id):
880
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
881
+ client = OktaClient(config)
882
+
883
+ # Get the application first
884
+ app, resp, err = await client.get_application(app_id)
885
+
886
+ if err:
887
+ print(f"Error getting application: {err}")
888
+ return
889
+
890
+ # Update label
891
+ app.label = 'Updated Application Name'
892
+
893
+ # Save changes
894
+ updated_app, resp, err = await client.update_application(app_id, app)
895
+
896
+ if err:
897
+ print(f"Error updating application: {err}")
898
+ else:
899
+ print(f"Application updated: {updated_app.id}")
900
+
901
+ asyncio.run(update_application('0oa1234567890abcdef'))
902
+ ```
903
+
904
+ ### Delete an Application
905
+
906
+ ```python
907
+ async def delete_application(app_id):
908
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
909
+ client = OktaClient(config)
910
+
911
+ # First deactivate
912
+ resp, err = await client.deactivate_application(app_id)
913
+ if err:
914
+ print(f"Error deactivating application: {err}")
915
+ return
916
+
917
+ # Then delete
918
+ resp, err = await client.delete_application(app_id)
919
+
920
+ if err:
921
+ print(f"Error deleting application: {err}")
922
+ else:
923
+ print(f"Application deleted successfully")
924
+
925
+ asyncio.run(delete_application('0oa1234567890abcdef'))
926
+ ```
927
+
928
+ ### Assign User to Application
929
+
930
+ ```python
931
+ from okta.models import AppUser
932
+
933
+ async def assign_user_to_application(app_id, user_id):
934
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
935
+ client = OktaClient(config)
936
+
937
+ app_user = AppUser({'id': user_id})
938
+
939
+ assigned_user, resp, err = await client.assign_user_to_application(app_id, app_user)
940
+
941
+ if err:
942
+ print(f"Error assigning user to application: {err}")
943
+ else:
944
+ print(f"User {user_id} assigned to application {app_id}")
945
+ return assigned_user
946
+
947
+ asyncio.run(assign_user_to_application('0oa1234567890abcdef', '00u1234567890abcdef'))
948
+ ```
949
+
950
+ ### Assign User with Credentials to Application
951
+
952
+ ```python
953
+ async def assign_user_with_credentials(app_id, user_id):
954
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
955
+ client = OktaClient(config)
956
+
957
+ app_user = AppUser({
958
+ 'id': user_id,
959
+ 'credentials': {
960
+ 'userName': 'user@example.com',
961
+ 'password': {'value': 'AppSpecificPassword123!'}
962
+ },
963
+ 'profile': {
964
+ 'role': 'Admin',
965
+ 'department': 'Engineering'
966
+ }
967
+ })
968
+
969
+ assigned_user, resp, err = await client.assign_user_to_application(app_id, app_user)
970
+
971
+ if err:
972
+ print(f"Error: {err}")
973
+ else:
974
+ print(f"User assigned with credentials")
975
+
976
+ asyncio.run(assign_user_with_credentials('0oa1234567890abcdef', '00u1234567890abcdef'))
977
+ ```
978
+
979
+ ### Remove User from Application
980
+
981
+ ```python
982
+ async def remove_user_from_application(app_id, user_id):
983
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
984
+ client = OktaClient(config)
985
+
986
+ resp, err = await client.delete_application_user(app_id, user_id)
987
+
988
+ if err:
989
+ print(f"Error removing user from application: {err}")
990
+ else:
991
+ print(f"User {user_id} removed from application {app_id}")
992
+
993
+ asyncio.run(remove_user_from_application('0oa1234567890abcdef', '00u1234567890abcdef'))
994
+ ```
995
+
996
+ ### Assign Group to Application
997
+
998
+ ```python
999
+ from okta.models import ApplicationGroupAssignment
1000
+
1001
+ async def assign_group_to_application(app_id, group_id):
1002
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
1003
+ client = OktaClient(config)
1004
+
1005
+ assignment = ApplicationGroupAssignment({'priority': 0})
1006
+
1007
+ result, resp, err = await client.create_application_group_assignment(
1008
+ app_id,
1009
+ group_id,
1010
+ assignment
1011
+ )
1012
+
1013
+ if err:
1014
+ print(f"Error assigning group to application: {err}")
1015
+ else:
1016
+ print(f"Group {group_id} assigned to application {app_id}")
1017
+ return result
1018
+
1019
+ asyncio.run(assign_group_to_application('0oa1234567890abcdef', '00g1234567890abcdef'))
1020
+ ```
1021
+
1022
+ ### Remove Group from Application
1023
+
1024
+ ```python
1025
+ async def remove_group_from_application(app_id, group_id):
1026
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
1027
+ client = OktaClient(config)
1028
+
1029
+ resp, err = await client.delete_application_group_assignment(app_id, group_id)
1030
+
1031
+ if err:
1032
+ print(f"Error removing group from application: {err}")
1033
+ else:
1034
+ print(f"Group {group_id} removed from application {app_id}")
1035
+
1036
+ asyncio.run(remove_group_from_application('0oa1234567890abcdef', '00g1234567890abcdef'))
1037
+ ```
1038
+
1039
+ ### List Application Users
1040
+
1041
+ Get all users assigned to an application:
1042
+
1043
+ ```python
1044
+ async def list_application_users(app_id):
1045
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
1046
+ client = OktaClient(config)
1047
+
1048
+ app_users, resp, err = await client.list_application_users(app_id)
1049
+
1050
+ if err:
1051
+ print(f"Error listing application users: {err}")
1052
+ else:
1053
+ print(f"Application has {len(app_users)} users:")
1054
+ for app_user in app_users:
1055
+ print(f" - User ID: {app_user.id}")
1056
+
1057
+ asyncio.run(list_application_users('0oa1234567890abcdef'))
1058
+ ```
1059
+
1060
+ ### List Application Groups
1061
+
1062
+ Get all groups assigned to an application:
1063
+
1064
+ ```python
1065
+ async def list_application_groups(app_id):
1066
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
1067
+ client = OktaClient(config)
1068
+
1069
+ groups, resp, err = await client.list_application_group_assignments(app_id)
1070
+
1071
+ if err:
1072
+ print(f"Error listing application groups: {err}")
1073
+ else:
1074
+ print(f"Application has {len(groups)} groups assigned:")
1075
+ for group in groups:
1076
+ print(f" - Group ID: {group.id}")
1077
+
1078
+ asyncio.run(list_application_groups('0oa1234567890abcdef'))
1079
+ ```
1080
+
1081
+ ## Custom Headers Management
1082
+
1083
+ Set custom headers sent with every request (requires SDK v1.3.0+):
1084
+
1085
+ ```python
1086
+ async def use_custom_headers():
1087
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
1088
+ client = OktaClient(config)
1089
+
1090
+ # Set custom headers
1091
+ client.set_custom_headers({'Custom-Header': 'custom value', 'X-Request-ID': '12345'})
1092
+
1093
+ # Make API calls with custom headers
1094
+ users, resp, err = await client.list_users()
1095
+
1096
+ # Get current custom headers
1097
+ headers = client.get_custom_headers()
1098
+ print(f"Custom headers: {headers}")
1099
+
1100
+ # Clear custom headers
1101
+ client.clear_custom_headers()
1102
+ print(f"Headers after clear: {client.get_custom_headers()}")
1103
+
1104
+ asyncio.run(use_custom_headers())
1105
+ ```
1106
+
1107
+ ## Error Handling
1108
+
1109
+ Always check the error return value:
1110
+
1111
+ ```python
1112
+ async def handle_errors():
1113
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
1114
+ client = OktaClient(config)
1115
+
1116
+ user, resp, err = await client.get_user('nonexistent@example.com')
1117
+
1118
+ if err:
1119
+ print(f"Error occurred: {err}")
1120
+ if hasattr(err, 'status'):
1121
+ if err.status == 404:
1122
+ print("User not found")
1123
+ elif err.status == 401:
1124
+ print("Authentication failed - check your API token")
1125
+ elif err.status == 403:
1126
+ print("Forbidden - insufficient permissions")
1127
+ elif err.status == 429:
1128
+ print("Rate limit exceeded")
1129
+ else:
1130
+ print(f"User found: {user.profile.email}")
1131
+
1132
+ asyncio.run(handle_errors())
1133
+ ```
1134
+
1135
+ ### Comprehensive Error Handling Pattern
1136
+
1137
+ ```python
1138
+ async def create_user_with_error_handling(user_data):
1139
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
1140
+ client = OktaClient(config)
1141
+
1142
+ try:
1143
+ user_profile = UserProfile(user_data['profile'])
1144
+ password_credential = PasswordCredential({'value': user_data['password']})
1145
+ user_credentials = UserCredentials({'password': password_credential})
1146
+
1147
+ create_user_request = CreateUserRequest({
1148
+ 'profile': user_profile,
1149
+ 'credentials': user_credentials
1150
+ })
1151
+
1152
+ user, resp, err = await client.create_user(create_user_request)
1153
+
1154
+ if err:
1155
+ print(f"Error creating user: {err}")
1156
+
1157
+ if hasattr(err, 'status'):
1158
+ if err.status == 400:
1159
+ print("Bad request - check user data format")
1160
+ elif err.status == 401:
1161
+ print("Authentication failed")
1162
+ elif err.status == 403:
1163
+ print("Forbidden - insufficient permissions")
1164
+ elif err.status == 409:
1165
+ print("Conflict - user already exists")
1166
+ elif err.status == 429:
1167
+ print("Rate limit exceeded - retry after delay")
1168
+ elif err.status >= 500:
1169
+ print("Server error - Okta service issue")
1170
+
1171
+ return {'success': False, 'error': str(err)}
1172
+ else:
1173
+ print(f"User created successfully: {user.id}")
1174
+ return {'success': True, 'user': user}
1175
+
1176
+ except Exception as e:
1177
+ print(f"Unexpected error: {e}")
1178
+ return {'success': False, 'error': str(e)}
1179
+
1180
+ user_data = {
1181
+ 'profile': {
1182
+ 'firstName': 'Alice',
1183
+ 'lastName': 'Johnson',
1184
+ 'email': 'alice.johnson@example.com',
1185
+ 'login': 'alice.johnson@example.com'
1186
+ },
1187
+ 'password': 'SecurePassword123!'
1188
+ }
1189
+
1190
+ asyncio.run(create_user_with_error_handling(user_data))
1191
+ ```
1192
+
1193
+ ## Pagination
1194
+
1195
+ Handle pagination for large result sets:
1196
+
1197
+ ```python
1198
+ async def paginate_users():
1199
+ config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
1200
+ client = OktaClient(config)
1201
+
1202
+ # Limit results per page
1203
+ query_params = {'limit': 20}
1204
+
1205
+ users, resp, err = await client.list_users(query_params)
1206
+
1207
+ if err:
1208
+ print(f"Error: {err}")
1209
+ else:
1210
+ print(f"Retrieved {len(users)} users")
1211
+
1212
+ # Check if there are more pages
1213
+ if hasattr(resp, 'has_next') and resp.has_next():
1214
+ print("More pages available")
1215
+
1216
+ asyncio.run(paginate_users())
1217
+ ```
1218
+
1219
+ ## Complete Example Application
1220
+
1221
+ ```python
1222
+ import asyncio
1223
+ import os
1224
+ from okta.client import Client as OktaClient
1225
+ from okta.models import (
1226
+ UserProfile, PasswordCredential, UserCredentials, CreateUserRequest,
1227
+ Group, GroupProfile, Application
1228
+ )
1229
+
1230
+ async def main():
1231
+ # Initialize client
1232
+ config = {
1233
+ 'orgUrl': os.getenv('OKTA_ORG_URL'),
1234
+ 'token': os.getenv('OKTA_API_TOKEN')
1235
+ }
1236
+ client = OktaClient(config)
1237
+
1238
+ try:
1239
+ # Create a user
1240
+ print("Creating user...")
1241
+ user_profile = UserProfile({
1242
+ 'firstName': 'Alice',
1243
+ 'lastName': 'Johnson',
1244
+ 'email': 'alice.johnson@example.com',
1245
+ 'login': 'alice.johnson@example.com'
1246
+ })
1247
+
1248
+ password_credential = PasswordCredential({'value': 'SecurePassword123!'})
1249
+ user_credentials = UserCredentials({'password': password_credential})
1250
+
1251
+ create_user_request = CreateUserRequest({
1252
+ 'profile': user_profile,
1253
+ 'credentials': user_credentials
1254
+ })
1255
+
1256
+ user, resp, err = await client.create_user(create_user_request, activate=True)
1257
+ if err:
1258
+ print(f"Error creating user: {err}")
1259
+ return
1260
+ print(f"Created user: {user.id}")
1261
+
1262
+ # Create a group
1263
+ print("\nCreating group...")
1264
+ group_profile = GroupProfile({
1265
+ 'name': 'Project Team Alpha',
1266
+ 'description': 'Members of Project Alpha'
1267
+ })
1268
+ new_group = Group({'profile': group_profile})
1269
+
1270
+ group, resp, err = await client.create_group(new_group)
1271
+ if err:
1272
+ print(f"Error creating group: {err}")
1273
+ return
1274
+ print(f"Created group: {group.id}")
1275
+
1276
+ # Add user to group
1277
+ print("\nAdding user to group...")
1278
+ resp, err = await client.add_user_to_group(group.id, user.id)
1279
+ if err:
1280
+ print(f"Error adding user to group: {err}")
1281
+ else:
1282
+ print("User added to group successfully")
1283
+
1284
+ # Create an application
1285
+ print("\nCreating application...")
1286
+ application = Application({
1287
+ 'name': 'template_basic_auth',
1288
+ 'label': 'Team Application',
1289
+ 'signOnMode': 'BASIC_AUTH',
1290
+ 'settings': {
1291
+ 'app': {
1292
+ 'url': 'https://example.com/app',
1293
+ 'authURL': 'https://example.com/login'
1294
+ }
1295
+ }
1296
+ })
1297
+
1298
+ app, resp, err = await client.create_application(application)
1299
+ if err:
1300
+ print(f"Error creating application: {err}")
1301
+ return
1302
+ print(f"Created application: {app.id}")
1303
+
1304
+ # Assign group to application
1305
+ print("\nAssigning group to application...")
1306
+ from okta.models import ApplicationGroupAssignment
1307
+ assignment = ApplicationGroupAssignment({'priority': 0})
1308
+
1309
+ result, resp, err = await client.create_application_group_assignment(
1310
+ app.id,
1311
+ group.id,
1312
+ assignment
1313
+ )
1314
+ if err:
1315
+ print(f"Error assigning group: {err}")
1316
+ else:
1317
+ print("Group assigned to application successfully")
1318
+
1319
+ # List all users in the group
1320
+ print("\nGroup members:")
1321
+ members, resp, err = await client.list_group_users(group.id)
1322
+ if err:
1323
+ print(f"Error listing group members: {err}")
1324
+ else:
1325
+ for member in members:
1326
+ print(f" - {member.profile.first_name} {member.profile.last_name}")
1327
+
1328
+ print("\n✅ All operations completed successfully!")
1329
+
1330
+ except Exception as e:
1331
+ print(f"Unexpected error: {e}")
1332
+
1333
+ if __name__ == '__main__':
1334
+ asyncio.run(main())
1335
+ ```
1336
+
1337
+ ## Environment Variables Setup
1338
+
1339
+ Create a `.env` file for configuration:
1340
+
1341
+ ```bash
1342
+ OKTA_ORG_URL=https://dev-1234567.okta.com
1343
+ OKTA_API_TOKEN=your_api_token_here
1344
+ ```
1345
+
1346
+ Load environment variables:
1347
+
1348
+ ```python
1349
+ import os
1350
+ from dotenv import load_dotenv
1351
+ from okta.client import Client as OktaClient
1352
+
1353
+ load_dotenv()
1354
+
1355
+ config = {
1356
+ 'orgUrl': os.getenv('OKTA_ORG_URL'),
1357
+ 'token': os.getenv('OKTA_API_TOKEN')
1358
+ }
1359
+
1360
+ client = OktaClient(config)
1361
+ ```
1362
+
1363
+ ## OAuth 2.0 Private Key Configuration
1364
+
1365
+ For OAuth 2.0 authentication, use environment variables:
1366
+
1367
+ ```bash
1368
+ OKTA_ORG_URL=https://dev-1234567.okta.com
1369
+ OKTA_CLIENT_ID=your_client_id
1370
+ OKTA_PRIVATE_KEY={"kty":"RSA","kid":"key-id","n":"...","e":"AQAB","d":"..."}
1371
+ OKTA_KEY_ID=key-id
1372
+ ```
1373
+
1374
+ ```python
1375
+ import os
1376
+ from dotenv import load_dotenv
1377
+ from okta.client import Client as OktaClient
1378
+
1379
+ load_dotenv()
1380
+
1381
+ config = {
1382
+ 'orgUrl': os.getenv('OKTA_ORG_URL'),
1383
+ 'authorizationMode': 'PrivateKey',
1384
+ 'clientId': os.getenv('OKTA_CLIENT_ID'),
1385
+ 'scopes': ['okta.users.manage', 'okta.groups.manage', 'okta.apps.manage'],
1386
+ 'privateKey': os.getenv('OKTA_PRIVATE_KEY'),
1387
+ 'kid': os.getenv('OKTA_KEY_ID')
1388
+ }
1389
+
1390
+ client = OktaClient(config)
1391
+ ```
1392
+
1393
+ ## Useful Links
1394
+
1395
+ - Official Documentation: https://developer.okta.com/
1396
+ - API Reference: https://developer.okta.com/docs/reference/
1397
+ - Users API: https://developer.okta.com/docs/reference/api/users/
1398
+ - Groups API: https://developer.okta.com/docs/reference/api/groups/
1399
+ - Applications API: https://developer.okta.com/docs/reference/api/apps/
1400
+ - GitHub Repository: https://github.com/okta/okta-sdk-python
1401
+ - PyPI Package: https://pypi.org/project/okta/