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.
- package/README.md +55 -0
- package/bin/chub-mcp +2 -0
- package/dist/airtable/docs/database/javascript/DOC.md +1437 -0
- package/dist/airtable/docs/database/python/DOC.md +1735 -0
- package/dist/amplitude/docs/analytics/javascript/DOC.md +1282 -0
- package/dist/amplitude/docs/analytics/python/DOC.md +1199 -0
- package/dist/anthropic/docs/claude-api/javascript/DOC.md +503 -0
- package/dist/anthropic/docs/claude-api/python/DOC.md +389 -0
- package/dist/asana/docs/tasks/DOC.md +1396 -0
- package/dist/assemblyai/docs/transcription/DOC.md +1043 -0
- package/dist/atlassian/docs/confluence/javascript/DOC.md +1347 -0
- package/dist/atlassian/docs/confluence/python/DOC.md +1604 -0
- package/dist/auth0/docs/identity/javascript/DOC.md +968 -0
- package/dist/auth0/docs/identity/python/DOC.md +1199 -0
- package/dist/aws/docs/s3/javascript/DOC.md +1773 -0
- package/dist/aws/docs/s3/python/DOC.md +1807 -0
- package/dist/binance/docs/trading/javascript/DOC.md +1315 -0
- package/dist/binance/docs/trading/python/DOC.md +1454 -0
- package/dist/braintree/docs/gateway/javascript/DOC.md +1278 -0
- package/dist/braintree/docs/gateway/python/DOC.md +1179 -0
- package/dist/chromadb/docs/embeddings-db/javascript/DOC.md +1263 -0
- package/dist/chromadb/docs/embeddings-db/python/DOC.md +1707 -0
- package/dist/clerk/docs/auth/javascript/DOC.md +1220 -0
- package/dist/clerk/docs/auth/python/DOC.md +274 -0
- package/dist/cloudflare/docs/workers/javascript/DOC.md +918 -0
- package/dist/cloudflare/docs/workers/python/DOC.md +994 -0
- package/dist/cockroachdb/docs/distributed-db/DOC.md +1500 -0
- package/dist/cohere/docs/llm/DOC.md +1335 -0
- package/dist/datadog/docs/monitoring/javascript/DOC.md +1740 -0
- package/dist/datadog/docs/monitoring/python/DOC.md +1815 -0
- package/dist/deepgram/docs/speech/javascript/DOC.md +885 -0
- package/dist/deepgram/docs/speech/python/DOC.md +685 -0
- package/dist/deepl/docs/translation/javascript/DOC.md +887 -0
- package/dist/deepl/docs/translation/python/DOC.md +944 -0
- package/dist/deepseek/docs/llm/DOC.md +1220 -0
- package/dist/directus/docs/headless-cms/javascript/DOC.md +1128 -0
- package/dist/directus/docs/headless-cms/python/DOC.md +1276 -0
- package/dist/discord/docs/bot/javascript/DOC.md +1090 -0
- package/dist/discord/docs/bot/python/DOC.md +1130 -0
- package/dist/elasticsearch/docs/search/DOC.md +1634 -0
- package/dist/elevenlabs/docs/text-to-speech/javascript/DOC.md +336 -0
- package/dist/elevenlabs/docs/text-to-speech/python/DOC.md +552 -0
- package/dist/firebase/docs/auth/DOC.md +1015 -0
- package/dist/gemini/docs/genai/javascript/DOC.md +691 -0
- package/dist/gemini/docs/genai/python/DOC.md +555 -0
- package/dist/github/docs/octokit/DOC.md +1560 -0
- package/dist/google/docs/bigquery/javascript/DOC.md +1688 -0
- package/dist/google/docs/bigquery/python/DOC.md +1503 -0
- package/dist/hubspot/docs/crm/javascript/DOC.md +1805 -0
- package/dist/hubspot/docs/crm/python/DOC.md +2033 -0
- package/dist/huggingface/docs/transformers/DOC.md +948 -0
- package/dist/intercom/docs/messaging/javascript/DOC.md +1844 -0
- package/dist/intercom/docs/messaging/python/DOC.md +1797 -0
- package/dist/jira/docs/issues/javascript/DOC.md +1420 -0
- package/dist/jira/docs/issues/python/DOC.md +1492 -0
- package/dist/kafka/docs/streaming/javascript/DOC.md +1671 -0
- package/dist/kafka/docs/streaming/python/DOC.md +1464 -0
- package/dist/landingai-ade/docs/api/DOC.md +620 -0
- package/dist/landingai-ade/docs/sdk/python/DOC.md +489 -0
- package/dist/landingai-ade/docs/sdk/typescript/DOC.md +542 -0
- package/dist/landingai-ade/skills/SKILL.md +489 -0
- package/dist/launchdarkly/docs/feature-flags/javascript/DOC.md +1191 -0
- package/dist/launchdarkly/docs/feature-flags/python/DOC.md +1671 -0
- package/dist/linear/docs/tracker/DOC.md +1554 -0
- package/dist/livekit/docs/realtime/javascript/DOC.md +303 -0
- package/dist/livekit/docs/realtime/python/DOC.md +163 -0
- package/dist/mailchimp/docs/marketing/DOC.md +1420 -0
- package/dist/meilisearch/docs/search/DOC.md +1241 -0
- package/dist/microsoft/docs/onedrive/javascript/DOC.md +1421 -0
- package/dist/microsoft/docs/onedrive/python/DOC.md +1549 -0
- package/dist/mongodb/docs/atlas/DOC.md +2041 -0
- package/dist/notion/docs/workspace-api/javascript/DOC.md +1435 -0
- package/dist/notion/docs/workspace-api/python/DOC.md +1400 -0
- package/dist/okta/docs/identity/javascript/DOC.md +1171 -0
- package/dist/okta/docs/identity/python/DOC.md +1401 -0
- package/dist/openai/docs/chat/javascript/DOC.md +407 -0
- package/dist/openai/docs/chat/python/DOC.md +568 -0
- package/dist/paypal/docs/checkout/DOC.md +278 -0
- package/dist/pinecone/docs/sdk/javascript/DOC.md +984 -0
- package/dist/pinecone/docs/sdk/python/DOC.md +1395 -0
- package/dist/plaid/docs/banking/javascript/DOC.md +1163 -0
- package/dist/plaid/docs/banking/python/DOC.md +1203 -0
- package/dist/playwright-community/skills/login-flows/SKILL.md +108 -0
- package/dist/postmark/docs/transactional-email/DOC.md +1168 -0
- package/dist/prisma/docs/orm/javascript/DOC.md +1419 -0
- package/dist/prisma/docs/orm/python/DOC.md +1317 -0
- package/dist/qdrant/docs/vector-search/javascript/DOC.md +1221 -0
- package/dist/qdrant/docs/vector-search/python/DOC.md +1653 -0
- package/dist/rabbitmq/docs/message-queue/javascript/DOC.md +1193 -0
- package/dist/rabbitmq/docs/message-queue/python/DOC.md +1243 -0
- package/dist/razorpay/docs/payments/javascript/DOC.md +1219 -0
- package/dist/razorpay/docs/payments/python/DOC.md +1330 -0
- package/dist/redis/docs/key-value/javascript/DOC.md +1851 -0
- package/dist/redis/docs/key-value/python/DOC.md +2054 -0
- package/dist/registry.json +2817 -0
- package/dist/replicate/docs/model-hosting/DOC.md +1318 -0
- package/dist/resend/docs/email/DOC.md +1271 -0
- package/dist/salesforce/docs/crm/javascript/DOC.md +1241 -0
- package/dist/salesforce/docs/crm/python/DOC.md +1183 -0
- package/dist/search-index.json +1 -0
- package/dist/sendgrid/docs/email-api/javascript/DOC.md +371 -0
- package/dist/sendgrid/docs/email-api/python/DOC.md +656 -0
- package/dist/sentry/docs/error-tracking/javascript/DOC.md +1073 -0
- package/dist/sentry/docs/error-tracking/python/DOC.md +1309 -0
- package/dist/shopify/docs/storefront/DOC.md +457 -0
- package/dist/slack/docs/workspace/javascript/DOC.md +933 -0
- package/dist/slack/docs/workspace/python/DOC.md +271 -0
- package/dist/square/docs/payments/javascript/DOC.md +1855 -0
- package/dist/square/docs/payments/python/DOC.md +1728 -0
- package/dist/stripe/docs/api/DOC.md +1727 -0
- package/dist/stripe/docs/payments/DOC.md +1726 -0
- package/dist/stytch/docs/auth/javascript/DOC.md +1813 -0
- package/dist/stytch/docs/auth/python/DOC.md +1962 -0
- package/dist/supabase/docs/client/DOC.md +1606 -0
- package/dist/twilio/docs/messaging/python/DOC.md +469 -0
- package/dist/twilio/docs/messaging/typescript/DOC.md +946 -0
- package/dist/vercel/docs/platform/DOC.md +1940 -0
- package/dist/weaviate/docs/vector-db/javascript/DOC.md +1268 -0
- package/dist/weaviate/docs/vector-db/python/DOC.md +1388 -0
- package/dist/zendesk/docs/support/javascript/DOC.md +2150 -0
- package/dist/zendesk/docs/support/python/DOC.md +2297 -0
- package/package.json +22 -6
- package/skills/get-api-docs/SKILL.md +84 -0
- package/src/commands/annotate.js +83 -0
- package/src/commands/build.js +12 -1
- package/src/commands/feedback.js +150 -0
- package/src/commands/get.js +83 -42
- package/src/commands/search.js +7 -0
- package/src/index.js +43 -17
- package/src/lib/analytics.js +90 -0
- package/src/lib/annotations.js +57 -0
- package/src/lib/bm25.js +170 -0
- package/src/lib/cache.js +69 -6
- package/src/lib/config.js +8 -3
- package/src/lib/identity.js +99 -0
- package/src/lib/registry.js +103 -20
- package/src/lib/telemetry.js +86 -0
- package/src/mcp/server.js +177 -0
- 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
|