0
# Jira API
1
2
Comprehensive Jira REST API client providing access to all Jira functionality including issues, projects, users, workflows, and administration. Supports both Jira Cloud and Server/Data Center platforms with 353 public methods covering the complete Jira API surface.
3
4
## Initialization
5
6
```python { .api }
7
class Jira(AtlassianRestAPI):
8
def __init__(self, url: str, username: str = None, password: str = None,
9
token: str = None, cloud: bool = None, api_version: str = "2",
10
**kwargs):
11
"""
12
Initialize Jira client.
13
14
Parameters:
15
- url (str): Base URL of Jira instance
16
- username (str, optional): Username for authentication
17
- password (str, optional): Password or API token
18
- token (str, optional): Bearer token for authentication
19
- cloud (bool, optional): True for Cloud, False for Server/DC
20
- api_version (str): API version, defaults to "2"
21
"""
22
```
23
24
## Capabilities
25
26
### Issue Management
27
28
Core functionality for creating, reading, updating, and deleting Jira issues with comprehensive field and metadata support.
29
30
```python { .api }
31
def get_issue(self, issue_id_or_key: T_id, fields: Optional[str] = None,
32
properties: Optional[str] = None, update_history: bool = True,
33
expand: Optional[str] = None) -> T_resp_json:
34
"""
35
Get issue details with optional field filtering.
36
37
Parameters:
38
- issue_id_or_key: Issue key (e.g., "PROJ-123") or numeric ID
39
- fields: Comma-separated field names or "*all"
40
- properties: Comma-separated property keys
41
- update_history: Include change history
42
- expand: Additional data to expand
43
44
Returns:
45
dict: Issue data with fields, transitions, changelog
46
"""
47
48
def issue(self, key: str, fields: str = "*all", expand: Optional[str] = None) -> T_resp_json:
49
"""
50
Simplified issue retrieval.
51
52
Parameters:
53
- key: Issue key (e.g., "PROJ-123")
54
- fields: Field specification or "*all"
55
- expand: Additional data to expand
56
57
Returns:
58
dict: Issue data
59
"""
60
61
def create_issue(self, fields: dict, update_history: bool = False,
62
update: Optional[dict] = None) -> T_resp_json:
63
"""
64
Create new issue.
65
66
Parameters:
67
- fields: Issue field data (project, summary, issuetype, etc.)
68
- update_history: Include creation in change history
69
- update: Additional update operations
70
71
Returns:
72
dict: Created issue with key and URL
73
"""
74
75
def create_issues(self, list_of_issues_data: List[dict]) -> T_resp_json:
76
"""
77
Bulk create multiple issues.
78
79
Parameters:
80
- list_of_issues_data: List of issue field dictionaries
81
82
Returns:
83
dict: Creation results with successes and errors
84
"""
85
86
def update_issue_field(self, key: str, fields: dict,
87
notify_users: bool = True) -> T_resp_json:
88
"""
89
Update issue fields.
90
91
Parameters:
92
- key: Issue key
93
- fields: Field updates to apply
94
- notify_users: Send notifications
95
96
Returns:
97
dict: Update confirmation
98
"""
99
100
def delete_issue(self, issue_id_or_key: T_id, delete_subtasks: bool = True) -> bool:
101
"""
102
Delete issue.
103
104
Parameters:
105
- issue_id_or_key: Issue identifier
106
- delete_subtasks: Also delete subtasks
107
108
Returns:
109
bool: True if deletion successful
110
"""
111
112
def issue_exists(self, issue_key: str) -> bool:
113
"""
114
Check if issue exists.
115
116
Parameters:
117
- issue_key: Issue key to check
118
119
Returns:
120
bool: True if issue exists
121
"""
122
```
123
124
### Issue Transitions and Status
125
126
```python { .api }
127
def get_issue_transitions(self, issue_key: str) -> T_resp_json:
128
"""
129
Get available transitions for issue.
130
131
Parameters:
132
- issue_key: Issue key
133
134
Returns:
135
dict: Available transitions with IDs and names
136
"""
137
138
def set_issue_status(self, issue_key: str, status_name: str,
139
fields: Optional[dict] = None,
140
update: Optional[dict] = None) -> T_resp_json:
141
"""
142
Transition issue to new status.
143
144
Parameters:
145
- issue_key: Issue key
146
- status_name: Target status name
147
- fields: Additional field updates
148
- update: Update operations to perform
149
150
Returns:
151
dict: Transition result
152
"""
153
154
def get_issue_status(self, issue_key: str) -> T_resp_json:
155
"""
156
Get current issue status.
157
158
Parameters:
159
- issue_key: Issue key
160
161
Returns:
162
dict: Status information
163
"""
164
```
165
166
### Issue Search and JQL
167
168
```python { .api }
169
def jql(self, jql: str, fields: str = "*all", start: int = 0,
170
limit: Optional[int] = None, expand: Optional[str] = None,
171
validate_query: Optional[bool] = None) -> T_resp_json:
172
"""
173
Execute JQL query.
174
175
Parameters:
176
- jql: JQL query string
177
- fields: Fields to retrieve
178
- start: Starting index for pagination
179
- limit: Maximum results to return
180
- expand: Additional data to expand
181
- validate_query: Validate JQL syntax
182
183
Returns:
184
dict: Search results with issues array
185
"""
186
187
def enhanced_jql(self, jql: str, fields: str = "*all",
188
nextPageToken: Optional[str] = None,
189
limit: Optional[int] = None,
190
expand: Optional[str] = None) -> T_resp_json:
191
"""
192
Enhanced JQL with token-based pagination (Cloud only).
193
194
Parameters:
195
- jql: JQL query string
196
- fields: Fields to retrieve
197
- nextPageToken: Pagination token
198
- limit: Maximum results per page
199
- expand: Additional data to expand
200
201
Returns:
202
dict: Enhanced search results with nextPageToken
203
"""
204
205
def jql_get_list_of_tickets(self, jql: str, fields: str = "*all",
206
expand: Optional[str] = None) -> List[dict]:
207
"""
208
Get all issues matching JQL query.
209
210
Parameters:
211
- jql: JQL query string
212
- fields: Fields to retrieve
213
- expand: Additional data to expand
214
215
Returns:
216
List[dict]: All matching issues
217
"""
218
```
219
220
### Comments and Worklogs
221
222
```python { .api }
223
def issue_get_comments(self, issue_id: T_id) -> T_resp_json:
224
"""
225
Get issue comments.
226
227
Parameters:
228
- issue_id: Issue key or ID
229
230
Returns:
231
dict: Comments data with pagination
232
"""
233
234
def issue_add_comment(self, issue_key: str, comment: str,
235
visibility: Optional[dict] = None) -> T_resp_json:
236
"""
237
Add comment to issue.
238
239
Parameters:
240
- issue_key: Issue key
241
- comment: Comment text (wiki markup or plain text)
242
- visibility: Visibility restrictions (role/group)
243
244
Returns:
245
dict: Created comment data
246
"""
247
248
def issue_add_worklog(self, key: str, started: str, time_sec: int,
249
comment: Optional[str] = None) -> T_resp_json:
250
"""
251
Add worklog to issue.
252
253
Parameters:
254
- key: Issue key
255
- started: Start date/time (ISO format)
256
- time_sec: Time spent in seconds
257
- comment: Optional worklog comment
258
259
Returns:
260
dict: Created worklog data
261
"""
262
263
def issue_get_worklog(self, issue_id_or_key: T_id) -> T_resp_json:
264
"""
265
Get issue worklogs.
266
267
Parameters:
268
- issue_id_or_key: Issue identifier
269
270
Returns:
271
dict: Worklog data with totals
272
"""
273
```
274
275
### Attachments
276
277
```python { .api }
278
def add_attachment(self, issue_key: str, filename: str) -> T_resp_json:
279
"""
280
Add attachment from file.
281
282
Parameters:
283
- issue_key: Issue key
284
- filename: Path to file to attach
285
286
Returns:
287
dict: Attachment metadata
288
"""
289
290
def get_attachment(self, attachment_id: str) -> T_resp_json:
291
"""
292
Get attachment metadata.
293
294
Parameters:
295
- attachment_id: Attachment ID
296
297
Returns:
298
dict: Attachment details (filename, size, author, etc.)
299
"""
300
301
def get_attachment_content(self, attachment_id: str) -> bytes:
302
"""
303
Download attachment content.
304
305
Parameters:
306
- attachment_id: Attachment ID
307
308
Returns:
309
bytes: File content
310
"""
311
312
def remove_attachment(self, attachment_id: str) -> bool:
313
"""
314
Delete attachment.
315
316
Parameters:
317
- attachment_id: Attachment ID
318
319
Returns:
320
bool: True if deletion successful
321
"""
322
```
323
324
### User Management
325
326
```python { .api }
327
def user(self, username: Optional[str] = None, key: Optional[str] = None,
328
account_id: Optional[str] = None, expand: Optional[str] = None) -> T_resp_json:
329
"""
330
Get user details.
331
332
Parameters:
333
- username: Username (Server/DC)
334
- key: User key (Server/DC)
335
- account_id: Account ID (Cloud)
336
- expand: Additional data to expand
337
338
Returns:
339
dict: User information
340
"""
341
342
def myself(self) -> T_resp_json:
343
"""
344
Get current user information.
345
346
Returns:
347
dict: Current user details
348
"""
349
350
def user_find_by_user_string(self, username: Optional[str] = None,
351
query: Optional[str] = None,
352
account_id: Optional[str] = None,
353
start: int = 0, limit: int = 50) -> List[dict]:
354
"""
355
Search users.
356
357
Parameters:
358
- username: Exact username match
359
- query: Search query string
360
- account_id: Account ID to find
361
- start: Starting index
362
- limit: Maximum results
363
364
Returns:
365
List[dict]: Matching users
366
"""
367
368
def user_create(self, username: str, email: str, display_name: str,
369
password: Optional[str] = None,
370
notification: Optional[bool] = None) -> T_resp_json:
371
"""
372
Create user (Server/DC only).
373
374
Parameters:
375
- username: Unique username
376
- email: Email address
377
- display_name: Display name
378
- password: Initial password
379
- notification: Send notification email
380
381
Returns:
382
dict: Created user data
383
"""
384
```
385
386
### Project Management
387
388
```python { .api }
389
def projects(self, included_archived: Optional[bool] = None,
390
expand: Optional[str] = None) -> List[dict]:
391
"""
392
Get all projects.
393
394
Parameters:
395
- included_archived: Include archived projects
396
- expand: Additional data to expand
397
398
Returns:
399
List[dict]: Project list with metadata
400
"""
401
402
def get_project(self, key: str, expand: Optional[str] = None) -> T_resp_json:
403
"""
404
Get project details.
405
406
Parameters:
407
- key: Project key
408
- expand: Additional data to expand
409
410
Returns:
411
dict: Project information with components, versions, roles
412
"""
413
414
def get_project_components(self, key: str) -> List[dict]:
415
"""
416
Get project components.
417
418
Parameters:
419
- key: Project key
420
421
Returns:
422
List[dict]: Component list
423
"""
424
425
def get_project_versions(self, key: str,
426
expand: Optional[str] = None) -> List[dict]:
427
"""
428
Get project versions.
429
430
Parameters:
431
- key: Project key
432
- expand: Additional data to expand
433
434
Returns:
435
List[dict]: Version list
436
"""
437
438
def get_all_project_issues(self, project: str, fields: str = "*all",
439
start: int = 0, limit: Optional[int] = None) -> List[dict]:
440
"""
441
Get all issues in project.
442
443
Parameters:
444
- project: Project key
445
- fields: Fields to retrieve
446
- start: Starting index
447
- limit: Maximum results
448
449
Returns:
450
List[dict]: Project issues
451
"""
452
```
453
454
### Group Management
455
456
```python { .api }
457
def get_groups(self, query: Optional[str] = None,
458
exclude: Optional[str] = None, limit: int = 20) -> List[dict]:
459
"""
460
Search groups.
461
462
Parameters:
463
- query: Search query
464
- exclude: Groups to exclude
465
- limit: Maximum results
466
467
Returns:
468
List[dict]: Matching groups
469
"""
470
471
def create_group(self, name: str) -> T_resp_json:
472
"""
473
Create group.
474
475
Parameters:
476
- name: Group name
477
478
Returns:
479
dict: Created group data
480
"""
481
482
def add_user_to_group(self, username: Optional[str] = None,
483
group_name: Optional[str] = None,
484
account_id: Optional[str] = None) -> T_resp_json:
485
"""
486
Add user to group.
487
488
Parameters:
489
- username: Username (Server/DC)
490
- group_name: Group name
491
- account_id: Account ID (Cloud)
492
493
Returns:
494
dict: Operation result
495
"""
496
497
def get_all_users_from_group(self, group: str,
498
include_inactive_users: bool = False,
499
start: int = 0, limit: int = 50) -> List[dict]:
500
"""
501
Get group members.
502
503
Parameters:
504
- group: Group name
505
- include_inactive_users: Include inactive users
506
- start: Starting index
507
- limit: Maximum results
508
509
Returns:
510
List[dict]: Group member list
511
"""
512
```
513
514
### Filter Management
515
516
```python { .api }
517
def create_filter(self, name: str, jql: str,
518
description: Optional[str] = None,
519
favourite: bool = False) -> T_resp_json:
520
"""
521
Create saved filter.
522
523
Parameters:
524
- name: Filter name
525
- jql: JQL query
526
- description: Filter description
527
- favourite: Mark as favourite
528
529
Returns:
530
dict: Created filter data
531
"""
532
533
def get_filter(self, filter_id: T_id) -> T_resp_json:
534
"""
535
Get filter details.
536
537
Parameters:
538
- filter_id: Filter ID
539
540
Returns:
541
dict: Filter information including JQL and sharing
542
"""
543
544
def update_filter(self, filter_id: T_id, jql: str, **kwargs) -> T_resp_json:
545
"""
546
Update filter.
547
548
Parameters:
549
- filter_id: Filter ID
550
- jql: New JQL query
551
- **kwargs: Additional properties (name, description, favourite)
552
553
Returns:
554
dict: Updated filter data
555
"""
556
```
557
558
### Agile and Boards
559
560
```python { .api }
561
def get_all_agile_boards(self, board_name: Optional[str] = None,
562
project_key: Optional[str] = None,
563
board_type: Optional[str] = None,
564
start: int = 0, limit: int = 50) -> T_resp_json:
565
"""
566
Get agile boards.
567
568
Parameters:
569
- board_name: Filter by board name
570
- project_key: Filter by project
571
- board_type: Board type (scrum, kanban)
572
- start: Starting index
573
- limit: Maximum results
574
575
Returns:
576
dict: Board list with pagination
577
"""
578
579
def create_agile_board(self, name: str, type: str, filter_id: T_id,
580
location: Optional[dict] = None) -> T_resp_json:
581
"""
582
Create agile board.
583
584
Parameters:
585
- name: Board name
586
- type: Board type (scrum, kanban)
587
- filter_id: Saved filter ID for board query
588
- location: Board location configuration
589
590
Returns:
591
dict: Created board data
592
"""
593
594
def create_sprint(self, name: str, board_id: T_id,
595
start_date: Optional[str] = None,
596
end_date: Optional[str] = None,
597
goal: Optional[str] = None) -> T_resp_json:
598
"""
599
Create sprint.
600
601
Parameters:
602
- name: Sprint name
603
- board_id: Board ID
604
- start_date: Start date (ISO format)
605
- end_date: End date (ISO format)
606
- goal: Sprint goal
607
608
Returns:
609
dict: Created sprint data
610
"""
611
612
def add_issues_to_sprint(self, sprint_id: T_id, issues: List[str]) -> T_resp_json:
613
"""
614
Add issues to sprint.
615
616
Parameters:
617
- sprint_id: Sprint ID
618
- issues: List of issue keys
619
620
Returns:
621
dict: Operation result
622
"""
623
624
def get_all_sprints_from_board(self, board_id: T_id) -> T_resp_json:
625
"""
626
Get all sprints from board.
627
628
Parameters:
629
- board_id: Board ID
630
631
Returns:
632
dict: Sprints list with status and dates
633
"""
634
635
def get_sprint_issues(self, sprint_id: T_id, start: int = 0,
636
limit: int = 50) -> T_resp_json:
637
"""
638
Get issues in sprint.
639
640
Parameters:
641
- sprint_id: Sprint ID
642
- start: Starting index
643
- limit: Maximum results
644
645
Returns:
646
dict: Sprint issues with status
647
"""
648
649
def move_issues_to_backlog(self, issue_keys: List[str]) -> T_resp_json:
650
"""
651
Move issues to backlog.
652
653
Parameters:
654
- issue_keys: List of issue keys to move
655
656
Returns:
657
dict: Operation result
658
"""
659
660
def get_epics(self, board_id: T_id, start: int = 0,
661
limit: int = 50) -> T_resp_json:
662
"""
663
Get epics from board.
664
665
Parameters:
666
- board_id: Board ID
667
- start: Starting index
668
- limit: Maximum results
669
670
Returns:
671
dict: Epics list
672
"""
673
674
def epic_issues(self, epic: str, fields: str = "*all",
675
expand: Optional[str] = None) -> T_resp_json:
676
"""
677
Get issues in epic.
678
679
Parameters:
680
- epic: Epic key
681
- fields: Fields to retrieve
682
- expand: Additional data to expand
683
684
Returns:
685
dict: Epic issues
686
"""
687
```
688
689
### Tempo Integration
690
691
Comprehensive Tempo time tracking, team management, and account administration for advanced project management and resource planning.
692
693
```python { .api }
694
def tempo_timesheets_get_worklogs(self, from_date: str, to_date: str,
695
username: Optional[str] = None,
696
project_key: Optional[str] = None) -> T_resp_json:
697
"""
698
Get worklogs from Tempo.
699
700
Parameters:
701
- from_date: Start date (YYYY-MM-DD format)
702
- to_date: End date (YYYY-MM-DD format)
703
- username: Filter by username
704
- project_key: Filter by project
705
706
Returns:
707
dict: Worklog entries with time tracking data
708
"""
709
710
def tempo_timesheets_write_worklog(self, issue: str, time_sec: int,
711
start_date: str, username: str,
712
comment: Optional[str] = None) -> T_resp_json:
713
"""
714
Create Tempo worklog entry.
715
716
Parameters:
717
- issue: Issue key
718
- time_sec: Time spent in seconds
719
- start_date: Work date (YYYY-MM-DD format)
720
- username: Username for worklog
721
- comment: Optional comment
722
723
Returns:
724
dict: Created worklog data
725
"""
726
727
def tempo_teams_get_all_teams(self, expand: Optional[str] = None) -> T_resp_json:
728
"""
729
Get all Tempo teams.
730
731
Parameters:
732
- expand: Additional data to expand
733
734
Returns:
735
dict: Teams list with members and configuration
736
"""
737
738
def tempo_teams_add_member(self, team_id: T_id, member_key: str) -> T_resp_json:
739
"""
740
Add member to Tempo team.
741
742
Parameters:
743
- team_id: Team ID
744
- member_key: User key to add
745
746
Returns:
747
dict: Updated team membership
748
"""
749
750
def tempo_account_get_accounts(self, skip: int = 0, limit: int = 50) -> T_resp_json:
751
"""
752
Get Tempo accounts for billing/project tracking.
753
754
Parameters:
755
- skip: Number of results to skip
756
- limit: Maximum results to return
757
758
Returns:
759
dict: Accounts list with billing information
760
"""
761
762
def tempo_account_add_account(self, data: dict) -> T_resp_json:
763
"""
764
Create Tempo account.
765
766
Parameters:
767
- data: Account data (key, name, customer_id, etc.)
768
769
Returns:
770
dict: Created account data
771
"""
772
773
def tempo_holiday_get_schemes(self) -> T_resp_json:
774
"""
775
Get holiday schemes for time tracking.
776
777
Returns:
778
dict: Holiday schemes with dates and rules
779
"""
780
781
def tempo_timesheets_get_team_utilization(self, team_id: T_id,
782
from_date: str, to_date: str) -> T_resp_json:
783
"""
784
Get team utilization metrics.
785
786
Parameters:
787
- team_id: Team ID
788
- from_date: Start date
789
- to_date: End date
790
791
Returns:
792
dict: Team utilization statistics and capacity data
793
"""
794
```
795
796
### Administration
797
798
```python { .api }
799
def get_server_info(self, do_health_check: bool = False) -> T_resp_json:
800
"""
801
Get server information.
802
803
Parameters:
804
- do_health_check: Include health check data
805
806
Returns:
807
dict: Server version, build info, health status
808
"""
809
810
def health_check(self) -> T_resp_json:
811
"""
812
Perform health check.
813
814
Returns:
815
dict: System health status
816
"""
817
818
def reindex(self, comments: bool = True, change_history: bool = True,
819
worklogs: bool = True,
820
indexing_type: str = "BACKGROUND_PREFERRED") -> T_resp_json:
821
"""
822
Reindex Jira.
823
824
Parameters:
825
- comments: Reindex comments
826
- change_history: Reindex change history
827
- worklogs: Reindex worklogs
828
- indexing_type: Indexing strategy
829
830
Returns:
831
dict: Reindex status
832
"""
833
834
def get_all_fields(self) -> List[dict]:
835
"""
836
Get all fields (system and custom).
837
838
Returns:
839
List[dict]: Field definitions with types and options
840
"""
841
842
def create_custom_field(self, name: str, type: str,
843
search_key: Optional[str] = None,
844
description: Optional[str] = None) -> T_resp_json:
845
"""
846
Create custom field.
847
848
Parameters:
849
- name: Field name
850
- type: Field type (text, number, select, etc.)
851
- search_key: Searcher key
852
- description: Field description
853
854
Returns:
855
dict: Created field data
856
"""
857
```
858
859
## Usage Examples
860
861
### Basic Issue Operations
862
863
```python
864
from atlassian import Jira
865
866
jira = Jira(
867
url="https://your-domain.atlassian.net",
868
username="email@example.com",
869
password="api-token"
870
)
871
872
# Create issue
873
issue_data = {
874
"project": {"key": "PROJ"},
875
"summary": "New feature request",
876
"description": "Detailed description of the feature",
877
"issuetype": {"name": "Story"},
878
}
879
new_issue = jira.create_issue(fields=issue_data)
880
881
# Get issue
882
issue = jira.get_issue("PROJ-123", fields="summary,status,assignee")
883
884
# Update issue
885
jira.update_issue_field("PROJ-123", {
886
"summary": "Updated summary",
887
"description": "Updated description"
888
})
889
890
# Transition issue
891
jira.set_issue_status("PROJ-123", "In Progress")
892
```
893
894
### Search and Export
895
896
```python
897
# JQL search
898
results = jira.jql("project = PROJ AND status = 'To Do'", limit=50)
899
issues = results["issues"]
900
901
# Get all issues (handles pagination)
902
all_issues = jira.jql_get_list_of_tickets(
903
"project = PROJ ORDER BY created DESC"
904
)
905
906
# Export to CSV
907
csv_content = jira.csv(
908
"project = PROJ AND status = Done",
909
limit=1000,
910
all_fields=True
911
)
912
```
913
914
### User and Project Management
915
916
```python
917
# User operations
918
user = jira.user(account_id="account-id-123")
919
users = jira.user_find_by_user_string(query="john@example.com")
920
921
# Project operations
922
projects = jira.projects()
923
project = jira.get_project("PROJ", expand="description,lead,components")
924
components = jira.get_project_components("PROJ")
925
```
926
927
### Advanced Features
928
929
```python
930
# Bulk operations
931
issues_to_create = [
932
{"project": {"key": "PROJ"}, "summary": "Task 1", "issuetype": {"name": "Task"}},
933
{"project": {"key": "PROJ"}, "summary": "Task 2", "issuetype": {"name": "Task"}},
934
]
935
bulk_result = jira.create_issues(issues_to_create)
936
937
# Agile operations
938
boards = jira.get_all_agile_boards(project_key="PROJ")
939
sprint = jira.create_sprint("Sprint 1", board_id=123)
940
jira.add_issues_to_sprint(sprint["id"], ["PROJ-1", "PROJ-2"])
941
942
# Filter management
943
filter_data = jira.create_filter(
944
"My Issues",
945
"assignee = currentUser() AND resolution = Unresolved"
946
)
947
```
948
949
## Error Handling
950
951
```python
952
from atlassian.errors import ApiNotFoundError, ApiPermissionError
953
954
try:
955
issue = jira.get_issue("INVALID-123")
956
except ApiNotFoundError:
957
print("Issue not found")
958
except ApiPermissionError:
959
print("Permission denied")
960
```
961
962
## Types
963
964
```python { .api }
965
from atlassian.typehints import T_id, T_resp_json
966
from typing import List, Dict, Optional, Union
967
968
# Common parameter types
969
IssueKey = str
970
ProjectKey = str
971
JQLQuery = str
972
IssueFields = Dict[str, Any]
973
UserIdentifier = Union[str, dict] # username/account_id or user object
974
IssueUpdate = Dict[str, List[dict]] # Update operations
975
```