🐞
Issues
This resource represents Jira issues

Overview

Need help working with issues? In Jira Software, issues help you manage code, estimate workload, and keep track of your team. On this page, you'll find a quick overview of everything that you can do with an issue.
Issue View
What is an issue? | Jira Software Cloud | Atlassian Support
Atlassian Support
This resource represents Jira issues. Use it to:
  • create or edit issues, individually or in bulk.
  • retrieve metadata about the options for creating or editing issues.
  • delete an issue.
  • assign a user to an issue.
  • get issue changelogs.
  • send notifications about an issue.
  • get details of the transitions available for an issue.
  • transition an issue.

Custom Fields

In the Jira REST API, custom fields are uniquely identified by the field ID, as the display names are not unique within a Jira instance. For example, you could have two fields named "Escalation date", one with an ID of "12221" and one with an ID of "12222". A custom field is actually referenced by customfield\_ + the field ID, rather than just the field ID.
Note: For example, the "Story points" custom field with ID = "10000" is referenced as customfield_10000 for REST calls. You can get this reference identifier by requesting the create metadata for the issue type.
We support the following custom field types.
  • Group(s) Picker
  • User(s) Picker
  • URL's
  • Text
  • Number
  • Date
  • DateTime
  • Select
  • MultiSelect
  • RadioButton
  • CheckBox
  • Cascading Multiselect
The examples below show how to set the values for different types of custom fields in the input data.
1
// The CustomFields struct is used on the Create(s), Edit methods
2
var customFields = jira.CustomFields{}
3
​
4
//Groups Picker customfield
5
err = customFields.Groups("customfield_10052", []string{"jira-administrators", "jira-administrators-system"})
6
if err != nil {
7
log.Fatal(err)
8
}
9
​
10
//Number customfield
11
err = customFields.Number("customfield_10043", 1000.3232)
12
if err != nil {
13
log.Fatal(err)
14
}
15
​
16
//User picker customfield
17
err = customFields.User("customfield_10044", "92c50fd7-4336-4761-abbc-bdd31ecbc380")
18
if err != nil {
19
log.Fatal(err)
20
}
21
​
22
//Users picker customfield
23
err = customFields.Users("customfield_10045", []string{"727b5300-78dc-4b92-961b-082676dea3f2", "697c6d52-993d-4ae3-84b2-1842bdb5b7c0"})
24
if err != nil {
25
log.Fatal(err)
26
}
27
​
28
//Group picker customfield
29
err = customFields.Group("customfield_10046", "scrum-masters")
30
if err != nil {
31
log.Fatal(err)
32
}
33
​
34
//URL customfield
35
err = customFields.URL("customfield_10047", "https://docs.go-atlassian.io/jira-software-cloud/issues")
36
if err != nil {
37
log.Fatal(err)
38
}
39
​
40
//Text customfield
41
err = customFields.Text("customfield_10048", "sample text")
42
if err != nil {
43
log.Fatal(err)
44
}
45
​
46
//Date customfield
47
err = customFields.Date("customfield_10049", time.Now().AddDate(0, -1, 0))
48
if err != nil {
49
log.Fatal(err)
50
}
51
​
52
//DateTime customfield
53
err = customFields.DateTime("customfield_10050", time.Now())
54
if err != nil {
55
log.Fatal(err)
56
}
57
​
58
//Select customfield
59
err = customFields.Select("customfield_10051", "Option 1")
60
if err != nil {
61
log.Fatal(err)
62
}
63
​
64
//MultiSelect customfield
65
err = customFields.MultiSelect("customfield_10052", []string{"Option 1", "Option 2", "Option 3"})
66
if err != nil {
67
log.Fatal(err)
68
}
69
​
70
//RadioButton customfield
71
err = customFields.RadioButton("customfield_10053", "Option 1")
72
if err != nil {
73
log.Fatal(err)
74
}
75
​
76
//CheckBox customfield
77
err = customFields.CheckBox("customfield_10054", []string{"Option 1", "Option 2"})
78
if err != nil {
79
log.Fatal(err)
80
}
81
​
82
//Cascading customfield
83
err = customFields.Cascading("customfield_10056", "America", "Costa Rica")
84
if err != nil {
85
log.Fatal(err)
86
}
Copied!

Create Issue

Creates an issue or, where the option to create subtasks is enabled in Jira, a subtask
1
package main
2
​
3
import (
4
"context"
5
"github.com/ctreminiom/go-atlassian/jira/v2"
6
"github.com/ctreminiom/go-atlassian/pkg/infra/models"
7
"log"
8
"os"
9
)
10
​
11
func main() {
12
​
13
var (
14
host = os.Getenv("HOST")
15
mail = os.Getenv("MAIL")
16
token = os.Getenv("TOKEN")
17
)
18
​
19
atlassian, err := v2.New(nil, host)
20
if err != nil {
21
log.Fatal(err)
22
}
23
​
24
atlassian.Auth.SetBasicAuth(mail, token)
25
​
26
var payload = models.IssueSchemeV2{
27
Fields: &models.IssueFieldsSchemeV2{
28
Summary: "New summary test",
29
Project: &models.ProjectScheme{ID: "10000"},
30
IssueType: &models.IssueTypeScheme{Name: "Story"},
31
},
32
}
33
​
34
//CustomFields
35
var customFields = models.CustomFields{}
36
err = customFields.Groups("customfield_10052", []string{"jira-administrators", "jira-administrators-system"})
37
if err != nil {
38
log.Fatal(err)
39
}
40
​
41
err = customFields.Number("customfield_10043", 1000.3232)
42
if err != nil {
43
log.Fatal(err)
44
}
45
​
46
newIssue, response, err := atlassian.Issue.Create(context.Background(), &payload, &customFields)
47
if err != nil {
48
log.Fatal(err)
49
}
50
​
51
log.Println("HTTP Endpoint Used", response.Endpoint)
52
log.Printf("The new issue %v has been created with the ID %v", newIssue.Key, newIssue.ID)
53
}
54
​
Copied!

Bulk create issue

Creates issues and, where the option to create subtasks is enabled in Jira, subtasks.
1
package main
2
​
3
import (
4
"context"
5
"github.com/ctreminiom/go-atlassian/jira/v2"
6
"github.com/ctreminiom/go-atlassian/pkg/infra/models"
7
"log"
8
"os"
9
)
10
​
11
func main() {
12
​
13
var (
14
host = os.Getenv("HOST")
15
mail = os.Getenv("MAIL")
16
token = os.Getenv("TOKEN")
17
)
18
​
19
atlassian, err := v2.New(nil, host)
20
if err != nil {
21
return
22
}
23
​
24
atlassian.Auth.SetBasicAuth(mail, token)
25
​
26
//CustomFields
27
var customFields = models.CustomFields{}
28
err = customFields.Groups("customfield_10052", []string{"jira-administrators", "jira-administrators-system"})
29
if err != nil {
30
log.Fatal(err)
31
}
32
​
33
err = customFields.Number("customfield_10043", 1000.3232)
34
if err != nil {
35
log.Fatal(err)
36
}
37
​
38
var issue1 = models.IssueBulkSchemeV2{
39
Payload: &models.IssueSchemeV2{
40
Fields: &models.IssueFieldsSchemeV2{
41
Summary: "New summary test",
42
Project: &models.ProjectScheme{ID: "10000"},
43
IssueType: &models.IssueTypeScheme{Name: "Story"},
44
},
45
},
46
CustomFields: &customFields,
47
}
48
​
49
var issue2 = models.IssueBulkSchemeV2{
50
Payload: &models.IssueSchemeV2{
51
Fields: &models.IssueFieldsSchemeV2{
52
Summary: "New summary test",
53
Project: &models.ProjectScheme{ID: "10000"},
54
IssueType: &models.IssueTypeScheme{Name: "Story"},
55
},
56
},
57
CustomFields: &customFields,
58
}
59
​
60
var issue3 = models.IssueBulkSchemeV2{
61
Payload: &models.IssueSchemeV2{
62
Fields: &models.IssueFieldsSchemeV2{
63
Summary: "New summary test",
64
Project: &models.ProjectScheme{ID: "10000"},
65
IssueType: &models.IssueTypeScheme{Name: "Story"},
66
},
67
},
68
CustomFields: &customFields,
69
}
70
​
71
var payload []*models.IssueBulkSchemeV2
72
payload = append(payload, &issue1, &issue2, &issue3)
73
​
74
newIssues, response, err := atlassian.Issue.Creates(context.Background(), payload)
75
if err != nil {
76
log.Fatal(err)
77
}
78
​
79
log.Println("HTTP Endpoint Used", response.Endpoint)
80
​
81
for _, issue := range newIssues.Issues {
82
log.Printf(issue.Key)
83
}
84
​
85
for _, apiError := range newIssues.Errors {
86
log.Println(apiError.Status, apiError.Status)
87
}
88
}
Copied!

Get issue

Returns the details for an issue.
1
package main
2
​
3
import (
4
"context"
5
"github.com/ctreminiom/go-atlassian/jira/v2"
6
"log"
7
"os"
8
)
9
​
10
func main() {
11
​
12
var (
13
host = os.Getenv("HOST")
14
mail = os.Getenv("MAIL")
15
token = os.Getenv("TOKEN")
16
)
17
​
18
atlassian, err := v2.New(nil, host)
19
if err != nil {
20
return
21
}
22
​
23
atlassian.Auth.SetBasicAuth(mail, token)
24
​
25
issue, response, err := atlassian.Issue.Get(context.Background(), "KP-2", nil, []string{"transitions"})
26
if err != nil {
27
log.Fatal(err)
28
}
29
​
30
log.Println("HTTP Endpoint Used", response.Endpoint)
31
​
32
log.Println(issue.Key)
33
log.Println(issue.Fields.Reporter.AccountID)
34
​
35
for _, transition := range issue.Transitions {
36
log.Println(transition.Name, transition.ID, transition.To.ID, transition.HasScreen)
37
}
38
​
39
// Check if the issue contains sub-tasks
40
if issue.Fields.Subtasks != nil {
41
for _, subTask := range issue.Fields.Subtasks {
42
log.Println("Sub-Task: ", subTask.Key, subTask.Fields.Summary)
43
}
44
}
45
}
Copied!

Edit issue

Edits an issue. The edits to the issue's fields are defined using update and fields. There're two ways to edit an issue on Jira, implicit and explicit.

Simple update (implicit set via "fields")

The simple way to update an issue is to do a GET, update the values inside "fields", and then PUT back.
  • If you PUT back exactly what you GOT, then you are also sending "names", "self", "key", etc. These are ignored.
  • You do not need to send all the fields inside "fields". You can just send the fields you want to update. Absent fields are left unchanged. For example, to update the summary:
1
var payload = jira.IssueScheme{
2
Fields: &jira.IssueFieldsScheme{
3
Summary: "New summary test test",
4
},
5
}
Copied!
  • Some fields cannot be updated this way (for example, comments). Instead you must use explicit-verb updates (see below). You can tell if a field cannot be implicitly set by the fact it doesn't have a SET verb.
  • If you do send along such un-SET-able fields in this manner, they are ignored. This means that you can PUT what you GET, but not all of the document is taken into consideration.

Operation (verb) based update (explicit verb via "update")

Each field supports a set of operations (verbs) for mutating the field. You can retrieve the editable fields and the operations they support using the "editmeta" resource.
The editmeta endpoint is not mapped, yet refer to this ticket​
The basic operations are defined below (but custom fields can define their own).
The general shape of an update is field, array of verb-value pairs, for example:
1
//Issue Update Operations
2
var operations = &jira.UpdateOperations{}
3
​
4
err = operations.AddArrayOperation("custom_field_id", map[string]string{
5
"value": "verb",
6
"value": "verb",
7
"value": "verb",
8
"value": "verb",
9
})
10
​
11
if err != nil {
12
log.Fatal(err)
13
}
Copied!
  • SET: Sets the value of the field. The incoming value must be the same shape as the value of the field from a GET. For example, a string for "summary", and array of strings for "labels".
  • ADD: Adds an element to a field that is an array. The incoming value must be the same shape as the items of the array in a GET. For example, to add a label:
  • REMOVE: Removes an element from a field that is an array. The incoming value must be the same shape as the items of the array in a GET (although you can remove parts of the object that are not needed to uniquely identify the object).
  • EDIT: Edits an element in a field that is an array. The element is indexed/identified by the value itself (usually by id/name/key).
1
package main
2
​
3
import (
4
"context"
5
"github.com/ctreminiom/go-atlassian/jira/v2"
6
"github.com/ctreminiom/go-atlassian/pkg/infra/models"
7
"log"
8
"os"
9
)
10
​
11
func main() {
12
​
13
var (
14
host = os.Getenv("HOST")
15
mail = os.Getenv("MAIL")
16
token = os.Getenv("TOKEN")
17
)
18
​
19
atlassian, err := v2.New(nil, host)
20
if err != nil {
21
return
22
}
23
​
24
atlassian.Auth.SetBasicAuth(mail, token)
25
​
26
var payload = models.IssueSchemeV2{
27
Fields: &models.IssueFieldsSchemeV2{
28
// Summary: "New summary test",
29
},
30
}
31
​
32
//CustomFields
33
var customFields = models.CustomFields{}
34
err = customFields.Groups("customfield_10052", []string{"jira-administrators", "jira-administrators-system"})
35
if err != nil {
36
log.Fatal(err)
37
}
38
​
39
err = customFields.Number("customfield_10043", 9000)
40
if err != nil {
41
log.Fatal(err)
42
}
43
​
44
//Issue Update Operations
45
var operations = &models.UpdateOperations{}
46
​
47
err = operations.AddArrayOperation("labels", map[string]string{
48
"triaged": "remove",
49
"triaged-2": "remove",
50
"triaged-1": "remove",
51
"blocker": "remove",
52
})
53
​
54
if err != nil {
55
log.Fatal(err)
56
}
57
​
58
err = operations.AddStringOperation("summary", "set", "new summary using operation")
59
if err != nil {
60
log.Fatal(err)
61
}
62
​
63
response, err := atlassian.Issue.Update(context.Background(), "KP-2", false, &payload, &customFields, operations)
64
if err != nil {
65
log.Fatal(err)
66
}
67
​
68
log.Println("HTTP Endpoint Used", response.Endpoint)
69
}
70
​
Copied!

Delete issue

Deletes an issue.
1
package main
2
​
3
import (
4
"context"
5
"github.com/ctreminiom/go-atlassian/jira/v2"
6
"log"
7
"os"
8
)
9
​
10
func main() {
11
​
12
var (
13
host = os.Getenv("HOST")
14
mail = os.Getenv("MAIL")
15
token = os.Getenv("TOKEN")
16
)
17
​
18
atlassian, err := v2.New(nil, host)
19
if err != nil {
20
return
21
}
22
​
23
atlassian.Auth.SetBasicAuth(mail, token)
24
​
25
response, err := atlassian.Issue.Delete(context.Background(), "KP-6", false)
26
if err != nil {
27
log.Fatal(err)
28
}
29
​
30
log.Println("HTTP Endpoint Used", response.Endpoint)
31
}
Copied!

Assign issue

Assigns an issue to a user. Use this operation when the calling user does not have the Edit Issues permission but has the Assign issue permission for the project that the issue is in.
1
package main
2
​
3
import (
4
"context"
5
"github.com/ctreminiom/go-atlassian/jira/v2"
6
"log"
7
"os"
8
)
9
​
10
func main() {
11
​
12
var (
13
host = os.Getenv("HOST")
14
mail = os.Getenv("MAIL")
15
token = os.Getenv("TOKEN")
16
)
17
​
18
atlassian, err := v2.New(nil, host)
19
if err != nil {
20
return
21
}
22
​
23
atlassian.Auth.SetBasicAuth(mail, token)
24
​
25
response, err := atlassian.Issue.Assign(context.Background(), "KP-2", "bedc0e56-c9d1-4f5d-b380-cbced9125849")
26
if err != nil {
27
log.Fatal(err)
28
}
29
​
30
log.Println("HTTP Endpoint Used", response.Endpoint)
31
}
Copied!

Send notification for issue

Creates an email notification for an issue and adds it to the mail queue.
1
package main
2
​
3
import (
4
"context"
5
"github.com/ctreminiom/go-atlassian/jira/v2"
6
"github.com/ctreminiom/go-atlassian/pkg/infra/models"
7
"log"
8
"os"
9
)
10
​
11
func main() {
12
​
13
var (
14
host = os.Getenv("HOST")
15
mail = os.Getenv("MAIL")
16
token = os.Getenv("TOKEN")
17
)
18
​
19
atlassian, err := v2.New(nil, host)
20
if err != nil {
21
return
22
}
23
​
24
atlassian.Auth.SetBasicAuth(mail, token)
25
​
26
var userRecipients []*models.IssueNotifyUserScheme
27
userRecipients = append(userRecipients, &models.IssueNotifyUserScheme{AccountID: "87dde939-73be-465f-83c5-2217fb9dd9de"})
28
userRecipients = append(userRecipients, &models.IssueNotifyUserScheme{AccountID: "8abc0d5f-5eb9-48af-bd8d-b83451828a40"})
29
​
30
var groupsRecipients []*models.IssueNotifyGroupScheme
31
groupsRecipients = append(groupsRecipients, &models.IssueNotifyGroupScheme{Name: "jira-users"})
32
groupsRecipients = append(groupsRecipients, &models.IssueNotifyGroupScheme{Name: "scrum-masters"})
33
​
34
opts := &models.IssueNotifyOptionsScheme{
35
​
36
// The HTML body of the email notification for the issue.
37
HTMLBody: "The <strong>latest</strong> test results for this ticket are now available.",
38
​
39
// The subject of the email notification for the issue.
40
// If this is not specified, then the subject is set to the issue key and summary.
41
Subject: "SUBJECT EMAIL EXAMPLE",
42
​
43
// The plain text body of the email notification for the issue.
44
// TextBody: "lorem",
45
​
46
// The recipients of the email notification for the issue.
47
To: &models.IssueNotifyToScheme{
48
Reporter: true,
49
Assignee: true,
50
Watchers: true,
51
Voters: true,
52
Users: userRecipients,
53
Groups: groupsRecipients,
54
},
55
​
56
// Restricts the notifications to users with the specified permissions.
57
Restrict: nil,
58
}
59
​
60
response, err := atlassian.Issue.Notify(context.Background(), "KP-2", opts)
61
if err != nil {
62
log.Fatal(err)
63
}
64
​
65
log.Println("HTTP Endpoint Used", response.Endpoint)
66
}
Copied!

Get transitions

Returns either all transitions or a transition that can be performed by the user on an issue, based on the issue's status.
1
package main
2
​
3
import (
4
"context"
5
"github.com/ctreminiom/go-atlassian/jira/v2"
6
"log"
7
"os"
8
)
9
​
10
func main() {
11
​
12
var (
13
host = os.Getenv("HOST")
14
mail = os.Getenv("MAIL")
15
token = os.Getenv("TOKEN")
16
)
17
​
18
atlassian, err := v2.New(nil, host)
19
if err != nil {
20
return
21
}
22
​
23
atlassian.Auth.SetBasicAuth(mail, token)
24
​
25
transitions, response, err := atlassian.Issue.Transitions(context.Background(), "KP-2")
26
if err != nil {
27
log.Fatal(err)
28
}
29
​
30
log.Println("HTTP Endpoint Used", response.Endpoint)
31
​
32
for _, transition := range transitions.Transitions {
33
log.Println(transition.ID, transition.Name)
34
}
35
}
Copied!
πŸ§šβ€β™€οΈ Tips: You can extract the following struct tags
1
type IssueTransitionsScheme struct {
2
Expand string `json:"expand,omitempty"`
3
Transitions []*IssueTransitionScheme `json:"transitions,omitempty"`
4
}
5
​
6
type IssueTransitionScheme struct {
7
ID string `json:"id,omitempty"`
8
Name string `json:"name,omitempty"`
9
To *TransitionToScheme `json:"to,omitempty"`
10
HasScreen bool `json:"hasScreen,omitempty"`
11
IsGlobal bool `json:"isGlobal,omitempty"`
12
IsInitial bool `json:"isInitial,omitempty"`
13
IsAvailable bool `json:"isAvailable,omitempty"`
14
IsConditional bool `json:"isConditional,omitempty"`
15
IsLooped bool `json:"isLooped,omitempty"`
16
}
17
​
18
type TransitionToScheme struct {
19
Self string `json:"self,omitempty"`
20
Description string `json:"description,omitempty"`
21
IconURL string `json:"iconUrl,omitempty"`
22
Name string `json:"name,omitempty"`
23
ID string `json:"id,omitempty"`
24
StatusCategory *StatusCategoryScheme `json:"statusCategory,omitempty"`
25
}
26
​
27
type StatusCategoryScheme struct {
28
Self string `json:"self,omitempty"`
29
ID int `json:"id,omitempty"`
30
Key string `json:"key,omitempty"`
31
ColorName string `json:"colorName,omitempty"`
32
Name string `json:"name,omitempty"`
33
}
Copied!

Transition issue

Performs an issue transition.
1
package main
2
​
3
import (
4
"context"
5
"github.com/ctreminiom/go-atlassian/jira/v2"
6
"github.com/ctreminiom/go-atlassian/pkg/infra/models"
7
"log"
8
"os"
9
)
10
​
11
func main() {
12
​
13
var (
14
host = os.Getenv("HOST")
15
mail = os.Getenv("MAIL")
16
token = os.Getenv("TOKEN")
17
)
18
​
19
atlassian, err := v2.New(nil, host)
20
if err != nil {
21
return
22
}
23
​
24
atlassian.Auth.SetBasicAuth(mail, token)
25
​
26
var payload = &models.IssueSchemeV2{
27
Fields: &models.IssueFieldsSchemeV2{
28
Summary: "New summary test test",
29
},
30
}
31
​
32
//CustomFields
33
var customFields = &models.CustomFields{}
34
err = customFields.Groups("customfield_10052", []string{"jira-administrators", "jira-administrators-system"})
35
if err != nil {
36
log.Fatal(err)
37
}
38
​
39
//Issue Update Operations
40
var operations = &models.UpdateOperations{}
41
​
42
err = operations.AddArrayOperation("labels", map[string]string{
43
"triaged": "add",
44
"triaged-2": "add",
45
"triaged-1": "add",
46
"blocker": "add",
47
})
48
​
49
if err != nil {
50
log.Fatal(err)
51
}
52
​
53
options := &models.IssueMoveOptionsV2{
54
Fields: payload,
55
Operations: operations,
56
CustomFields: customFields,
57
}
58
​
59
response, err := atlassian.Issue.Move(context.Background(), "KP-7", "41", options)
60
if err != nil {
61
log.Fatal(err)
62
}
63
​
64
log.Println("HTTP Endpoint Used", response.Endpoint)
65
}
Copied!