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,2297 @@
1
+ ---
2
+ name: support
3
+ description: "Zendesk API Python SDK (zenpy) for helpdesk, tickets, and customer service integration"
4
+ metadata:
5
+ languages: "python"
6
+ versions: "2.0.56"
7
+ updated-on: "2026-03-02"
8
+ source: maintainer
9
+ tags: "zendesk,support,helpdesk,tickets,customer-service"
10
+ ---
11
+
12
+ # Zendesk API - Python SDK (zenpy)
13
+
14
+ ## Golden Rule
15
+
16
+ **ALWAYS use the `zenpy` package (version 2.0.56 or later) for Zendesk API integration in Python projects.**
17
+
18
+ ```bash
19
+ pip install zenpy
20
+ ```
21
+
22
+ **DO NOT use:**
23
+ - `zend esk` (typo/incorrect package)
24
+ - `python-zendesk` (outdated)
25
+ - `@zendesk/client` (JavaScript package)
26
+ - Direct HTTP requests to Zendesk API endpoints (use the SDK instead)
27
+
28
+ The `zenpy` library is the officially recommended and most actively maintained Python client for the Zendesk API, providing a clean, Pythonic interface for interacting with Zendesk Support, Chat, and Help Center APIs.
29
+
30
+ ---
31
+
32
+ ## Installation
33
+
34
+ ```bash
35
+ pip install zenpy
36
+ ```
37
+
38
+ For development or testing:
39
+
40
+ ```bash
41
+ pip install zenpy[dev]
42
+ ```
43
+
44
+ With requirements.txt:
45
+
46
+ ```txt
47
+ zenpy>=2.0.56
48
+ ```
49
+
50
+ With Poetry:
51
+
52
+ ```bash
53
+ poetry add zenpy
54
+ ```
55
+
56
+ ---
57
+
58
+ ## Authentication & Initialization
59
+
60
+ ### API Token Authentication (Recommended)
61
+
62
+ The most common authentication method uses email, API token, and subdomain:
63
+
64
+ ```python
65
+ from zenpy import Zenpy
66
+
67
+ # Credentials dictionary
68
+ credentials = {
69
+ 'email': 'your_email@example.com',
70
+ 'token': 'your_api_token',
71
+ 'subdomain': 'your_subdomain'
72
+ }
73
+
74
+ # Create Zenpy client
75
+ zenpy_client = Zenpy(**credentials)
76
+ ```
77
+
78
+ **Environment Variables Example:**
79
+
80
+ ```python
81
+ import os
82
+ from zenpy import Zenpy
83
+ from dotenv import load_dotenv
84
+
85
+ load_dotenv()
86
+
87
+ credentials = {
88
+ 'email': os.getenv('ZENDESK_EMAIL'),
89
+ 'token': os.getenv('ZENDESK_API_TOKEN'),
90
+ 'subdomain': os.getenv('ZENDESK_SUBDOMAIN')
91
+ }
92
+
93
+ zenpy_client = Zenpy(**credentials)
94
+ ```
95
+
96
+ **.env file:**
97
+
98
+ ```
99
+ ZENDESK_EMAIL=your_email@example.com
100
+ ZENDESK_API_TOKEN=your_api_token_here
101
+ ZENDESK_SUBDOMAIN=your_company
102
+ ```
103
+
104
+ ### OAuth Token Authentication
105
+
106
+ For OAuth-based authentication:
107
+
108
+ ```python
109
+ from zenpy import Zenpy
110
+
111
+ credentials = {
112
+ 'email': 'your_email@example.com',
113
+ 'oauth_token': 'your_oauth_access_token',
114
+ 'subdomain': 'your_subdomain'
115
+ }
116
+
117
+ zenpy_client = Zenpy(**credentials)
118
+ ```
119
+
120
+ **With Environment Variables:**
121
+
122
+ ```python
123
+ import os
124
+ from zenpy import Zenpy
125
+
126
+ credentials = {
127
+ 'email': os.getenv('ZENDESK_EMAIL'),
128
+ 'oauth_token': os.getenv('ZENDESK_OAUTH_TOKEN'),
129
+ 'subdomain': os.getenv('ZENDESK_SUBDOMAIN')
130
+ }
131
+
132
+ zenpy_client = Zenpy(**credentials)
133
+ ```
134
+
135
+ ### Password Authentication (Not Recommended)
136
+
137
+ ```python
138
+ from zenpy import Zenpy
139
+
140
+ credentials = {
141
+ 'email': 'your_email@example.com',
142
+ 'password': 'your_password',
143
+ 'subdomain': 'your_subdomain'
144
+ }
145
+
146
+ zenpy_client = Zenpy(**credentials)
147
+ ```
148
+
149
+ **Note:** Password authentication is deprecated and should be avoided. Use API token or OAuth authentication instead.
150
+
151
+ ### Custom Configuration
152
+
153
+ ```python
154
+ from zenpy import Zenpy
155
+
156
+ credentials = {
157
+ 'email': 'your_email@example.com',
158
+ 'token': 'your_api_token',
159
+ 'subdomain': 'your_subdomain',
160
+ 'timeout': 60, # Request timeout in seconds
161
+ 'ratelimit': 700, # API rate limit (requests per minute)
162
+ 'session': custom_session # Optional: custom requests.Session object
163
+ }
164
+
165
+ zenpy_client = Zenpy(**credentials)
166
+ ```
167
+
168
+ ---
169
+
170
+ ## Core API Surfaces
171
+
172
+ ### Tickets API
173
+
174
+ #### List All Tickets
175
+
176
+ **Basic Example:**
177
+
178
+ ```python
179
+ from zenpy import Zenpy
180
+
181
+ # Initialize client
182
+ zenpy_client = Zenpy(**credentials)
183
+
184
+ # List all tickets
185
+ for ticket in zenpy_client.tickets():
186
+ print(f"#{ticket.id}: {ticket.subject}")
187
+ print(f"Status: {ticket.status}, Priority: {ticket.priority}")
188
+ print("---")
189
+ ```
190
+
191
+ **Advanced Example with Filtering:**
192
+
193
+ ```python
194
+ from zenpy import Zenpy
195
+
196
+ zenpy_client = Zenpy(**credentials)
197
+
198
+ def list_open_tickets():
199
+ """List all open tickets with details"""
200
+ open_tickets = []
201
+
202
+ for ticket in zenpy_client.tickets():
203
+ if ticket.status == 'open':
204
+ print(f"Ticket #{ticket.id}")
205
+ print(f"Subject: {ticket.subject}")
206
+ print(f"Status: {ticket.status}")
207
+ print(f"Priority: {ticket.priority}")
208
+ print(f"Requester: {ticket.requester.name}")
209
+ print(f"Created: {ticket.created_at}")
210
+ print("---")
211
+ open_tickets.append(ticket)
212
+
213
+ print(f"\nTotal open tickets: {len(open_tickets)}")
214
+ return open_tickets
215
+
216
+ list_open_tickets()
217
+ ```
218
+
219
+ #### Show Single Ticket
220
+
221
+ **Basic Example:**
222
+
223
+ ```python
224
+ from zenpy import Zenpy
225
+
226
+ zenpy_client = Zenpy(**credentials)
227
+
228
+ # Get a single ticket by ID
229
+ ticket_id = 12345
230
+ ticket = zenpy_client.tickets(id=ticket_id)
231
+
232
+ print(f"Subject: {ticket.subject}")
233
+ print(f"Status: {ticket.status}")
234
+ print(f"Requester: {ticket.requester.name}")
235
+ ```
236
+
237
+ **Advanced Example:**
238
+
239
+ ```python
240
+ from zenpy import Zenpy
241
+
242
+ zenpy_client = Zenpy(**credentials)
243
+
244
+ def get_ticket_details(ticket_id):
245
+ """Get comprehensive ticket details"""
246
+ try:
247
+ ticket = zenpy_client.tickets(id=ticket_id)
248
+
249
+ print("=== Ticket Details ===")
250
+ print(f"ID: {ticket.id}")
251
+ print(f"Subject: {ticket.subject}")
252
+ print(f"Description: {ticket.description}")
253
+ print(f"Status: {ticket.status}")
254
+ print(f"Priority: {ticket.priority}")
255
+ print(f"Type: {ticket.type}")
256
+ print(f"Requester: {ticket.requester.name} ({ticket.requester.email})")
257
+
258
+ if ticket.assignee:
259
+ print(f"Assignee: {ticket.assignee.name}")
260
+ else:
261
+ print("Assignee: Unassigned")
262
+
263
+ if ticket.group:
264
+ print(f"Group: {ticket.group.name}")
265
+
266
+ print(f"Created: {ticket.created_at}")
267
+ print(f"Updated: {ticket.updated_at}")
268
+ print(f"Tags: {', '.join(ticket.tags)}")
269
+
270
+ # Custom fields
271
+ if ticket.custom_fields:
272
+ print("\nCustom Fields:")
273
+ for field in ticket.custom_fields:
274
+ print(f" {field.id}: {field.value}")
275
+
276
+ return ticket
277
+
278
+ except Exception as e:
279
+ print(f"Error fetching ticket: {e}")
280
+ return None
281
+
282
+ get_ticket_details(12345)
283
+ ```
284
+
285
+ #### Create Ticket
286
+
287
+ **Basic Example:**
288
+
289
+ ```python
290
+ from zenpy import Zenpy
291
+ from zenpy.lib.api_objects import Ticket, Comment
292
+
293
+ zenpy_client = Zenpy(**credentials)
294
+
295
+ # Create a new ticket
296
+ ticket = Ticket(
297
+ subject="Help with product installation",
298
+ description="I need assistance installing the product on my system."
299
+ )
300
+
301
+ created_ticket = zenpy_client.tickets.create(ticket)
302
+
303
+ print("Ticket created successfully!")
304
+ print(f"Ticket ID: {created_ticket.id}")
305
+ print(f"Subject: {created_ticket.subject}")
306
+ ```
307
+
308
+ **Advanced Example with Custom Fields:**
309
+
310
+ ```python
311
+ from zenpy import Zenpy
312
+ from zenpy.lib.api_objects import Ticket, User, Comment, CustomField
313
+
314
+ zenpy_client = Zenpy(**credentials)
315
+
316
+ def create_detailed_ticket():
317
+ """Create a ticket with all details"""
318
+
319
+ # Create ticket with requester
320
+ ticket = Ticket(
321
+ subject="Technical Support Request",
322
+ description="Detailed description of the issue...\n\nSteps to reproduce:\n1. Step one\n2. Step two",
323
+ requester=User(
324
+ name="Jane Smith",
325
+ email="jane.smith@example.com"
326
+ ),
327
+ priority="high",
328
+ status="open",
329
+ type="problem",
330
+ tags=["technical", "urgent", "product-bug"],
331
+ custom_fields=[
332
+ CustomField(id=12345, value="Custom value 1"),
333
+ CustomField(id=67890, value="Custom value 2")
334
+ ]
335
+ )
336
+
337
+ # Create the ticket
338
+ created_ticket = zenpy_client.tickets.create(ticket)
339
+
340
+ print(f"Ticket created: {created_ticket.id}")
341
+ print(f"Subject: {created_ticket.subject}")
342
+ print(f"Requester: {created_ticket.requester.name}")
343
+
344
+ return created_ticket
345
+
346
+ create_detailed_ticket()
347
+ ```
348
+
349
+ **Create Ticket with Existing User:**
350
+
351
+ ```python
352
+ from zenpy import Zenpy
353
+ from zenpy.lib.api_objects import Ticket
354
+
355
+ zenpy_client = Zenpy(**credentials)
356
+
357
+ def create_ticket_for_user(user_id, subject, description):
358
+ """Create ticket for an existing user"""
359
+
360
+ ticket = Ticket(
361
+ subject=subject,
362
+ description=description,
363
+ requester_id=user_id,
364
+ priority="normal",
365
+ status="new"
366
+ )
367
+
368
+ created_ticket = zenpy_client.tickets.create(ticket)
369
+ print(f"Ticket {created_ticket.id} created for user {user_id}")
370
+
371
+ return created_ticket
372
+
373
+ create_ticket_for_user(67890, "Account Issue", "Cannot access account")
374
+ ```
375
+
376
+ #### Update Ticket
377
+
378
+ **Basic Example:**
379
+
380
+ ```python
381
+ from zenpy import Zenpy
382
+
383
+ zenpy_client = Zenpy(**credentials)
384
+
385
+ # Get the ticket
386
+ ticket_id = 12345
387
+ ticket = zenpy_client.tickets(id=ticket_id)
388
+
389
+ # Update ticket status
390
+ ticket.status = "solved"
391
+ ticket.comment = "This issue has been resolved."
392
+
393
+ # Save changes
394
+ updated_ticket = zenpy_client.tickets.update(ticket)
395
+
396
+ print("Ticket updated successfully!")
397
+ print(f"New status: {updated_ticket.status}")
398
+ ```
399
+
400
+ **Advanced Example:**
401
+
402
+ ```python
403
+ from zenpy import Zenpy
404
+ from zenpy.lib.api_objects import Comment
405
+
406
+ zenpy_client = Zenpy(**credentials)
407
+
408
+ def update_ticket_with_comment(ticket_id):
409
+ """Update ticket with detailed changes"""
410
+
411
+ # Get the ticket
412
+ ticket = zenpy_client.tickets(id=ticket_id)
413
+
414
+ # Update multiple fields
415
+ ticket.status = "pending"
416
+ ticket.priority = "high"
417
+ ticket.tags.extend(["escalated", "requires-attention"])
418
+
419
+ # Add internal comment
420
+ ticket.comment = Comment(
421
+ body="This ticket has been escalated to senior support.",
422
+ public=False
423
+ )
424
+
425
+ # Update custom fields
426
+ from zenpy.lib.api_objects import CustomField
427
+ ticket.custom_fields = [
428
+ CustomField(id=11111, value="Updated value")
429
+ ]
430
+
431
+ # Save changes
432
+ updated_ticket = zenpy_client.tickets.update(ticket)
433
+
434
+ print(f"Ticket updated: {updated_ticket.id}")
435
+ print(f"New status: {updated_ticket.status}")
436
+ print(f"New priority: {updated_ticket.priority}")
437
+
438
+ return updated_ticket
439
+
440
+ update_ticket_with_comment(12345)
441
+ ```
442
+
443
+ **Update Ticket Status:**
444
+
445
+ ```python
446
+ from zenpy import Zenpy
447
+
448
+ zenpy_client = Zenpy(**credentials)
449
+
450
+ def close_ticket(ticket_id):
451
+ """Close a ticket with a resolution comment"""
452
+
453
+ ticket = zenpy_client.tickets(id=ticket_id)
454
+ ticket.status = "solved"
455
+ ticket.comment = "This ticket has been resolved and closed."
456
+
457
+ updated_ticket = zenpy_client.tickets.update(ticket)
458
+ print(f"Ticket {ticket_id} closed successfully")
459
+
460
+ return updated_ticket
461
+
462
+ close_ticket(12345)
463
+ ```
464
+
465
+ #### Delete Ticket
466
+
467
+ **Basic Example:**
468
+
469
+ ```python
470
+ from zenpy import Zenpy
471
+
472
+ zenpy_client = Zenpy(**credentials)
473
+
474
+ # Delete a ticket
475
+ ticket_id = 12345
476
+ zenpy_client.tickets.delete(ticket_id)
477
+
478
+ print(f"Ticket {ticket_id} deleted successfully")
479
+ ```
480
+
481
+ **Advanced Example with Confirmation:**
482
+
483
+ ```python
484
+ from zenpy import Zenpy
485
+
486
+ zenpy_client = Zenpy(**credentials)
487
+
488
+ def delete_ticket_safely(ticket_id):
489
+ """Delete a ticket with verification"""
490
+
491
+ try:
492
+ # First, verify the ticket exists
493
+ ticket = zenpy_client.tickets(id=ticket_id)
494
+ print(f"About to delete ticket #{ticket.id}: {ticket.subject}")
495
+
496
+ # Delete the ticket
497
+ zenpy_client.tickets.delete(ticket_id)
498
+ print("Ticket deleted successfully")
499
+
500
+ return True
501
+
502
+ except Exception as e:
503
+ print(f"Error deleting ticket: {e}")
504
+ return False
505
+
506
+ delete_ticket_safely(12345)
507
+ ```
508
+
509
+ #### List Ticket Comments
510
+
511
+ **Basic Example:**
512
+
513
+ ```python
514
+ from zenpy import Zenpy
515
+
516
+ zenpy_client = Zenpy(**credentials)
517
+
518
+ # Get ticket comments
519
+ ticket_id = 12345
520
+ comments = zenpy_client.tickets.comments(ticket_id)
521
+
522
+ for comment in comments:
523
+ print(f"Author ID: {comment.author_id}")
524
+ print(f"Body: {comment.body}")
525
+ print(f"Public: {comment.public}")
526
+ print("---")
527
+ ```
528
+
529
+ **Advanced Example:**
530
+
531
+ ```python
532
+ from zenpy import Zenpy
533
+
534
+ zenpy_client = Zenpy(**credentials)
535
+
536
+ def analyze_ticket_conversation(ticket_id):
537
+ """Analyze ticket conversation history"""
538
+
539
+ try:
540
+ comments = list(zenpy_client.tickets.comments(ticket_id))
541
+
542
+ public_comments = [c for c in comments if c.public]
543
+ private_comments = [c for c in comments if not c.public]
544
+
545
+ print("=== Ticket Conversation Analysis ===")
546
+ print(f"Total comments: {len(comments)}")
547
+ print(f"Public comments: {len(public_comments)}")
548
+ print(f"Private/Internal notes: {len(private_comments)}")
549
+
550
+ print("\n=== Comment History ===")
551
+ for i, comment in enumerate(comments, 1):
552
+ print(f"\n[Comment {i}]")
553
+ print(f"ID: {comment.id}")
554
+ print(f"Author ID: {comment.author_id}")
555
+ print(f"Created: {comment.created_at}")
556
+ print(f"Type: {'Public' if comment.public else 'Internal'}")
557
+ print(f"Body: {comment.body[:100]}...")
558
+
559
+ if hasattr(comment, 'attachments') and comment.attachments:
560
+ print("Attachments:")
561
+ for att in comment.attachments:
562
+ print(f" - {att.file_name} ({att.size} bytes)")
563
+
564
+ return comments
565
+
566
+ except Exception as e:
567
+ print(f"Error fetching comments: {e}")
568
+ return None
569
+
570
+ analyze_ticket_conversation(12345)
571
+ ```
572
+
573
+ ---
574
+
575
+ ### Users API
576
+
577
+ #### List Users
578
+
579
+ **Basic Example:**
580
+
581
+ ```python
582
+ from zenpy import Zenpy
583
+
584
+ zenpy_client = Zenpy(**credentials)
585
+
586
+ # List all users
587
+ for user in zenpy_client.users():
588
+ print(f"{user.name} ({user.email})")
589
+ ```
590
+
591
+ **Advanced Example with Filtering:**
592
+
593
+ ```python
594
+ from zenpy import Zenpy
595
+
596
+ zenpy_client = Zenpy(**credentials)
597
+
598
+ def list_active_agents():
599
+ """List all active agents"""
600
+
601
+ agents = []
602
+
603
+ for user in zenpy_client.users():
604
+ if user.role == 'agent' and user.active:
605
+ print(f"\nName: {user.name}")
606
+ print(f"Email: {user.email}")
607
+ print(f"Role: {user.role}")
608
+ print(f"Timezone: {user.time_zone}")
609
+ print(f"Locale: {user.locale}")
610
+
611
+ if user.last_login_at:
612
+ print(f"Last Login: {user.last_login_at}")
613
+
614
+ agents.append(user)
615
+
616
+ print(f"\nFound {len(agents)} active agents")
617
+ return agents
618
+
619
+ list_active_agents()
620
+ ```
621
+
622
+ #### Show User
623
+
624
+ **Basic Example:**
625
+
626
+ ```python
627
+ from zenpy import Zenpy
628
+
629
+ zenpy_client = Zenpy(**credentials)
630
+
631
+ # Get a single user by ID
632
+ user_id = 67890
633
+ user = zenpy_client.users(id=user_id)
634
+
635
+ print(f"Name: {user.name}")
636
+ print(f"Email: {user.email}")
637
+ print(f"Role: {user.role}")
638
+ ```
639
+
640
+ **Advanced Example:**
641
+
642
+ ```python
643
+ from zenpy import Zenpy
644
+
645
+ zenpy_client = Zenpy(**credentials)
646
+
647
+ def get_user_profile(user_id):
648
+ """Get comprehensive user profile"""
649
+
650
+ try:
651
+ user = zenpy_client.users(id=user_id)
652
+
653
+ print("=== User Profile ===")
654
+ print(f"ID: {user.id}")
655
+ print(f"Name: {user.name}")
656
+ print(f"Email: {user.email}")
657
+ print(f"Phone: {user.phone or 'N/A'}")
658
+ print(f"Role: {user.role}")
659
+ print(f"Active: {user.active}")
660
+ print(f"Verified: {user.verified}")
661
+ print(f"Timezone: {user.time_zone}")
662
+ print(f"Locale: {user.locale}")
663
+
664
+ if user.organization_id:
665
+ print(f"Organization ID: {user.organization_id}")
666
+
667
+ print(f"Created: {user.created_at}")
668
+ print(f"Updated: {user.updated_at}")
669
+
670
+ if user.last_login_at:
671
+ print(f"Last Login: {user.last_login_at}")
672
+
673
+ if user.tags:
674
+ print(f"Tags: {', '.join(user.tags)}")
675
+
676
+ if hasattr(user, 'user_fields') and user.user_fields:
677
+ print("\nCustom User Fields:")
678
+ for key, value in user.user_fields.items():
679
+ print(f" {key}: {value}")
680
+
681
+ return user
682
+
683
+ except Exception as e:
684
+ print(f"Error fetching user: {e}")
685
+ return None
686
+
687
+ get_user_profile(67890)
688
+ ```
689
+
690
+ #### Create User
691
+
692
+ **Basic Example:**
693
+
694
+ ```python
695
+ from zenpy import Zenpy
696
+ from zenpy.lib.api_objects import User
697
+
698
+ zenpy_client = Zenpy(**credentials)
699
+
700
+ # Create a new user
701
+ user = User(
702
+ name="John Doe",
703
+ email="john.doe@example.com",
704
+ role="end-user"
705
+ )
706
+
707
+ created_user = zenpy_client.users.create(user)
708
+
709
+ print("User created successfully!")
710
+ print(f"User ID: {created_user.id}")
711
+ print(f"Name: {created_user.name}")
712
+ ```
713
+
714
+ **Advanced Example:**
715
+
716
+ ```python
717
+ from zenpy import Zenpy
718
+ from zenpy.lib.api_objects import User
719
+
720
+ zenpy_client = Zenpy(**credentials)
721
+
722
+ def create_agent_user():
723
+ """Create an agent user with full details"""
724
+
725
+ user = User(
726
+ name="Sarah Agent",
727
+ email="sarah.agent@company.com",
728
+ role="agent",
729
+ phone="+1-555-123-4567",
730
+ time_zone="America/New_York",
731
+ locale="en-US",
732
+ verified=True,
733
+ tags=["support-team", "tier-2"],
734
+ user_fields={
735
+ "department": "Technical Support",
736
+ "employee_id": "EMP-12345"
737
+ },
738
+ organization_id=98765
739
+ )
740
+
741
+ try:
742
+ created_user = zenpy_client.users.create(user)
743
+ print(f"Agent created: {created_user.id}")
744
+ print(f"Name: {created_user.name}")
745
+ print(f"Email: {created_user.email}")
746
+ return created_user
747
+
748
+ except Exception as e:
749
+ print(f"Failed to create user: {e}")
750
+ return None
751
+
752
+ create_agent_user()
753
+ ```
754
+
755
+ #### Update User
756
+
757
+ **Basic Example:**
758
+
759
+ ```python
760
+ from zenpy import Zenpy
761
+
762
+ zenpy_client = Zenpy(**credentials)
763
+
764
+ # Get the user
765
+ user_id = 67890
766
+ user = zenpy_client.users(id=user_id)
767
+
768
+ # Update user details
769
+ user.name = "Jane Smith"
770
+ user.phone = "+1-555-999-8888"
771
+
772
+ # Save changes
773
+ updated_user = zenpy_client.users.update(user)
774
+
775
+ print("User updated successfully!")
776
+ print(f"Name: {updated_user.name}")
777
+ print(f"Phone: {updated_user.phone}")
778
+ ```
779
+
780
+ **Advanced Example:**
781
+
782
+ ```python
783
+ from zenpy import Zenpy
784
+
785
+ zenpy_client = Zenpy(**credentials)
786
+
787
+ def update_user_profile(user_id):
788
+ """Update user profile with multiple fields"""
789
+
790
+ try:
791
+ user = zenpy_client.users(id=user_id)
792
+
793
+ # Update multiple fields
794
+ user.name = "Jane Smith-Johnson"
795
+ user.phone = "+1-555-987-6543"
796
+ user.time_zone = "Pacific/Auckland"
797
+ user.locale = "en-GB"
798
+ user.tags = ["vip", "premium-support"]
799
+ user.user_fields = {
800
+ "department": "Engineering",
801
+ "location": "Remote"
802
+ }
803
+ user.organization_id = 11111
804
+
805
+ # Save changes
806
+ updated_user = zenpy_client.users.update(user)
807
+
808
+ print(f"User updated: {updated_user.id}")
809
+ print(f"New name: {updated_user.name}")
810
+ print(f"New timezone: {updated_user.time_zone}")
811
+
812
+ return updated_user
813
+
814
+ except Exception as e:
815
+ print(f"Failed to update user: {e}")
816
+ return None
817
+
818
+ update_user_profile(67890)
819
+ ```
820
+
821
+ #### Delete User
822
+
823
+ **Basic Example:**
824
+
825
+ ```python
826
+ from zenpy import Zenpy
827
+
828
+ zenpy_client = Zenpy(**credentials)
829
+
830
+ # Delete a user
831
+ user_id = 67890
832
+ zenpy_client.users.delete(user_id)
833
+
834
+ print(f"User {user_id} deleted successfully")
835
+ ```
836
+
837
+ #### Search Users
838
+
839
+ **Basic Example:**
840
+
841
+ ```python
842
+ from zenpy import Zenpy
843
+
844
+ zenpy_client = Zenpy(**credentials)
845
+
846
+ # Search for users by email
847
+ users = zenpy_client.search('john@example.com', type='user')
848
+
849
+ for user in users:
850
+ print(f"{user.name} - {user.email}")
851
+ ```
852
+
853
+ **Advanced Example:**
854
+
855
+ ```python
856
+ from zenpy import Zenpy
857
+
858
+ zenpy_client = Zenpy(**credentials)
859
+
860
+ def search_users_by_name(search_term):
861
+ """Search users by name"""
862
+
863
+ try:
864
+ users = list(zenpy_client.search(f'name:{search_term}', type='user'))
865
+
866
+ print(f"Found {len(users)} users matching \"{search_term}\"")
867
+
868
+ for user in users:
869
+ print("\n---")
870
+ print(f"ID: {user.id}")
871
+ print(f"Name: {user.name}")
872
+ print(f"Email: {user.email}")
873
+ print(f"Role: {user.role}")
874
+ print(f"Organization: {user.organization_id}")
875
+
876
+ return users
877
+
878
+ except Exception as e:
879
+ print(f"Search failed: {e}")
880
+ return []
881
+
882
+ search_users_by_name('John')
883
+ ```
884
+
885
+ #### Get Current User
886
+
887
+ **Basic Example:**
888
+
889
+ ```python
890
+ from zenpy import Zenpy
891
+
892
+ zenpy_client = Zenpy(**credentials)
893
+
894
+ # Get current authenticated user
895
+ current_user = zenpy_client.users.me()
896
+
897
+ print(f"Current user: {current_user.name}")
898
+ print(f"Email: {current_user.email}")
899
+ print(f"Role: {current_user.role}")
900
+ ```
901
+
902
+ ---
903
+
904
+ ### Organizations API
905
+
906
+ #### List Organizations
907
+
908
+ **Basic Example:**
909
+
910
+ ```python
911
+ from zenpy import Zenpy
912
+
913
+ zenpy_client = Zenpy(**credentials)
914
+
915
+ # List all organizations
916
+ for org in zenpy_client.organizations():
917
+ print(f"{org.name} (ID: {org.id})")
918
+ ```
919
+
920
+ **Advanced Example:**
921
+
922
+ ```python
923
+ from zenpy import Zenpy
924
+
925
+ zenpy_client = Zenpy(**credentials)
926
+
927
+ def analyze_organizations():
928
+ """Analyze all organizations"""
929
+
930
+ organizations = list(zenpy_client.organizations())
931
+
932
+ print(f"Total organizations: {len(organizations)}")
933
+
934
+ for org in organizations:
935
+ print("\n=== Organization ===")
936
+ print(f"ID: {org.id}")
937
+ print(f"Name: {org.name}")
938
+
939
+ if hasattr(org, 'domain_names') and org.domain_names:
940
+ print(f"Domain Names: {', '.join(org.domain_names)}")
941
+ else:
942
+ print("Domain Names: None")
943
+
944
+ print(f"Details: {org.details or 'N/A'}")
945
+ print(f"Notes: {org.notes or 'N/A'}")
946
+
947
+ if org.tags:
948
+ print(f"Tags: {', '.join(org.tags)}")
949
+
950
+ print(f"Created: {org.created_at}")
951
+
952
+ if hasattr(org, 'organization_fields') and org.organization_fields:
953
+ print("Custom Fields:", org.organization_fields)
954
+
955
+ return organizations
956
+
957
+ analyze_organizations()
958
+ ```
959
+
960
+ #### Show Organization
961
+
962
+ **Basic Example:**
963
+
964
+ ```python
965
+ from zenpy import Zenpy
966
+
967
+ zenpy_client = Zenpy(**credentials)
968
+
969
+ # Get a single organization by ID
970
+ org_id = 98765
971
+ org = zenpy_client.organizations(id=org_id)
972
+
973
+ print(f"Name: {org.name}")
974
+ print(f"Details: {org.details}")
975
+ ```
976
+
977
+ **Advanced Example:**
978
+
979
+ ```python
980
+ from zenpy import Zenpy
981
+
982
+ zenpy_client = Zenpy(**credentials)
983
+
984
+ def get_organization_details(org_id):
985
+ """Get comprehensive organization details"""
986
+
987
+ try:
988
+ org = zenpy_client.organizations(id=org_id)
989
+
990
+ print("=== Organization Details ===")
991
+ print(f"ID: {org.id}")
992
+ print(f"Name: {org.name}")
993
+
994
+ if hasattr(org, 'domain_names') and org.domain_names:
995
+ print(f"Domain Names: {', '.join(org.domain_names)}")
996
+ else:
997
+ print("Domain Names: None")
998
+
999
+ print(f"Details: {org.details or 'N/A'}")
1000
+ print(f"Notes: {org.notes or 'N/A'}")
1001
+
1002
+ if hasattr(org, 'group_id'):
1003
+ print(f"Group ID: {org.group_id or 'N/A'}")
1004
+
1005
+ if org.tags:
1006
+ print(f"Tags: {', '.join(org.tags)}")
1007
+
1008
+ print(f"Created: {org.created_at}")
1009
+ print(f"Updated: {org.updated_at}")
1010
+
1011
+ if hasattr(org, 'organization_fields') and org.organization_fields:
1012
+ print("\nCustom Organization Fields:")
1013
+ for key, value in org.organization_fields.items():
1014
+ print(f" {key}: {value}")
1015
+
1016
+ return org
1017
+
1018
+ except Exception as e:
1019
+ print(f"Error fetching organization: {e}")
1020
+ return None
1021
+
1022
+ get_organization_details(98765)
1023
+ ```
1024
+
1025
+ #### Create Organization
1026
+
1027
+ **Basic Example:**
1028
+
1029
+ ```python
1030
+ from zenpy import Zenpy
1031
+ from zenpy.lib.api_objects import Organization
1032
+
1033
+ zenpy_client = Zenpy(**credentials)
1034
+
1035
+ # Create a new organization
1036
+ org = Organization(
1037
+ name="Acme Corporation",
1038
+ domain_names=["acme.com"]
1039
+ )
1040
+
1041
+ created_org = zenpy_client.organizations.create(org)
1042
+
1043
+ print("Organization created!")
1044
+ print(f"ID: {created_org.id}")
1045
+ print(f"Name: {created_org.name}")
1046
+ ```
1047
+
1048
+ **Advanced Example:**
1049
+
1050
+ ```python
1051
+ from zenpy import Zenpy
1052
+ from zenpy.lib.api_objects import Organization
1053
+
1054
+ zenpy_client = Zenpy(**credentials)
1055
+
1056
+ def create_organization():
1057
+ """Create organization with full details"""
1058
+
1059
+ organization = Organization(
1060
+ name="TechStart Solutions Inc.",
1061
+ domain_names=["techstart.com", "techstart.io"],
1062
+ details="Premium enterprise customer",
1063
+ notes="VIP support tier, 24/7 coverage",
1064
+ tags=["enterprise", "vip", "priority-support"],
1065
+ group_id=12345,
1066
+ organization_fields={
1067
+ "industry": "Technology",
1068
+ "account_tier": "Enterprise",
1069
+ "annual_revenue": "10M+"
1070
+ }
1071
+ )
1072
+
1073
+ try:
1074
+ created_org = zenpy_client.organizations.create(organization)
1075
+ print(f"Organization created: {created_org.id}")
1076
+ print(f"Name: {created_org.name}")
1077
+
1078
+ if created_org.domain_names:
1079
+ print(f"Domains: {', '.join(created_org.domain_names)}")
1080
+
1081
+ return created_org
1082
+
1083
+ except Exception as e:
1084
+ print(f"Failed to create organization: {e}")
1085
+ return None
1086
+
1087
+ create_organization()
1088
+ ```
1089
+
1090
+ #### Update Organization
1091
+
1092
+ **Basic Example:**
1093
+
1094
+ ```python
1095
+ from zenpy import Zenpy
1096
+
1097
+ zenpy_client = Zenpy(**credentials)
1098
+
1099
+ # Get the organization
1100
+ org_id = 98765
1101
+ org = zenpy_client.organizations(id=org_id)
1102
+
1103
+ # Update organization
1104
+ org.name = "Acme Corporation Ltd."
1105
+ org.details = "Updated company details"
1106
+
1107
+ # Save changes
1108
+ updated_org = zenpy_client.organizations.update(org)
1109
+
1110
+ print("Organization updated!")
1111
+ print(f"Name: {updated_org.name}")
1112
+ ```
1113
+
1114
+ **Advanced Example:**
1115
+
1116
+ ```python
1117
+ from zenpy import Zenpy
1118
+
1119
+ zenpy_client = Zenpy(**credentials)
1120
+
1121
+ def update_organization(org_id):
1122
+ """Update organization with multiple fields"""
1123
+
1124
+ try:
1125
+ org = zenpy_client.organizations(id=org_id)
1126
+
1127
+ # Update multiple fields
1128
+ org.name = "Acme Global Corporation"
1129
+ org.domain_names = ["acme.com", "acme.global", "acmeglobal.com"]
1130
+ org.details = "Global enterprise customer with multiple subsidiaries"
1131
+ org.notes = "Upgraded to platinum tier - assign dedicated account manager"
1132
+ org.tags = ["enterprise", "platinum", "global", "strategic-account"]
1133
+ org.organization_fields = {
1134
+ "account_tier": "Platinum",
1135
+ "contract_renewal": "2025-12-31",
1136
+ "dedicated_support": "yes"
1137
+ }
1138
+
1139
+ # Save changes
1140
+ updated_org = zenpy_client.organizations.update(org)
1141
+
1142
+ print(f"Organization updated: {updated_org.id}")
1143
+ print(f"New name: {updated_org.name}")
1144
+
1145
+ if updated_org.tags:
1146
+ print(f"Tags: {', '.join(updated_org.tags)}")
1147
+
1148
+ return updated_org
1149
+
1150
+ except Exception as e:
1151
+ print(f"Failed to update organization: {e}")
1152
+ return None
1153
+
1154
+ update_organization(98765)
1155
+ ```
1156
+
1157
+ #### Delete Organization
1158
+
1159
+ **Basic Example:**
1160
+
1161
+ ```python
1162
+ from zenpy import Zenpy
1163
+
1164
+ zenpy_client = Zenpy(**credentials)
1165
+
1166
+ # Delete an organization
1167
+ org_id = 98765
1168
+ zenpy_client.organizations.delete(org_id)
1169
+
1170
+ print(f"Organization {org_id} deleted successfully")
1171
+ ```
1172
+
1173
+ ---
1174
+
1175
+ ### Search API
1176
+
1177
+ #### Basic Search
1178
+
1179
+ **Basic Example:**
1180
+
1181
+ ```python
1182
+ from zenpy import Zenpy
1183
+
1184
+ zenpy_client = Zenpy(**credentials)
1185
+
1186
+ # Search for open tickets
1187
+ results = zenpy_client.search('status:open', type='ticket')
1188
+
1189
+ for ticket in results:
1190
+ print(f"#{ticket.id}: {ticket.subject}")
1191
+ ```
1192
+
1193
+ **Advanced Example:**
1194
+
1195
+ ```python
1196
+ from zenpy import Zenpy
1197
+
1198
+ zenpy_client = Zenpy(**credentials)
1199
+
1200
+ def search_open_tickets():
1201
+ """Search for high-priority open tickets"""
1202
+
1203
+ try:
1204
+ results = list(zenpy_client.search('status:open priority:high', type='ticket'))
1205
+
1206
+ print(f"Found {len(results)} high-priority open tickets")
1207
+
1208
+ for ticket in results:
1209
+ print("\n---")
1210
+ print(f"Ticket #{ticket.id}")
1211
+ print(f"Subject: {ticket.subject}")
1212
+ print(f"Priority: {ticket.priority}")
1213
+ print(f"Status: {ticket.status}")
1214
+ print(f"Created: {ticket.created_at}")
1215
+ print(f"Assignee: {ticket.assignee_id or 'Unassigned'}")
1216
+
1217
+ return results
1218
+
1219
+ except Exception as e:
1220
+ print(f"Search failed: {e}")
1221
+ return []
1222
+
1223
+ search_open_tickets()
1224
+ ```
1225
+
1226
+ #### Search Tickets
1227
+
1228
+ **Basic Example:**
1229
+
1230
+ ```python
1231
+ from zenpy import Zenpy
1232
+
1233
+ zenpy_client = Zenpy(**credentials)
1234
+
1235
+ # Search tickets by subject
1236
+ tickets = zenpy_client.search('subject:login', type='ticket')
1237
+
1238
+ print(f"Tickets mentioning 'login': {len(list(tickets))}")
1239
+ ```
1240
+
1241
+ **Advanced Example with Multiple Filters:**
1242
+
1243
+ ```python
1244
+ from zenpy import Zenpy
1245
+
1246
+ zenpy_client = Zenpy(**credentials)
1247
+
1248
+ def advanced_ticket_search():
1249
+ """Perform advanced ticket search with multiple filters"""
1250
+
1251
+ # Build search query
1252
+ query_parts = [
1253
+ 'type:ticket',
1254
+ 'status:open',
1255
+ 'priority:urgent',
1256
+ 'tags:bug',
1257
+ 'created>2025-01-01'
1258
+ ]
1259
+ query = ' '.join(query_parts)
1260
+
1261
+ try:
1262
+ tickets = list(zenpy_client.search(query))
1263
+
1264
+ print(f"Search Query: {query}")
1265
+ print(f"Results: {len(tickets)} tickets\n")
1266
+
1267
+ for ticket in tickets:
1268
+ print(f"#{ticket.id}: {ticket.subject}")
1269
+ print(f" Status: {ticket.status} | Priority: {ticket.priority}")
1270
+
1271
+ if ticket.tags:
1272
+ print(f" Tags: {', '.join(ticket.tags)}")
1273
+
1274
+ print(f" Created: {ticket.created_at}")
1275
+
1276
+ return tickets
1277
+
1278
+ except Exception as e:
1279
+ print(f"Search error: {e}")
1280
+ return []
1281
+
1282
+ advanced_ticket_search()
1283
+ ```
1284
+
1285
+ #### Search Users
1286
+
1287
+ **Basic Example:**
1288
+
1289
+ ```python
1290
+ from zenpy import Zenpy
1291
+
1292
+ zenpy_client = Zenpy(**credentials)
1293
+
1294
+ # Search users by email domain
1295
+ users = zenpy_client.search('email:*@example.com', type='user')
1296
+
1297
+ for user in users:
1298
+ print(f"{user.name} - {user.email}")
1299
+ ```
1300
+
1301
+ **Advanced Example:**
1302
+
1303
+ ```python
1304
+ from zenpy import Zenpy
1305
+
1306
+ zenpy_client = Zenpy(**credentials)
1307
+
1308
+ def find_agents_by_organization(org_id):
1309
+ """Find all agents in a specific organization"""
1310
+
1311
+ query = f'type:user role:agent organization_id:{org_id}'
1312
+
1313
+ try:
1314
+ agents = list(zenpy_client.search(query))
1315
+
1316
+ print(f"Found {len(agents)} agents in organization {org_id}")
1317
+
1318
+ for agent in agents:
1319
+ print("\n---")
1320
+ print(f"Name: {agent.name}")
1321
+ print(f"Email: {agent.email}")
1322
+ print(f"Role: {agent.role}")
1323
+ print(f"Active: {agent.active}")
1324
+ print(f"Last Login: {agent.last_login_at or 'Never'}")
1325
+
1326
+ return agents
1327
+
1328
+ except Exception as e:
1329
+ print(f"User search failed: {e}")
1330
+ return []
1331
+
1332
+ find_agents_by_organization(98765)
1333
+ ```
1334
+
1335
+ #### Search Organizations
1336
+
1337
+ **Basic Example:**
1338
+
1339
+ ```python
1340
+ from zenpy import Zenpy
1341
+
1342
+ zenpy_client = Zenpy(**credentials)
1343
+
1344
+ # Search organizations by name
1345
+ orgs = zenpy_client.search('name:Acme', type='organization')
1346
+
1347
+ for org in orgs:
1348
+ print(f"{org.name} (ID: {org.id})")
1349
+ ```
1350
+
1351
+ **Advanced Example:**
1352
+
1353
+ ```python
1354
+ from zenpy import Zenpy
1355
+
1356
+ zenpy_client = Zenpy(**credentials)
1357
+
1358
+ def search_organizations_by_domain(domain):
1359
+ """Search organizations by domain"""
1360
+
1361
+ query = f'type:organization {domain}'
1362
+
1363
+ try:
1364
+ organizations = list(zenpy_client.search(query))
1365
+
1366
+ print(f"Found {len(organizations)} organizations with domain \"{domain}\"")
1367
+
1368
+ for org in organizations:
1369
+ print("\n=== Organization ===")
1370
+ print(f"ID: {org.id}")
1371
+ print(f"Name: {org.name}")
1372
+
1373
+ if hasattr(org, 'domain_names') and org.domain_names:
1374
+ print(f"Domains: {', '.join(org.domain_names)}")
1375
+ else:
1376
+ print("Domains: None")
1377
+
1378
+ print(f"Details: {org.details or 'N/A'}")
1379
+ print(f"Created: {org.created_at}")
1380
+
1381
+ return organizations
1382
+
1383
+ except Exception as e:
1384
+ print(f"Organization search failed: {e}")
1385
+ return []
1386
+
1387
+ search_organizations_by_domain('acme.com')
1388
+ ```
1389
+
1390
+ #### Search Export (For Large Result Sets)
1391
+
1392
+ **Advanced Example:**
1393
+
1394
+ ```python
1395
+ from zenpy import Zenpy
1396
+
1397
+ zenpy_client = Zenpy(**credentials)
1398
+
1399
+ def export_all_tickets():
1400
+ """Export all tickets using search_export for large datasets"""
1401
+
1402
+ # search_export is designed for exporting large numbers of tickets
1403
+ # It bypasses the 1000-result limit of regular search
1404
+
1405
+ count = 0
1406
+
1407
+ for ticket in zenpy_client.search_export(type='ticket', status='open'):
1408
+ count += 1
1409
+ print(f"#{ticket.id}: {ticket.subject}")
1410
+
1411
+ # Process tickets in batches
1412
+ if count % 100 == 0:
1413
+ print(f"Processed {count} tickets...")
1414
+
1415
+ print(f"\nTotal tickets exported: {count}")
1416
+ return count
1417
+
1418
+ export_all_tickets()
1419
+ ```
1420
+
1421
+ ---
1422
+
1423
+ ### Groups API
1424
+
1425
+ #### List Groups
1426
+
1427
+ **Basic Example:**
1428
+
1429
+ ```python
1430
+ from zenpy import Zenpy
1431
+
1432
+ zenpy_client = Zenpy(**credentials)
1433
+
1434
+ # List all groups
1435
+ for group in zenpy_client.groups():
1436
+ print(f"{group.name} (ID: {group.id})")
1437
+ ```
1438
+
1439
+ **Advanced Example:**
1440
+
1441
+ ```python
1442
+ from zenpy import Zenpy
1443
+
1444
+ zenpy_client = Zenpy(**credentials)
1445
+
1446
+ def list_all_groups():
1447
+ """List all groups with details"""
1448
+
1449
+ groups = list(zenpy_client.groups())
1450
+
1451
+ print(f"Found {len(groups)} groups\n")
1452
+
1453
+ for group in groups:
1454
+ print("=== Group ===")
1455
+ print(f"ID: {group.id}")
1456
+ print(f"Name: {group.name}")
1457
+ print(f"Description: {group.description or 'N/A'}")
1458
+ print(f"Created: {group.created_at}")
1459
+ print(f"Updated: {group.updated_at}")
1460
+ print("---")
1461
+
1462
+ return groups
1463
+
1464
+ list_all_groups()
1465
+ ```
1466
+
1467
+ #### Show Group
1468
+
1469
+ **Basic Example:**
1470
+
1471
+ ```python
1472
+ from zenpy import Zenpy
1473
+
1474
+ zenpy_client = Zenpy(**credentials)
1475
+
1476
+ # Get a single group by ID
1477
+ group_id = 12345
1478
+ group = zenpy_client.groups(id=group_id)
1479
+
1480
+ print(f"Group: {group.name}")
1481
+ print(f"Description: {group.description}")
1482
+ ```
1483
+
1484
+ #### Create Group
1485
+
1486
+ **Basic Example:**
1487
+
1488
+ ```python
1489
+ from zenpy import Zenpy
1490
+ from zenpy.lib.api_objects import Group
1491
+
1492
+ zenpy_client = Zenpy(**credentials)
1493
+
1494
+ # Create a new group
1495
+ group = Group(name="Technical Support Team")
1496
+
1497
+ created_group = zenpy_client.groups.create(group)
1498
+
1499
+ print("Group created!")
1500
+ print(f"ID: {created_group.id}")
1501
+ print(f"Name: {created_group.name}")
1502
+ ```
1503
+
1504
+ **Advanced Example:**
1505
+
1506
+ ```python
1507
+ from zenpy import Zenpy
1508
+ from zenpy.lib.api_objects import Group
1509
+
1510
+ zenpy_client = Zenpy(**credentials)
1511
+
1512
+ def create_support_group():
1513
+ """Create a support group with description"""
1514
+
1515
+ group = Group(
1516
+ name="Enterprise Support Team",
1517
+ description="Dedicated support team for enterprise customers"
1518
+ )
1519
+
1520
+ try:
1521
+ created_group = zenpy_client.groups.create(group)
1522
+ print(f"Group created: {created_group.id}")
1523
+ print(f"Name: {created_group.name}")
1524
+ print(f"Description: {created_group.description}")
1525
+ return created_group
1526
+
1527
+ except Exception as e:
1528
+ print(f"Failed to create group: {e}")
1529
+ return None
1530
+
1531
+ create_support_group()
1532
+ ```
1533
+
1534
+ #### Update Group
1535
+
1536
+ **Basic Example:**
1537
+
1538
+ ```python
1539
+ from zenpy import Zenpy
1540
+
1541
+ zenpy_client = Zenpy(**credentials)
1542
+
1543
+ # Get the group
1544
+ group_id = 12345
1545
+ group = zenpy_client.groups(id=group_id)
1546
+
1547
+ # Update group
1548
+ group.name = "Updated Group Name"
1549
+ group.description = "Updated description"
1550
+
1551
+ # Save changes
1552
+ updated_group = zenpy_client.groups.update(group)
1553
+
1554
+ print("Group updated!")
1555
+ print(f"Name: {updated_group.name}")
1556
+ ```
1557
+
1558
+ #### Delete Group
1559
+
1560
+ **Basic Example:**
1561
+
1562
+ ```python
1563
+ from zenpy import Zenpy
1564
+
1565
+ zenpy_client = Zenpy(**credentials)
1566
+
1567
+ # Delete a group
1568
+ group_id = 12345
1569
+ zenpy_client.groups.delete(group_id)
1570
+
1571
+ print(f"Group {group_id} deleted successfully")
1572
+ ```
1573
+
1574
+ ---
1575
+
1576
+ ### Attachments API
1577
+
1578
+ #### Upload Attachment
1579
+
1580
+ **Basic Example:**
1581
+
1582
+ ```python
1583
+ from zenpy import Zenpy
1584
+
1585
+ zenpy_client = Zenpy(**credentials)
1586
+
1587
+ # Upload a file
1588
+ file_path = './documents/file.pdf'
1589
+ upload = zenpy_client.attachments.upload(file_path)
1590
+
1591
+ print("File uploaded successfully!")
1592
+ print(f"Upload token: {upload.token}")
1593
+ print(f"Attachment: {upload.attachment}")
1594
+ ```
1595
+
1596
+ **Advanced Example with Ticket Creation:**
1597
+
1598
+ ```python
1599
+ from zenpy import Zenpy
1600
+ from zenpy.lib.api_objects import Ticket, User, Comment
1601
+
1602
+ zenpy_client = Zenpy(**credentials)
1603
+
1604
+ def create_ticket_with_attachment():
1605
+ """Create a ticket with an attached file"""
1606
+
1607
+ try:
1608
+ # Step 1: Upload the file
1609
+ file_path = './screenshots/bug-screenshot.png'
1610
+ upload = zenpy_client.attachments.upload(file_path)
1611
+
1612
+ print(f"File uploaded. Token: {upload.token}")
1613
+
1614
+ # Step 2: Create ticket with attachment
1615
+ ticket = Ticket(
1616
+ subject="Bug Report: UI Issue",
1617
+ comment=Comment(
1618
+ body="Please see the attached screenshot showing the UI bug.",
1619
+ uploads=[upload.token]
1620
+ ),
1621
+ requester=User(
1622
+ name="Bug Reporter",
1623
+ email="reporter@example.com"
1624
+ ),
1625
+ priority="high",
1626
+ tags=["bug", "ui"]
1627
+ )
1628
+
1629
+ created_ticket = zenpy_client.tickets.create(ticket)
1630
+
1631
+ print("Ticket created with attachment!")
1632
+ print(f"Ticket ID: {created_ticket.id}")
1633
+ print(f"Subject: {created_ticket.subject}")
1634
+
1635
+ return created_ticket
1636
+
1637
+ except Exception as e:
1638
+ print(f"Error: {e}")
1639
+ return None
1640
+
1641
+ create_ticket_with_attachment()
1642
+ ```
1643
+
1644
+ #### Upload Multiple Attachments
1645
+
1646
+ **Advanced Example:**
1647
+
1648
+ ```python
1649
+ from zenpy import Zenpy
1650
+ from zenpy.lib.api_objects import Ticket, User, Comment
1651
+
1652
+ zenpy_client = Zenpy(**credentials)
1653
+
1654
+ def create_ticket_with_multiple_attachments():
1655
+ """Create a ticket with multiple attached files"""
1656
+
1657
+ try:
1658
+ # Upload multiple files
1659
+ files = [
1660
+ './documents/report.pdf',
1661
+ './screenshots/screen1.png',
1662
+ './screenshots/screen2.png'
1663
+ ]
1664
+
1665
+ upload_tokens = []
1666
+ for file_path in files:
1667
+ upload = zenpy_client.attachments.upload(file_path)
1668
+ upload_tokens.append(upload.token)
1669
+ print(f"Uploaded: {file_path}")
1670
+
1671
+ print(f"Uploaded {len(upload_tokens)} files")
1672
+
1673
+ # Create ticket with all attachments
1674
+ ticket = Ticket(
1675
+ subject="Detailed Bug Report",
1676
+ comment=Comment(
1677
+ body="Please find the attached report and screenshots.",
1678
+ uploads=upload_tokens
1679
+ ),
1680
+ requester=User(
1681
+ name="QA Tester",
1682
+ email="qa@example.com"
1683
+ ),
1684
+ priority="normal"
1685
+ )
1686
+
1687
+ created_ticket = zenpy_client.tickets.create(ticket)
1688
+
1689
+ print("Ticket created with multiple attachments!")
1690
+ print(f"Ticket ID: {created_ticket.id}")
1691
+
1692
+ return created_ticket
1693
+
1694
+ except Exception as e:
1695
+ print(f"Error: {e}")
1696
+ return None
1697
+
1698
+ create_ticket_with_multiple_attachments()
1699
+ ```
1700
+
1701
+ ---
1702
+
1703
+ ### Macros API
1704
+
1705
+ #### List Macros
1706
+
1707
+ **Basic Example:**
1708
+
1709
+ ```python
1710
+ from zenpy import Zenpy
1711
+
1712
+ zenpy_client = Zenpy(**credentials)
1713
+
1714
+ # List all macros
1715
+ for macro in zenpy_client.macros():
1716
+ print(f"{macro.title} (ID: {macro.id})")
1717
+ ```
1718
+
1719
+ **Advanced Example:**
1720
+
1721
+ ```python
1722
+ from zenpy import Zenpy
1723
+
1724
+ zenpy_client = Zenpy(**credentials)
1725
+
1726
+ def list_active_macros():
1727
+ """List all active macros"""
1728
+
1729
+ macros = list(zenpy_client.macros())
1730
+ active_macros = [m for m in macros if m.active]
1731
+
1732
+ print(f"Found {len(active_macros)} active macros\n")
1733
+
1734
+ for macro in active_macros:
1735
+ print("=== Macro ===")
1736
+ print(f"ID: {macro.id}")
1737
+ print(f"Title: {macro.title}")
1738
+ print(f"Description: {macro.description or 'N/A'}")
1739
+ print(f"Active: {macro.active}")
1740
+ print(f"Created: {macro.created_at}")
1741
+ print("---")
1742
+
1743
+ return active_macros
1744
+
1745
+ list_active_macros()
1746
+ ```
1747
+
1748
+ #### Show Macro
1749
+
1750
+ **Basic Example:**
1751
+
1752
+ ```python
1753
+ from zenpy import Zenpy
1754
+
1755
+ zenpy_client = Zenpy(**credentials)
1756
+
1757
+ # Get a single macro by ID
1758
+ macro_id = 54321
1759
+ macro = zenpy_client.macros(id=macro_id)
1760
+
1761
+ print(f"Title: {macro.title}")
1762
+ print(f"Description: {macro.description}")
1763
+ print(f"Actions: {macro.actions}")
1764
+ ```
1765
+
1766
+ #### Apply Macro to Ticket
1767
+
1768
+ **Advanced Example:**
1769
+
1770
+ ```python
1771
+ from zenpy import Zenpy
1772
+
1773
+ zenpy_client = Zenpy(**credentials)
1774
+
1775
+ def apply_macro_to_ticket(ticket_id, macro_id):
1776
+ """Apply a macro to a ticket"""
1777
+
1778
+ try:
1779
+ # Get the ticket
1780
+ ticket = zenpy_client.tickets(id=ticket_id)
1781
+
1782
+ # Show macro effect
1783
+ result = zenpy_client.tickets.show_macro_effect(ticket, macro_id)
1784
+
1785
+ print("Macro applied successfully!")
1786
+ print(f"Result: {result}")
1787
+
1788
+ return result
1789
+
1790
+ except Exception as e:
1791
+ print(f"Error applying macro: {e}")
1792
+ return None
1793
+
1794
+ apply_macro_to_ticket(12345, 54321)
1795
+ ```
1796
+
1797
+ ---
1798
+
1799
+ ### Views API
1800
+
1801
+ #### List Views
1802
+
1803
+ **Basic Example:**
1804
+
1805
+ ```python
1806
+ from zenpy import Zenpy
1807
+
1808
+ zenpy_client = Zenpy(**credentials)
1809
+
1810
+ # List all views
1811
+ for view in zenpy_client.views():
1812
+ print(f"{view.title} (ID: {view.id})")
1813
+ ```
1814
+
1815
+ **Advanced Example:**
1816
+
1817
+ ```python
1818
+ from zenpy import Zenpy
1819
+
1820
+ zenpy_client = Zenpy(**credentials)
1821
+
1822
+ def list_active_views():
1823
+ """List all active views"""
1824
+
1825
+ views = list(zenpy_client.views())
1826
+ active_views = [v for v in views if v.active]
1827
+
1828
+ print(f"Found {len(active_views)} active views\n")
1829
+
1830
+ for view in active_views:
1831
+ print("=== View ===")
1832
+ print(f"ID: {view.id}")
1833
+ print(f"Title: {view.title}")
1834
+ print(f"Description: {view.description or 'N/A'}")
1835
+ print(f"Active: {view.active}")
1836
+ print(f"Position: {view.position}")
1837
+ print("---")
1838
+
1839
+ return active_views
1840
+
1841
+ list_active_views()
1842
+ ```
1843
+
1844
+ #### Execute View
1845
+
1846
+ **Basic Example:**
1847
+
1848
+ ```python
1849
+ from zenpy import Zenpy
1850
+
1851
+ zenpy_client = Zenpy(**credentials)
1852
+
1853
+ # Execute a view
1854
+ view_id = 11111
1855
+ result = zenpy_client.views.execute(view_id)
1856
+
1857
+ print("View executed!")
1858
+ for ticket in result:
1859
+ print(f"#{ticket.id}: {ticket.subject}")
1860
+ ```
1861
+
1862
+ **Advanced Example:**
1863
+
1864
+ ```python
1865
+ from zenpy import Zenpy
1866
+
1867
+ zenpy_client = Zenpy(**credentials)
1868
+
1869
+ def execute_view_and_analyze(view_id):
1870
+ """Execute a view and analyze results"""
1871
+
1872
+ try:
1873
+ result = list(zenpy_client.views.execute(view_id))
1874
+
1875
+ print("=== View Execution Results ===")
1876
+ print(f"Total tickets: {len(result)}")
1877
+
1878
+ # Analyze ticket statuses
1879
+ status_counts = {}
1880
+ for ticket in result:
1881
+ status = ticket.status
1882
+ status_counts[status] = status_counts.get(status, 0) + 1
1883
+
1884
+ print("\n=== Status Distribution ===")
1885
+ for status, count in status_counts.items():
1886
+ print(f"{status}: {count}")
1887
+
1888
+ # Show first 10 tickets
1889
+ print("\n=== Tickets ===")
1890
+ for ticket in result[:10]:
1891
+ print(f"#{ticket.id}: {ticket.subject}")
1892
+ print(f" Status: {ticket.status} | Priority: {ticket.priority}")
1893
+
1894
+ return result
1895
+
1896
+ except Exception as e:
1897
+ print(f"Error executing view: {e}")
1898
+ return []
1899
+
1900
+ execute_view_and_analyze(11111)
1901
+ ```
1902
+
1903
+ ---
1904
+
1905
+ ## Error Handling
1906
+
1907
+ ### Basic Error Handling
1908
+
1909
+ ```python
1910
+ from zenpy import Zenpy
1911
+ from zenpy.lib.exception import ZenpyException
1912
+
1913
+ zenpy_client = Zenpy(**credentials)
1914
+
1915
+ try:
1916
+ ticket = zenpy_client.tickets(id=99999)
1917
+ print(f"Ticket: {ticket.subject}")
1918
+ except ZenpyException as e:
1919
+ print(f"Error occurred: {e}")
1920
+ ```
1921
+
1922
+ ### Advanced Error Handling
1923
+
1924
+ ```python
1925
+ from zenpy import Zenpy
1926
+ from zenpy.lib.exception import ZenpyException, RecordNotFoundException, ZenpyRateLimitExceeded
1927
+ import time
1928
+
1929
+ zenpy_client = Zenpy(**credentials)
1930
+
1931
+ def handle_zendesk_errors():
1932
+ """Demonstrate comprehensive error handling"""
1933
+
1934
+ try:
1935
+ ticket = zenpy_client.tickets(id=99999)
1936
+ return ticket
1937
+
1938
+ except RecordNotFoundException:
1939
+ print("Ticket not found")
1940
+
1941
+ except ZenpyRateLimitExceeded as e:
1942
+ print(f"Rate limit exceeded - retry after: {e.retry_after}")
1943
+ time.sleep(e.retry_after)
1944
+
1945
+ except ZenpyException as e:
1946
+ print(f"Zenpy error: {e}")
1947
+
1948
+ except Exception as e:
1949
+ print(f"Unknown error: {e}")
1950
+
1951
+ return None
1952
+ ```
1953
+
1954
+ ### Retry Logic
1955
+
1956
+ ```python
1957
+ from zenpy import Zenpy
1958
+ from zenpy.lib.exception import ZenpyRateLimitExceeded
1959
+ import time
1960
+
1961
+ zenpy_client = Zenpy(**credentials)
1962
+
1963
+ def fetch_ticket_with_retry(ticket_id, max_retries=3):
1964
+ """Fetch a ticket with automatic retry logic"""
1965
+
1966
+ for attempt in range(1, max_retries + 1):
1967
+ try:
1968
+ ticket = zenpy_client.tickets(id=ticket_id)
1969
+ return ticket
1970
+
1971
+ except ZenpyRateLimitExceeded as e:
1972
+ if attempt < max_retries:
1973
+ wait_time = e.retry_after if hasattr(e, 'retry_after') else 2 ** attempt
1974
+ print(f"Rate limited. Retrying in {wait_time}s...")
1975
+ time.sleep(wait_time)
1976
+ else:
1977
+ print(f"Failed after {max_retries} attempts")
1978
+ raise
1979
+
1980
+ except Exception as e:
1981
+ if attempt < max_retries:
1982
+ print(f"Error. Attempt {attempt}/{max_retries}")
1983
+ time.sleep(1)
1984
+ else:
1985
+ raise
1986
+
1987
+ return None
1988
+
1989
+ ticket = fetch_ticket_with_retry(12345)
1990
+ ```
1991
+
1992
+ ---
1993
+
1994
+ ## Complete Working Examples
1995
+
1996
+ ### Basic Support Ticket System
1997
+
1998
+ ```python
1999
+ import os
2000
+ from zenpy import Zenpy
2001
+ from zenpy.lib.api_objects import Ticket, User
2002
+ from dotenv import load_dotenv
2003
+
2004
+ load_dotenv()
2005
+
2006
+ credentials = {
2007
+ 'email': os.getenv('ZENDESK_EMAIL'),
2008
+ 'token': os.getenv('ZENDESK_API_TOKEN'),
2009
+ 'subdomain': os.getenv('ZENDESK_SUBDOMAIN')
2010
+ }
2011
+
2012
+ zenpy_client = Zenpy(**credentials)
2013
+
2014
+ def create_support_ticket(customer_email, subject, description):
2015
+ """Create a support ticket for a customer"""
2016
+
2017
+ try:
2018
+ # Check if user exists
2019
+ users = list(zenpy_client.search(customer_email, type='user'))
2020
+
2021
+ if users:
2022
+ user = users[0]
2023
+ print(f"Found existing user: {user.name}")
2024
+ else:
2025
+ # Create new user
2026
+ user = User(
2027
+ name=customer_email.split('@')[0],
2028
+ email=customer_email,
2029
+ role='end-user'
2030
+ )
2031
+ user = zenpy_client.users.create(user)
2032
+ print(f"Created new user: {user.name}")
2033
+
2034
+ # Create ticket
2035
+ ticket = Ticket(
2036
+ subject=subject,
2037
+ description=description,
2038
+ requester_id=user.id,
2039
+ priority='normal',
2040
+ status='new'
2041
+ )
2042
+
2043
+ created_ticket = zenpy_client.tickets.create(ticket)
2044
+
2045
+ print("Ticket created successfully!")
2046
+ print(f"Ticket ID: {created_ticket.id}")
2047
+ print(f"Subject: {created_ticket.subject}")
2048
+
2049
+ return created_ticket
2050
+
2051
+ except Exception as e:
2052
+ print(f"Error creating support ticket: {e}")
2053
+ return None
2054
+
2055
+ # Usage
2056
+ create_support_ticket(
2057
+ 'customer@example.com',
2058
+ 'Cannot access my account',
2059
+ 'I am unable to log into my account. I keep getting an error message.'
2060
+ )
2061
+ ```
2062
+
2063
+ ### Ticket Management Dashboard
2064
+
2065
+ ```python
2066
+ import os
2067
+ from zenpy import Zenpy
2068
+ from dotenv import load_dotenv
2069
+
2070
+ load_dotenv()
2071
+
2072
+ credentials = {
2073
+ 'email': os.getenv('ZENDESK_EMAIL'),
2074
+ 'token': os.getenv('ZENDESK_API_TOKEN'),
2075
+ 'subdomain': os.getenv('ZENDESK_SUBDOMAIN')
2076
+ }
2077
+
2078
+ zenpy_client = Zenpy(**credentials)
2079
+
2080
+ def get_dashboard_stats():
2081
+ """Get support dashboard statistics"""
2082
+
2083
+ try:
2084
+ # Search for different ticket categories
2085
+ open_tickets = list(zenpy_client.search('type:ticket status:open'))
2086
+ pending_tickets = list(zenpy_client.search('type:ticket status:pending'))
2087
+ solved_tickets = list(zenpy_client.search('type:ticket status:solved created>2025-01-01'))
2088
+ urgent_tickets = list(zenpy_client.search('type:ticket priority:urgent status<solved'))
2089
+
2090
+ print("=== Support Dashboard ===")
2091
+ print(f"Open Tickets: {len(open_tickets)}")
2092
+ print(f"Pending Tickets: {len(pending_tickets)}")
2093
+ print(f"Solved This Year: {len(solved_tickets)}")
2094
+ print(f"Urgent Tickets: {len(urgent_tickets)}")
2095
+
2096
+ # List urgent tickets
2097
+ if urgent_tickets:
2098
+ print("\n=== Urgent Tickets Requiring Attention ===")
2099
+ for ticket in urgent_tickets:
2100
+ print(f"#{ticket.id}: {ticket.subject}")
2101
+ print(f" Status: {ticket.status} | Created: {ticket.created_at}")
2102
+ print(f" Assignee: {ticket.assignee_id or 'Unassigned'}")
2103
+
2104
+ return {
2105
+ 'open': len(open_tickets),
2106
+ 'pending': len(pending_tickets),
2107
+ 'solved': len(solved_tickets),
2108
+ 'urgent': len(urgent_tickets)
2109
+ }
2110
+
2111
+ except Exception as e:
2112
+ print(f"Error fetching dashboard stats: {e}")
2113
+ return None
2114
+
2115
+ get_dashboard_stats()
2116
+ ```
2117
+
2118
+ ### Bulk Ticket Operations
2119
+
2120
+ ```python
2121
+ import os
2122
+ from zenpy import Zenpy
2123
+ from zenpy.lib.api_objects import Comment
2124
+ from dotenv import load_dotenv
2125
+
2126
+ load_dotenv()
2127
+
2128
+ credentials = {
2129
+ 'email': os.getenv('ZENDESK_EMAIL'),
2130
+ 'token': os.getenv('ZENDESK_API_TOKEN'),
2131
+ 'subdomain': os.getenv('ZENDESK_SUBDOMAIN')
2132
+ }
2133
+
2134
+ zenpy_client = Zenpy(**credentials)
2135
+
2136
+ def bulk_update_tickets(ticket_ids, status='solved', comment_text=None):
2137
+ """Update multiple tickets at once"""
2138
+
2139
+ try:
2140
+ print(f"Updating {len(ticket_ids)} tickets...")
2141
+
2142
+ results = []
2143
+
2144
+ for ticket_id in ticket_ids:
2145
+ try:
2146
+ ticket = zenpy_client.tickets(id=ticket_id)
2147
+ ticket.status = status
2148
+
2149
+ if comment_text:
2150
+ ticket.comment = Comment(body=comment_text, public=False)
2151
+
2152
+ updated_ticket = zenpy_client.tickets.update(ticket)
2153
+ print(f"✓ Updated ticket #{ticket_id}")
2154
+ results.append({'success': True, 'ticket_id': ticket_id, 'ticket': updated_ticket})
2155
+
2156
+ except Exception as e:
2157
+ print(f"✗ Failed to update ticket #{ticket_id}: {e}")
2158
+ results.append({'success': False, 'ticket_id': ticket_id, 'error': str(e)})
2159
+
2160
+ successful = len([r for r in results if r['success']])
2161
+ failed = len([r for r in results if not r['success']])
2162
+
2163
+ print(f"\n=== Bulk Update Complete ===")
2164
+ print(f"Successful: {successful}")
2165
+ print(f"Failed: {failed}")
2166
+
2167
+ return results
2168
+
2169
+ except Exception as e:
2170
+ print(f"Bulk update error: {e}")
2171
+ return None
2172
+
2173
+ # Usage: Close multiple tickets at once
2174
+ ticket_ids = [12345, 12346, 12347]
2175
+ bulk_update_tickets(
2176
+ ticket_ids,
2177
+ status='solved',
2178
+ comment_text='This ticket has been resolved and closed.'
2179
+ )
2180
+ ```
2181
+
2182
+ ---
2183
+
2184
+ ## Additional Features
2185
+
2186
+ ### Object Conversion
2187
+
2188
+ Zenpy provides convenient methods for converting objects:
2189
+
2190
+ ```python
2191
+ from zenpy import Zenpy
2192
+
2193
+ zenpy_client = Zenpy(**credentials)
2194
+
2195
+ # Get a ticket
2196
+ ticket = zenpy_client.tickets(id=12345)
2197
+
2198
+ # Convert to dictionary
2199
+ ticket_dict = ticket.to_dict()
2200
+ print(ticket_dict)
2201
+
2202
+ # Convert to JSON
2203
+ ticket_json = ticket.to_json()
2204
+ print(ticket_json)
2205
+ ```
2206
+
2207
+ ### Lazy Attribute Evaluation
2208
+
2209
+ Zenpy uses lazy evaluation to minimize API calls:
2210
+
2211
+ ```python
2212
+ from zenpy import Zenpy
2213
+
2214
+ zenpy_client = Zenpy(**credentials)
2215
+
2216
+ # Get a ticket
2217
+ ticket = zenpy_client.tickets(id=12345)
2218
+
2219
+ # Accessing requester automatically fetches the User object
2220
+ print(f"Requester: {ticket.requester.name}") # Only one API call
2221
+
2222
+ # Accessing organization
2223
+ if ticket.organization:
2224
+ print(f"Organization: {ticket.organization.name}")
2225
+ ```
2226
+
2227
+ ### Caching
2228
+
2229
+ Zenpy automatically caches objects to reduce API calls:
2230
+
2231
+ ```python
2232
+ from zenpy import Zenpy
2233
+
2234
+ zenpy_client = Zenpy(**credentials)
2235
+
2236
+ # First access - API call made
2237
+ ticket1 = zenpy_client.tickets(id=12345)
2238
+
2239
+ # Second access - served from cache
2240
+ ticket2 = zenpy_client.tickets(id=12345)
2241
+
2242
+ # Both references point to the same object
2243
+ assert ticket1 is ticket2
2244
+ ```
2245
+
2246
+ ---
2247
+
2248
+ ## Best Practices
2249
+
2250
+ 1. **Always use environment variables for credentials**
2251
+ 2. **Handle rate limiting with appropriate retry logic**
2252
+ 3. **Use search_export for large result sets (>1000 items)**
2253
+ 4. **Leverage lazy evaluation and caching**
2254
+ 5. **Catch specific Zenpy exceptions for better error handling**
2255
+ 6. **Use batch operations when updating multiple tickets**
2256
+ 7. **Validate user input before creating tickets**
2257
+ 8. **Use custom fields appropriately for your organization**
2258
+ 9. **Test with a sandbox account before production**
2259
+ 10. **Monitor API usage to avoid rate limits**
2260
+
2261
+ ---
2262
+
2263
+ ## Rate Limits
2264
+
2265
+ Zendesk enforces rate limits on API requests:
2266
+
2267
+ - **Standard**: 700 requests per minute
2268
+ - **Enterprise**: 2500+ requests per minute (varies by plan)
2269
+
2270
+ Zenpy automatically handles rate limiting by:
2271
+ - Tracking request counts
2272
+ - Sleeping when limits are approached
2273
+ - Raising `ZenpyRateLimitExceeded` when exceeded
2274
+
2275
+ ```python
2276
+ from zenpy import Zenpy
2277
+
2278
+ # Configure custom rate limit
2279
+ credentials = {
2280
+ 'email': 'your_email@example.com',
2281
+ 'token': 'your_api_token',
2282
+ 'subdomain': 'your_subdomain',
2283
+ 'ratelimit': 700 # Requests per minute
2284
+ }
2285
+
2286
+ zenpy_client = Zenpy(**credentials)
2287
+ ```
2288
+
2289
+ ---
2290
+
2291
+ ## Resources
2292
+
2293
+ - **GitHub Repository**: https://github.com/facetoe/zenpy
2294
+ - **Documentation**: http://docs.facetoe.com.au/zenpy.html
2295
+ - **PyPI Package**: https://pypi.org/project/zenpy/
2296
+ - **Zendesk API Docs**: https://developer.zendesk.com/api-reference/
2297
+ - **Issue Tracker**: https://github.com/facetoe/zenpy/issues