Repository: andygrunwald/go-jira
Pull request #509 (2022-09-11 16:08:22)

Changes to both (OnPremise and Cloud) clients to make it more future proof and configurable
# What this PR does

* [Moved all Sub-Services to a common service struct](https://github.com/andygrunwald/go-jira/commit/0507afda1ae3a736a36a74d38a95c2fcc03db63f)
* [NewClient: Change order of arguments, baseUrl first, http client second](https://github.com/andygrunwald/go-jira/commit/c5f70e9d753b1b6f86c315b868d1a2bb81341c6c)
* [Client: Add default user agent to identify the client](https://github.com/andygrunwald/go-jira/commit/bfdd992708ea3198e7a80e6778fc149799488959)
* [addOptions: Rename opt to opts, because those can be multiple](https://github.com/andygrunwald/go-jira/commit/5560252e569bacfc6a6d9a1b6d8d8f2c799d8fc4)
* [Delete NewRawRequestWithContext, because NewRawRequest requires now a context](https://github.com/andygrunwald/go-jira/commit/1215c46bec55eff84c91faa470ef0da3f4c18dda) 
* [Add Client() method to get the HTTP client](https://github.com/andygrunwald/go-jira/commit/6411dc3b1ef543a1b4f8752a23f7db68bb291201)
* [Add Changelog entries and Migration entries](https://github.com/andygrunwald/go-jira/commit/28e5b4e00ea36b325cd026432980467bab92ca96)
* [Delete NewRequestWithContext, because NewRequest requires now a context](https://github.com/andygrunwald/go-jira/commit/2b8cc87fb11a045a280a8af036deabe29ae5796f)

# TODO

- [x] Playback changes into onpremise client

# Notes

https://github.com/andygrunwald/go-jira/pull/504 was the previous PR which has a few merge conflicts. Hence, this one is the new one.

Related https://github.com/andygrunwald/go-jira/issues/473


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 35dd84e..7f90595 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -37,6 +37,91 @@ import (
 )
 ```
 
+#### Init a new client
+
+The order of arguments in the `jira.NewClient` has changed:
+
+1. The base URL of your JIRA instance
+2. A HTTP client (optional)
+
+Before:
+
+```go
+jira.NewClient(nil, "https://issues.apache.org/jira/")
+```
+
+After:
+
+```go
+jira.NewClient("https://issues.apache.org/jira/", nil)
+```
+
+#### User Agent
+
+The client will identify itself via a UserAgent `go-jira/2.0.0`.
+
+#### `NewRawRequestWithContext` removed, `NewRawRequest` requires `context`
+
+The function `client.NewRawRequestWithContext()` has been removed.
+`client.NewRawRequest()` accepts now a context as the first argument.
+This is a drop in replacement.
+
+Before:
+
+```go
+client.NewRawRequestWithContext(context.Background(), "GET", .....)
+```
+
+After:
+
+```go
+client.NewRawRequest(context.Background(), "GET", .....)
+```
+
+For people who used `jira.NewRawRequest()`: You need to pass a context as the first argument.
+Like
+
+```go
+client.NewRawRequest(context.Background(), "GET", .....)
+```
+
+#### `NewRequestWithContext` removed, `NewRequest` requires `context`
+
+The function `client.NewRequestWithContext()` has been removed.
+`client.NewRequest()` accepts now a context as the first argument.
+This is a drop in replacement.
+
+Before:
+
+```go
+client.NewRequestWithContext(context.Background(), "GET", .....)
+```
+
+After:
+
+```go
+client.NewRequest(context.Background(), "GET", .....)
+```
+
+For people who used `jira.NewRequest()`: You need to pass a context as the first argument.
+Like
+
+```go
+client.NewRequest(context.Background(), "GET", .....)
+```
+
+### Breaking changes
+
+* Jira On-Premise and Jira Cloud have now different clients, because the API differs
+* `client.NewRawRequestWithContext()` has been removed in favor of `client.NewRawRequest()`, which requires now a context as first argument
+* `client.NewRequestWithContext()` has been removed in favor of `client.NewRequest()`, which requires now a context as first argument
+
+### Features
+
+* UserAgent: Client HTTP calls are now identifable via a User Agent. This user agent can be configured (default: `go-jira/2.0.0`)
+* The underlying used HTTP client for API calls can be retrieved via `client.Client()`
+
+
 ### Changes
 
 ## [1.13.0](https://github.com/andygrunwald/go-jira/compare/v1.11.1...v1.13.0) (2020-10-25)
diff --git a/cloud/auth_transport_cookie.go b/cloud/auth_transport_cookie.go
index bd6e4b4..ddde946 100644
--- a/cloud/auth_transport_cookie.go
+++ b/cloud/auth_transport_cookie.go
@@ -89,6 +89,7 @@ func (t *CookieAuthTransport) buildAuthRequest() (*http.Request, error) {
 	b := new(bytes.Buffer)
 	json.NewEncoder(b).Encode(body)
 
+	// TODO Use a context here
 	req, err := http.NewRequest("POST", t.AuthURL, b)
 	if err != nil {
 		return nil, err
diff --git a/cloud/authentication.go b/cloud/authentication.go
index fc0828c..caa7c31 100644
--- a/cloud/authentication.go
+++ b/cloud/authentication.go
@@ -67,7 +67,7 @@ func (s *AuthenticationService) AcquireSessionCookieWithContext(ctx context.Cont
 		password,
 	}
 
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, body)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndpoint, body)
 	if err != nil {
 		return false, err
 	}
@@ -133,7 +133,7 @@ func (s *AuthenticationService) LogoutWithContext(ctx context.Context) error {
 	}
 
 	apiEndpoint := "rest/auth/1/session"
-	req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "DELETE", apiEndpoint, nil)
 	if err != nil {
 		return fmt.Errorf("creating the request to log the user out failed : %s", err)
 	}
@@ -174,7 +174,7 @@ func (s *AuthenticationService) GetCurrentUserWithContext(ctx context.Context) (
 	}
 
 	apiEndpoint := "rest/auth/1/session"
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, fmt.Errorf("could not create request for getting user info : %s", err)
 	}
diff --git a/cloud/board.go b/cloud/board.go
index 25b4783..7ef0f15 100644
--- a/cloud/board.go
+++ b/cloud/board.go
@@ -10,9 +10,7 @@ import (
 // BoardService handles Agile Boards for the Jira instance / API.
 //
 // Jira API docs: https://docs.atlassian.com/jira-software/REST/server/
-type BoardService struct {
-	client *Client
-}
+type BoardService service
 
 // BoardsList reflects a list of agile boards
 type BoardsList struct {
@@ -136,7 +134,7 @@ func (s *BoardService) GetAllBoardsWithContext(ctx context.Context, opt *BoardLi
 	if err != nil {
 		return nil, nil, err
 	}
-	req, err := s.client.NewRequestWithContext(ctx, "GET", url, nil)
+	req, err := s.client.NewRequest(ctx, "GET", url, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -162,7 +160,7 @@ func (s *BoardService) GetAllBoards(opt *BoardListOptions) (*BoardsList, *Respon
 // Jira API docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board-getBoard
 func (s *BoardService) GetBoardWithContext(ctx context.Context, boardID int) (*Board, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/agile/1.0/board/%v", boardID)
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -192,7 +190,7 @@ func (s *BoardService) GetBoard(boardID int) (*Board, *Response, error) {
 // Jira API docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board-createBoard
 func (s *BoardService) CreateBoardWithContext(ctx context.Context, board *Board) (*Board, *Response, error) {
 	apiEndpoint := "rest/agile/1.0/board"
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, board)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndpoint, board)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -218,7 +216,7 @@ func (s *BoardService) CreateBoard(board *Board) (*Board, *Response, error) {
 // Caller must close resp.Body
 func (s *BoardService) DeleteBoardWithContext(ctx context.Context, boardID int) (*Board, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/agile/1.0/board/%v", boardID)
-	req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "DELETE", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -269,7 +267,7 @@ func (s *BoardService) GetAllSprintsWithOptionsWithContext(ctx context.Context,
 	if err != nil {
 		return nil, nil, err
 	}
-	req, err := s.client.NewRequestWithContext(ctx, "GET", url, nil)
+	req, err := s.client.NewRequest(ctx, "GET", url, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -293,7 +291,7 @@ func (s *BoardService) GetAllSprintsWithOptions(boardID int, options *GetAllSpri
 func (s *BoardService) GetBoardConfigurationWithContext(ctx context.Context, boardID int) (*BoardConfiguration, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/agile/1.0/board/%d/configuration", boardID)
 
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 
 	if err != nil {
 		return nil, nil, err
diff --git a/cloud/component.go b/cloud/component.go
index 51a36b6..ad18e43 100644
--- a/cloud/component.go
+++ b/cloud/component.go
@@ -4,9 +4,7 @@ import "context"
 
 // ComponentService handles components for the Jira instance / API.//
 // Jira API docs: https://docs.atlassian.com/software/jira/docs/api/REST/7.10.1/#api/2/component
-type ComponentService struct {
-	client *Client
-}
+type ComponentService service
 
 // CreateComponentOptions are passed to the ComponentService.Create function to create a new Jira component
 type CreateComponentOptions struct {
@@ -23,7 +21,7 @@ type CreateComponentOptions struct {
 // CreateWithContext creates a new Jira component based on the given options.
 func (s *ComponentService) CreateWithContext(ctx context.Context, options *CreateComponentOptions) (*ProjectComponent, *Response, error) {
 	apiEndpoint := "rest/api/2/component"
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, options)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndpoint, options)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/cloud/customer.go b/cloud/customer.go
index fb43ff9..095c81e 100644
--- a/cloud/customer.go
+++ b/cloud/customer.go
@@ -6,9 +6,7 @@ import (
 )
 
 // CustomerService handles ServiceDesk customers for the Jira instance / API.
-type CustomerService struct {
-	client *Client
-}
+type CustomerService service
 
 // Customer represents a ServiceDesk customer.
 type Customer struct {
@@ -52,7 +50,7 @@ func (c *CustomerService) CreateWithContext(ctx context.Context, email, displayN
 		DisplayName: displayName,
 	}
 
-	req, err := c.client.NewRequestWithContext(ctx, http.MethodPost, apiEndpoint, payload)
+	req, err := c.client.NewRequest(ctx, http.MethodPost, apiEndpoint, payload)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/cloud/examples/addlabel/main.go b/cloud/examples/addlabel/main.go
index d7dc3fa..ca5a6f7 100644
--- a/cloud/examples/addlabel/main.go
+++ b/cloud/examples/addlabel/main.go
@@ -38,7 +38,7 @@ func main() {
 		Password: strings.TrimSpace(password),
 	}
 
-	client, err := jira.NewClient(tp.Client(), strings.TrimSpace(jiraURL))
+	client, err := jira.NewClient(strings.TrimSpace(jiraURL), tp.Client())
 	if err != nil {
 		fmt.Printf("\nerror: %v\n", err)
 		return
diff --git a/cloud/examples/basicauth/main.go b/cloud/examples/basicauth/main.go
index c9d6eb0..7221d87 100644
--- a/cloud/examples/basicauth/main.go
+++ b/cloud/examples/basicauth/main.go
@@ -30,7 +30,7 @@ func main() {
 		Password: strings.TrimSpace(password),
 	}
 
-	client, err := jira.NewClient(tp.Client(), strings.TrimSpace(jiraURL))
+	client, err := jira.NewClient(strings.TrimSpace(jiraURL), tp.Client())
 	if err != nil {
 		fmt.Printf("\nerror: %v\n", err)
 		return
diff --git a/cloud/examples/create/main.go b/cloud/examples/create/main.go
index b803de0..2dc3837 100644
--- a/cloud/examples/create/main.go
+++ b/cloud/examples/create/main.go
@@ -29,7 +29,7 @@ func main() {
 		Password: strings.TrimSpace(password),
 	}
 
-	client, err := jira.NewClient(tp.Client(), strings.TrimSpace(jiraURL))
+	client, err := jira.NewClient(strings.TrimSpace(jiraURL), tp.Client())
 	if err != nil {
 		fmt.Printf("\nerror: %v\n", err)
 		return
diff --git a/cloud/examples/createwithcustomfields/main.go b/cloud/examples/createwithcustomfields/main.go
index 8ebe424..68b32b5 100644
--- a/cloud/examples/createwithcustomfields/main.go
+++ b/cloud/examples/createwithcustomfields/main.go
@@ -36,7 +36,7 @@ func main() {
 		Password: strings.TrimSpace(password),
 	}
 
-	client, err := jira.NewClient(tp.Client(), strings.TrimSpace(jiraURL))
+	client, err := jira.NewClient(strings.TrimSpace(jiraURL), tp.Client())
 	if err != nil {
 		fmt.Printf("\nerror: %v\n", err)
 		os.Exit(1)
diff --git a/cloud/examples/do/main.go b/cloud/examples/do/main.go
index b13adc2..eee2747 100644
--- a/cloud/examples/do/main.go
+++ b/cloud/examples/do/main.go
@@ -1,14 +1,15 @@
 package main
 
 import (
+	"context"
 	"fmt"
 
 	jira "github.com/andygrunwald/go-jira/cloud"
 )
 
 func main() {
-	jiraClient, _ := jira.NewClient(nil, "https://jira.atlassian.com/")
-	req, _ := jiraClient.NewRequest("GET", "/rest/api/2/project", nil)
+	jiraClient, _ := jira.NewClient("https://jira.atlassian.com/", nil)
+	req, _ := jiraClient.NewRequest(context.Background(), "GET", "/rest/api/2/project", nil)
 
 	projects := new([]jira.Project)
 	res, err := jiraClient.Do(req, projects)
diff --git a/cloud/examples/ignorecerts/main.go b/cloud/examples/ignorecerts/main.go
index e5325bc..525cd51 100644
--- a/cloud/examples/ignorecerts/main.go
+++ b/cloud/examples/ignorecerts/main.go
@@ -14,7 +14,7 @@ func main() {
 	}
 	client := &http.Client{Transport: tr}
 
-	jiraClient, _ := jira.NewClient(client, "https://issues.apache.org/jira/")
+	jiraClient, _ := jira.NewClient("https://issues.apache.org/jira/", client)
 	issue, _, _ := jiraClient.Issue.Get("MESOS-3325", nil)
 
 	fmt.Printf("%s: %+v\n", issue.Key, issue.Fields.Summary)
diff --git a/cloud/examples/jql/main.go b/cloud/examples/jql/main.go
index de980db..ce3ba45 100644
--- a/cloud/examples/jql/main.go
+++ b/cloud/examples/jql/main.go
@@ -7,7 +7,7 @@ import (
 )
 
 func main() {
-	jiraClient, _ := jira.NewClient(nil, "https://issues.apache.org/jira/")
+	jiraClient, _ := jira.NewClient("https://issues.apache.org/jira/", nil)
 
 	// Running JQL query
 
diff --git a/cloud/examples/newclient/main.go b/cloud/examples/newclient/main.go
index 9b259d8..c03461d 100644
--- a/cloud/examples/newclient/main.go
+++ b/cloud/examples/newclient/main.go
@@ -7,7 +7,7 @@ import (
 )
 
 func main() {
-	jiraClient, _ := jira.NewClient(nil, "https://issues.apache.org/jira/")
+	jiraClient, _ := jira.NewClient("https://issues.apache.org/jira/", nil)
 	issue, _, _ := jiraClient.Issue.Get("MESOS-3325", nil)
 
 	fmt.Printf("%s: %+v\n", issue.Key, issue.Fields.Summary)
diff --git a/cloud/examples/pagination/main.go b/cloud/examples/pagination/main.go
index 69a9857..07c52ee 100644
--- a/cloud/examples/pagination/main.go
+++ b/cloud/examples/pagination/main.go
@@ -38,7 +38,7 @@ func GetAllIssues(client *jira.Client, searchString string) ([]jira.Issue, error
 }
 
 func main() {
-	jiraClient, err := jira.NewClient(nil, "https://issues.apache.org/jira/")
+	jiraClient, err := jira.NewClient("https://issues.apache.org/jira/", nil)
 	if err != nil {
 		panic(err)
 	}
diff --git a/cloud/examples/renderedfields/main.go b/cloud/examples/renderedfields/main.go
index 92e8aa9..274498f 100644
--- a/cloud/examples/renderedfields/main.go
+++ b/cloud/examples/renderedfields/main.go
@@ -43,7 +43,7 @@ func main() {
 		tp = ba.Client()
 	}
 
-	client, err := jira.NewClient(tp, strings.TrimSpace(jiraURL))
+	client, err := jira.NewClient(strings.TrimSpace(jiraURL), tp)
 	if err != nil {
 		fmt.Printf("\nerror: %v\n", err)
 		return
diff --git a/cloud/examples/searchpages/main.go b/cloud/examples/searchpages/main.go
index f1dac69..51db937 100644
--- a/cloud/examples/searchpages/main.go
+++ b/cloud/examples/searchpages/main.go
@@ -34,7 +34,7 @@ func main() {
 		Password: strings.TrimSpace(password),
 	}
 
-	client, err := jira.NewClient(tp.Client(), strings.TrimSpace(jiraURL))
+	client, err := jira.NewClient(strings.TrimSpace(jiraURL), tp.Client())
 	if err != nil {
 		log.Fatal(err)
 	}
diff --git a/cloud/field.go b/cloud/field.go
index 4cc369a..c53a14f 100644
--- a/cloud/field.go
+++ b/cloud/field.go
@@ -5,9 +5,7 @@ import "context"
 // FieldService handles fields for the Jira instance / API.
 //
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-Field
-type FieldService struct {
-	client *Client
-}
+type FieldService service
 
 // Field represents a field of a Jira issue.
 type Field struct {
@@ -36,7 +34,7 @@ type FieldSchema struct {
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-field-get
 func (s *FieldService) GetListWithContext(ctx context.Context) ([]Field, *Response, error) {
 	apiEndpoint := "rest/api/2/field"
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/cloud/filter.go b/cloud/filter.go
index 6a0937c..a92e0e5 100644
--- a/cloud/filter.go
+++ b/cloud/filter.go
@@ -10,9 +10,7 @@ import (
 // FilterService handles fields for the Jira instance / API.
 //
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-group-Filter
-type FilterService struct {
-	client *Client
-}
+type FilterService service
 
 // Filter represents a Filter in Jira
 type Filter struct {
@@ -125,7 +123,7 @@ func (fs *FilterService) GetListWithContext(ctx context.Context) ([]*Filter, *Re
 
 	options := &GetQueryOptions{}
 	apiEndpoint := "rest/api/2/filter"
-	req, err := fs.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := fs.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -153,7 +151,7 @@ func (fs *FilterService) GetList() ([]*Filter, *Response, error) {
 // GetFavouriteListWithContext retrieves the user's favourited filters from Jira
 func (fs *FilterService) GetFavouriteListWithContext(ctx context.Context) ([]*Filter, *Response, error) {
 	apiEndpoint := "rest/api/2/filter/favourite"
-	req, err := fs.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := fs.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -174,7 +172,7 @@ func (fs *FilterService) GetFavouriteList() ([]*Filter, *Response, error) {
 // GetWithContext retrieves a single Filter from Jira
 func (fs *FilterService) GetWithContext(ctx context.Context, filterID int) (*Filter, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/filter/%d", filterID)
-	req, err := fs.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := fs.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -202,7 +200,7 @@ func (fs *FilterService) GetMyFiltersWithContext(ctx context.Context, opts *GetM
 	if err != nil {
 		return nil, nil, err
 	}
-	req, err := fs.client.NewRequestWithContext(ctx, "GET", url, nil)
+	req, err := fs.client.NewRequest(ctx, "GET", url, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -230,7 +228,7 @@ func (fs *FilterService) SearchWithContext(ctx context.Context, opt *FilterSearc
 	if err != nil {
 		return nil, nil, err
 	}
-	req, err := fs.client.NewRequestWithContext(ctx, "GET", url, nil)
+	req, err := fs.client.NewRequest(ctx, "GET", url, nil)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/cloud/group.go b/cloud/group.go
index 39fb295..29a70d1 100644
--- a/cloud/group.go
+++ b/cloud/group.go
@@ -9,9 +9,7 @@ import (
 // GroupService handles Groups for the Jira instance / API.
 //
 // Jira API docs: https://docs.atlassian.com/jira/REST/server/#api/2/group
-type GroupService struct {
-	client *Client
-}
+type GroupService service
 
 // groupMembersResult is only a small wrapper around the Group* methods
 // to be able to parse the results
@@ -68,7 +66,7 @@ type GroupSearchOptions struct {
 // WARNING: This API only returns the first page of group members
 func (s *GroupService) GetWithContext(ctx context.Context, name string) ([]GroupMember, *Response, error) {
 	apiEndpoint := fmt.Sprintf("/rest/api/2/group/member?groupname=%s", url.QueryEscape(name))
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -105,7 +103,7 @@ func (s *GroupService) GetWithOptionsWithContext(ctx context.Context, name strin
 			options.IncludeInactiveUsers,
 		)
 	}
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -132,7 +130,7 @@ func (s *GroupService) AddWithContext(ctx context.Context, groupname string, use
 		Name string `json:"name"`
 	}
 	user.Name = username
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, &user)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndpoint, &user)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -158,7 +156,7 @@ func (s *GroupService) Add(groupname string, username string) (*Group, *Response
 // Caller must close resp.Body
 func (s *GroupService) RemoveWithContext(ctx context.Context, groupname string, username string) (*Response, error) {
 	apiEndpoint := fmt.Sprintf("/rest/api/2/group/user?groupname=%s&username=%s", groupname, username)
-	req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "DELETE", apiEndpoint, nil)
 	if err != nil {
 		return nil, err
 	}
diff --git a/cloud/issue.go b/cloud/issue.go
index dc17183..e18d0db 100644
--- a/cloud/issue.go
+++ b/cloud/issue.go
@@ -27,9 +27,7 @@ const (
 // IssueService handles Issues for the Jira instance / API.
 //
 // Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/issue
-type IssueService struct {
-	client *Client
-}
+type IssueService service
 
 // UpdateQueryOptions specifies the optional parameters to the Edit issue
 type UpdateQueryOptions struct {
@@ -617,7 +615,7 @@ type RemoteLinkStatus struct {
 // Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/issue-getIssue
 func (s *IssueService) GetWithContext(ctx context.Context, issueID string, options *GetQueryOptions) (*Issue, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s", issueID)
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -651,7 +649,7 @@ func (s *IssueService) Get(issueID string, options *GetQueryOptions) (*Issue, *R
 // Caller must close resp.Body.
 func (s *IssueService) DownloadAttachmentWithContext(ctx context.Context, attachmentID string) (*Response, error) {
 	apiEndpoint := fmt.Sprintf("secure/attachment/%s/", attachmentID)
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, err
 	}
@@ -719,7 +717,7 @@ func (s *IssueService) PostAttachment(issueID string, r io.Reader, attachmentNam
 func (s *IssueService) DeleteAttachmentWithContext(ctx context.Context, attachmentID string) (*Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/attachment/%s", attachmentID)
 
-	req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "DELETE", apiEndpoint, nil)
 	if err != nil {
 		return nil, err
 	}
@@ -744,7 +742,7 @@ func (s *IssueService) DeleteAttachment(attachmentID string) (*Response, error)
 func (s *IssueService) DeleteLinkWithContext(ctx context.Context, linkID string) (*Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/issueLink/%s", linkID)
 
-	req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "DELETE", apiEndpoint, nil)
 	if err != nil {
 		return nil, err
 	}
@@ -771,7 +769,7 @@ func (s *IssueService) DeleteLink(linkID string) (*Response, error) {
 func (s *IssueService) GetWorklogsWithContext(ctx context.Context, issueID string, options ...func(*http.Request) error) (*Worklog, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/worklog", issueID)
 
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -816,7 +814,7 @@ func WithQueryOptions(options interface{}) func(*http.Request) error {
 // Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/issue-createIssues
 func (s *IssueService) CreateWithContext(ctx context.Context, issue *Issue) (*Issue, *Response, error) {
 	apiEndpoint := "rest/api/2/issue"
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, issue)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndpoint, issue)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -855,7 +853,7 @@ func (s *IssueService) UpdateWithOptionsWithContext(ctx context.Context, issue *
 	if err != nil {
 		return nil, nil, err
 	}
-	req, err := s.client.NewRequestWithContext(ctx, "PUT", url, issue)
+	req, err := s.client.NewRequest(ctx, "PUT", url, issue)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -895,7 +893,7 @@ func (s *IssueService) Update(issue *Issue) (*Issue, *Response, error) {
 // Caller must close resp.Body
 func (s *IssueService) UpdateIssueWithContext(ctx context.Context, jiraID string, data map[string]interface{}) (*Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/issue/%v", jiraID)
-	req, err := s.client.NewRequestWithContext(ctx, "PUT", apiEndpoint, data)
+	req, err := s.client.NewRequest(ctx, "PUT", apiEndpoint, data)
 	if err != nil {
 		return nil, err
 	}
@@ -920,7 +918,7 @@ func (s *IssueService) UpdateIssue(jiraID string, data map[string]interface{}) (
 // Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/issue-addComment
 func (s *IssueService) AddCommentWithContext(ctx context.Context, issueID string, comment *Comment) (*Comment, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/comment", issueID)
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, comment)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndpoint, comment)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -950,7 +948,7 @@ func (s *IssueService) UpdateCommentWithContext(ctx context.Context, issueID str
 		Body: comment.Body,
 	}
 	apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/comment/%s", issueID, comment.ID)
-	req, err := s.client.NewRequestWithContext(ctx, "PUT", apiEndpoint, reqBody)
+	req, err := s.client.NewRequest(ctx, "PUT", apiEndpoint, reqBody)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -974,7 +972,7 @@ func (s *IssueService) UpdateComment(issueID string, comment *Comment) (*Comment
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-api-3-issue-issueIdOrKey-comment-id-delete
 func (s *IssueService) DeleteCommentWithContext(ctx context.Context, issueID, commentID string) error {
 	apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/comment/%s", issueID, commentID)
-	req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "DELETE", apiEndpoint, nil)
 	if err != nil {
 		return err
 	}
@@ -999,7 +997,7 @@ func (s *IssueService) DeleteComment(issueID, commentID string) error {
 // https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-issue-issueIdOrKey-worklog-post
 func (s *IssueService) AddWorklogRecordWithContext(ctx context.Context, issueID string, record *WorklogRecord, options ...func(*http.Request) error) (*WorklogRecord, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/worklog", issueID)
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, record)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndpoint, record)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -1031,7 +1029,7 @@ func (s *IssueService) AddWorklogRecord(issueID string, record *WorklogRecord, o
 // https://docs.atlassian.com/software/jira/docs/api/REST/7.1.2/#api/2/issue-updateWorklog
 func (s *IssueService) UpdateWorklogRecordWithContext(ctx context.Context, issueID, worklogID string, record *WorklogRecord, options ...func(*http.Request) error) (*WorklogRecord, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/worklog/%s", issueID, worklogID)
-	req, err := s.client.NewRequestWithContext(ctx, "PUT", apiEndpoint, record)
+	req, err := s.client.NewRequest(ctx, "PUT", apiEndpoint, record)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -1064,7 +1062,7 @@ func (s *IssueService) UpdateWorklogRecord(issueID, worklogID string, record *Wo
 // Caller must close resp.Body
 func (s *IssueService) AddLinkWithContext(ctx context.Context, issueLink *IssueLink) (*Response, error) {
 	apiEndpoint := "rest/api/2/issueLink"
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, issueLink)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndpoint, issueLink)
 	if err != nil {
 		return nil, err
 	}
@@ -1115,7 +1113,7 @@ func (s *IssueService) SearchWithContext(ctx context.Context, jql string, option
 
 	u.RawQuery = uv.Encode()
 
-	req, err := s.client.NewRequestWithContext(ctx, "GET", u.String(), nil)
+	req, err := s.client.NewRequest(ctx, "GET", u.String(), nil)
 	if err != nil {
 		return []Issue{}, nil, err
 	}
@@ -1185,7 +1183,7 @@ func (s *IssueService) SearchPages(jql string, options *SearchOptions, f func(Is
 // GetCustomFieldsWithContext returns a map of customfield_* keys with string values
 func (s *IssueService) GetCustomFieldsWithContext(ctx context.Context, issueID string) (CustomFields, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s", issueID)
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -1230,7 +1228,7 @@ func (s *IssueService) GetCustomFields(issueID string) (CustomFields, *Response,
 // Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/issue-getTransitions
 func (s *IssueService) GetTransitionsWithContext(ctx context.Context, id string) ([]Transition, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/transitions?expand=transitions.fields", id)
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -1274,7 +1272,7 @@ func (s *IssueService) DoTransition(ticketID, transitionID string) (*Response, e
 func (s *IssueService) DoTransitionWithPayloadWithContext(ctx context.Context, ticketID, payload interface{}) (*Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/transitions", ticketID)
 
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, payload)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndpoint, payload)
 	if err != nil {
 		return nil, err
 	}
@@ -1384,7 +1382,7 @@ func (s *IssueService) DeleteWithContext(ctx context.Context, issueID string) (*
 	deletePayload["deleteSubtasks"] = "true"
 	content, _ := json.Marshal(deletePayload)
 
-	req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, content)
+	req, err := s.client.NewRequest(ctx, "DELETE", apiEndpoint, content)
 	if err != nil {
 		return nil, err
 	}
@@ -1405,7 +1403,7 @@ func (s *IssueService) Delete(issueID string) (*Response, error) {
 func (s *IssueService) GetWatchersWithContext(ctx context.Context, issueID string) (*[]User, *Response, error) {
 	watchesAPIEndpoint := fmt.Sprintf("rest/api/2/issue/%s/watchers", issueID)
 
-	req, err := s.client.NewRequestWithContext(ctx, "GET", watchesAPIEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", watchesAPIEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -1443,7 +1441,7 @@ func (s *IssueService) GetWatchers(issueID string) (*[]User, *Response, error) {
 func (s *IssueService) AddWatcherWithContext(ctx context.Context, issueID string, userName string) (*Response, error) {
 	apiEndPoint := fmt.Sprintf("rest/api/2/issue/%s/watchers", issueID)
 
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndPoint, userName)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndPoint, userName)
 	if err != nil {
 		return nil, err
 	}
@@ -1469,7 +1467,7 @@ func (s *IssueService) AddWatcher(issueID string, userName string) (*Response, e
 func (s *IssueService) RemoveWatcherWithContext(ctx context.Context, issueID string, userName string) (*Response, error) {
 	apiEndPoint := fmt.Sprintf("rest/api/2/issue/%s/watchers", issueID)
 
-	req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndPoint, userName)
+	req, err := s.client.NewRequest(ctx, "DELETE", apiEndPoint, userName)
 	if err != nil {
 		return nil, err
 	}
@@ -1495,7 +1493,7 @@ func (s *IssueService) RemoveWatcher(issueID string, userName string) (*Response
 func (s *IssueService) UpdateAssigneeWithContext(ctx context.Context, issueID string, assignee *User) (*Response, error) {
 	apiEndPoint := fmt.Sprintf("rest/api/2/issue/%s/assignee", issueID)
 
-	req, err := s.client.NewRequestWithContext(ctx, "PUT", apiEndPoint, assignee)
+	req, err := s.client.NewRequest(ctx, "PUT", apiEndPoint, assignee)
 	if err != nil {
 		return nil, err
 	}
@@ -1529,7 +1527,7 @@ func (c ChangelogHistory) CreatedTime() (time.Time, error) {
 // Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/issue-getRemoteIssueLinks
 func (s *IssueService) GetRemoteLinksWithContext(ctx context.Context, id string) (*[]RemoteLink, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/remotelink", id)
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -1553,7 +1551,7 @@ func (s *IssueService) GetRemoteLinks(id string) (*[]RemoteLink, *Response, erro
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issue-issueIdOrKey-remotelink-post
 func (s *IssueService) AddRemoteLinkWithContext(ctx context.Context, issueID string, remotelink *RemoteLink) (*RemoteLink, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/remotelink", issueID)
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, remotelink)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndpoint, remotelink)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -1578,7 +1576,7 @@ func (s *IssueService) AddRemoteLink(issueID string, remotelink *RemoteLink) (*R
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-issue-remote-links/#api-rest-api-2-issue-issueidorkey-remotelink-linkid-put
 func (s *IssueService) UpdateRemoteLinkWithContext(ctx context.Context, issueID string, linkID int, remotelink *RemoteLink) (*Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/remotelink/%d", issueID, linkID)
-	req, err := s.client.NewRequestWithContext(ctx, "PUT", apiEndpoint, remotelink)
+	req, err := s.client.NewRequest(ctx, "PUT", apiEndpoint, remotelink)
 	if err != nil {
 		return nil, err
 	}
diff --git a/cloud/issuelinktype.go b/cloud/issuelinktype.go
index a65e88e..9d09eea 100644
--- a/cloud/issuelinktype.go
+++ b/cloud/issuelinktype.go
@@ -10,16 +10,14 @@ import (
 // IssueLinkTypeService handles issue link types for the Jira instance / API.
 //
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-group-Issue-link-types
-type IssueLinkTypeService struct {
-	client *Client
-}
+type IssueLinkTypeService service
 
 // GetListWithContext gets all of the issue link types from Jira.
 //
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issueLinkType-get
 func (s *IssueLinkTypeService) GetListWithContext(ctx context.Context) ([]IssueLinkType, *Response, error) {
 	apiEndpoint := "rest/api/2/issueLinkType"
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -42,7 +40,7 @@ func (s *IssueLinkTypeService) GetList() ([]IssueLinkType, *Response, error) {
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issueLinkType-issueLinkTypeId-get
 func (s *IssueLinkTypeService) GetWithContext(ctx context.Context, ID string) (*IssueLinkType, *Response, error) {
 	apiEndPoint := fmt.Sprintf("rest/api/2/issueLinkType/%s", ID)
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndPoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndPoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -65,7 +63,7 @@ func (s *IssueLinkTypeService) Get(ID string) (*IssueLinkType, *Response, error)
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issueLinkType-post
 func (s *IssueLinkTypeService) CreateWithContext(ctx context.Context, linkType *IssueLinkType) (*IssueLinkType, *Response, error) {
 	apiEndpoint := "/rest/api/2/issueLinkType"
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, linkType)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndpoint, linkType)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -101,7 +99,7 @@ func (s *IssueLinkTypeService) Create(linkType *IssueLinkType) (*IssueLinkType,
 // Caller must close resp.Body
 func (s *IssueLinkTypeService) UpdateWithContext(ctx context.Context, linkType *IssueLinkType) (*IssueLinkType, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/issueLinkType/%s", linkType.ID)
-	req, err := s.client.NewRequestWithContext(ctx, "PUT", apiEndpoint, linkType)
+	req, err := s.client.NewRequest(ctx, "PUT", apiEndpoint, linkType)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -125,7 +123,7 @@ func (s *IssueLinkTypeService) Update(linkType *IssueLinkType) (*IssueLinkType,
 // Caller must close resp.Body
 func (s *IssueLinkTypeService) DeleteWithContext(ctx context.Context, ID string) (*Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/issueLinkType/%s", ID)
-	req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "DELETE", apiEndpoint, nil)
 	if err != nil {
 		return nil, err
 	}
diff --git a/cloud/jira.go b/cloud/jira.go
index bd1894b..f4273b7 100644
--- a/cloud/jira.go
+++ b/cloud/jira.go
@@ -10,27 +10,37 @@ import (
 	"net/url"
 	"reflect"
 	"strings"
+	"sync"
 
 	"github.com/google/go-querystring/query"
 )
 
-// httpClient defines an interface for an http.Client implementation so that alternative
-// http Clients can be passed in for making requests
-type httpClient interface {
-	Do(request *http.Request) (response *http.Response, err error)
-}
+const (
+	ClientVersion = "2.0.0"
+
+	defaultUserAgent = "go-jira" + "/" + ClientVersion
+)
 
 // A Client manages communication with the Jira API.
 type Client struct {
-	// HTTP client used to communicate with the API.
-	client httpClient
+	clientMu sync.Mutex   // clientMu protects the client during calls that modify it.
+	client   *http.Client // HTTP client used to communicate with the API.
 
 	// Base URL for API requests.
-	baseURL *url.URL
+	// Should be set to a domain endpoint of the Jira instance.
+	// BaseURL should always be specified with a trailing slash.
+	BaseURL *url.URL
+
+	// User agent used when communicating with the Jira API.
+	UserAgent string
 
 	// Session storage if the user authenticates with a Session cookie
+	// TODO Needed in Cloud and/or onpremise?
 	session *Session
 
+	// Reuse a single struct instead of allocating one for each service on the heap.
+	common service
+
 	// Services used for talking to different parts of the Jira API.
 	Authentication   *AuthenticationService
 	Issue            *IssueService
@@ -56,62 +66,77 @@ type Client struct {
 	Request          *RequestService
 }
 
-// NewClient returns a new Jira API client.
-// If a nil httpClient is provided, http.DefaultClient will be used.
-// To use API methods which require authentication you can follow the preferred solution and
-// provide an http.Client that will perform the authentication for you with OAuth and HTTP Basic (such as that provided by the golang.org/x/oauth2 library).
-// As an alternative you can use Session Cookie based authentication provided by this package as well.
-// See https://docs.atlassian.com/jira/REST/latest/#authentication
+// service is the base structure to bundle API services
+// under a sub-struct.
+type service struct {
+	client *Client
+}
+
+// Client returns the http.Client used by this Jira client.
+func (c *Client) Client() *http.Client {
+	c.clientMu.Lock()
+	defer c.clientMu.Unlock()
+	clientCopy := *c.client
+	return &clientCopy
+}
+
+// NewClient returns a new Jira API client with provided base URL (often is your Jira hostname)
+// If a nil httpClient is provided, a new http.Client will be used.
+// To use API methods which require authentication, provide an http.Client that will perform the authentication for you (such as that provided by the golang.org/x/oauth2 library).
 // baseURL is the HTTP endpoint of your Jira instance and should always be specified with a trailing slash.
-func NewClient(httpClient httpClient, baseURL string) (*Client, error) {
+func NewClient(baseURL string, httpClient *http.Client) (*Client, error) {
 	if httpClient == nil {
-		httpClient = http.DefaultClient
-	}
-
-	// ensure the baseURL contains a trailing slash so that all paths are preserved in later calls
-	if !strings.HasSuffix(baseURL, "/") {
-		baseURL += "/"
+		httpClient = &http.Client{}
 	}
 
-	parsedBaseURL, err := url.Parse(baseURL)
+	baseEndpoint, err := url.Parse(baseURL)
 	if err != nil {
 		return nil, err
 	}
+	// ensure the baseURL contains a trailing slash so that all paths are preserved in later calls
+	if !strings.HasSuffix(baseEndpoint.Path, "/") {
+		baseEndpoint.Path += "/"
+	}
 
 	c := &Client{
-		client:  httpClient,
-		baseURL: parsedBaseURL,
+		client:    httpClient,
+		BaseURL:   baseEndpoint,
+		UserAgent: defaultUserAgent,
 	}
+	c.common.client = c
+
+	// TODO Check if the authentication service is still needed (because of the transports)
 	c.Authentication = &AuthenticationService{client: c}
-	c.Issue = &IssueService{client: c}
-	c.Project = &ProjectService{client: c}
-	c.Board = &BoardService{client: c}
-	c.Sprint = &SprintService{client: c}
-	c.User = &UserService{client: c}
-	c.Group = &GroupService{client: c}
-	c.Version = &VersionService{client: c}
-	c.Priority = &PriorityService{client: c}
-	c.Field = &FieldService{client: c}
-	c.Component = &ComponentService{client: c}
-	c.Resolution = &ResolutionService{client: c}
-	c.StatusCategory = &StatusCategoryService{client: c}
-	c.Filter = &FilterService{client: c}
-	c.Role = &RoleService{client: c}
-	c.PermissionScheme = &PermissionSchemeService{client: c}
-	c.Status = &StatusService{client: c}
-	c.IssueLinkType = &IssueLinkTypeService{client: c}
-	c.Organization = &OrganizationService{client: c}
-	c.ServiceDesk = &ServiceDeskService{client: c}
-	c.Customer = &CustomerService{client: c}
-	c.Request = &RequestService{client: c}
+	c.Issue = (*IssueService)(&c.common)
+	c.Project = (*ProjectService)(&c.common)
+	c.Board = (*BoardService)(&c.common)
+	c.Sprint = (*SprintService)(&c.common)
+	c.User = (*UserService)(&c.common)
+	c.Group = (*GroupService)(&c.common)
+	c.Version = (*VersionService)(&c.common)
+	c.Priority = (*PriorityService)(&c.common)
+	c.Field = (*FieldService)(&c.common)
+	c.Component = (*ComponentService)(&c.common)
+	c.Resolution = (*ResolutionService)(&c.common)
+	c.StatusCategory = (*StatusCategoryService)(&c.common)
+	c.Filter = (*FilterService)(&c.common)
+	c.Role = (*RoleService)(&c.common)
+	c.PermissionScheme = (*PermissionSchemeService)(&c.common)
+	c.Status = (*StatusService)(&c.common)
+	c.IssueLinkType = (*IssueLinkTypeService)(&c.common)
+	c.Organization = (*OrganizationService)(&c.common)
+	c.ServiceDesk = (*ServiceDeskService)(&c.common)
+	c.Customer = (*CustomerService)(&c.common)
+	c.Request = (*RequestService)(&c.common)
 
 	return c, nil
 }
 
-// NewRawRequestWithContext creates an API request.
+// TODO Do we need it?
+// NewRawRequest creates an API request.
 // A relative URL can be provided in urlStr, in which case it is resolved relative to the baseURL of the Client.
 // Allows using an optional native io.Reader for sourcing the request body.
-func (c *Client) NewRawRequestWithContext(ctx context.Context, method, urlStr string, body io.Reader) (*http.Request, error) {
+func (c *Client) NewRawRequest(ctx context.Context, method, urlStr string, body io.Reader) (*http.Request, error) {
 	rel, err := url.Parse(urlStr)
 	if err != nil {
 		return nil, err
@@ -119,7 +144,7 @@ func (c *Client) NewRawRequestWithContext(ctx context.Context, method, urlStr st
 	// Relative URLs should be specified without a preceding slash since baseURL will have the trailing slash
 	rel.Path = strings.TrimLeft(rel.Path, "/")
 
-	u := c.baseURL.ResolveReference(rel)
+	u := c.BaseURL.ResolveReference(rel)
 
 	req, err := http.NewRequestWithContext(ctx, method, u.String(), body)
 	if err != nil {
@@ -146,24 +171,21 @@ func (c *Client) NewRawRequestWithContext(ctx context.Context, method, urlStr st
 	return req, nil
 }
 
-// NewRawRequest wraps NewRawRequestWithContext using the background context.
-func (c *Client) NewRawRequest(method, urlStr string, body io.Reader) (*http.Request, error) {
-	return c.NewRawRequestWithContext(context.Background(), method, urlStr, body)
-}
-
-// NewRequestWithContext creates an API request.
-// A relative URL can be provided in urlStr, in which case it is resolved relative to the baseURL of the Client.
+// NewRequest creates an API request.
+// A relative URL can be provided in urlStr, in which case it is resolved relative to the BaseURL of the Client.
 // If specified, the value pointed to by body is JSON encoded and included as the request body.
-func (c *Client) NewRequestWithContext(ctx context.Context, method, urlStr string, body interface{}) (*http.Request, error) {
+func (c *Client) NewRequest(ctx context.Context, method, urlStr string, body interface{}) (*http.Request, error) {
 	rel, err := url.Parse(urlStr)
 	if err != nil {
 		return nil, err
 	}
-	// Relative URLs should be specified without a preceding slash since baseURL will have the trailing slash
+	// Relative URLs should be specified without a preceding slash since BaseURL will have the trailing slash
 	rel.Path = strings.TrimLeft(rel.Path, "/")
 
-	u := c.baseURL.ResolveReference(rel)
+	u := c.BaseURL.ResolveReference(rel)
 
+	// TODO This part is the difference between NewRawRequestWithContext
+	// Check if we can get this working in one function
 	var buf io.ReadWriter
 	if body != nil {
 		buf = new(bytes.Buffer)
@@ -198,15 +220,10 @@ func (c *Client) NewRequestWithContext(ctx context.Context, method, urlStr strin
 	return req, nil
 }
 
-// NewRequest wraps NewRequestWithContext using the background context.
-func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error) {
-	return c.NewRequestWithContext(context.Background(), method, urlStr, body)
-}
-
-// addOptions adds the parameters in opt as URL query parameters to s.  opt
+// addOptions adds the parameters in opts as URL query parameters to s. opts
 // must be a struct whose fields may contain "url" tags.
-func addOptions(s string, opt interface{}) (string, error) {
-	v := reflect.ValueOf(opt)
+func addOptions(s string, opts interface{}) (string, error) {
+	v := reflect.ValueOf(opts)
 	if v.Kind() == reflect.Ptr && v.IsNil() {
 		return s, nil
 	}
@@ -216,7 +233,7 @@ func addOptions(s string, opt interface{}) (string, error) {
 		return s, err
 	}
 
-	qs, err := query.Values(opt)
+	qs, err := query.Values(opts)
 	if err != nil {
 		return s, err
 	}
@@ -236,7 +253,7 @@ func (c *Client) NewMultiPartRequestWithContext(ctx context.Context, method, url
 	// Relative URLs should be specified without a preceding slash since baseURL will have the trailing slash
 	rel.Path = strings.TrimLeft(rel.Path, "/")
 
-	u := c.baseURL.ResolveReference(rel)
+	u := c.BaseURL.ResolveReference(rel)
 
 	req, err := http.NewRequestWithContext(ctx, method, u.String(), buf)
 	if err != nil {
@@ -307,12 +324,6 @@ func CheckResponse(r *http.Response) error {
 	return err
 }
 
-// GetBaseURL will return you the Base URL.
-// This is the same URL as in the NewClient constructor
-func (c *Client) GetBaseURL() url.URL {
-	return *c.baseURL
-}
-
 // Response represents Jira API response. It wraps http.Response returned from
 // API and provides information about paging.
 type Response struct {
diff --git a/cloud/metaissue.go b/cloud/metaissue.go
index 40f0ae6..438a5f3 100644
--- a/cloud/metaissue.go
+++ b/cloud/metaissue.go
@@ -62,7 +62,7 @@ func (s *IssueService) GetCreateMeta(projectkeys string) (*CreateMetaInfo, *Resp
 func (s *IssueService) GetCreateMetaWithOptionsWithContext(ctx context.Context, options *GetQueryOptions) (*CreateMetaInfo, *Response, error) {
 	apiEndpoint := "rest/api/2/issue/createmeta"
 
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -93,7 +93,7 @@ func (s *IssueService) GetCreateMetaWithOptions(options *GetQueryOptions) (*Crea
 func (s *IssueService) GetEditMetaWithContext(ctx context.Context, issue *Issue) (*EditMetaInfo, *Response, error) {
 	apiEndpoint := fmt.Sprintf("/rest/api/2/issue/%s/editmeta", issue.Key)
 
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/cloud/organization.go b/cloud/organization.go
index 4950f2b..515f6c2 100644
--- a/cloud/organization.go
+++ b/cloud/organization.go
@@ -8,9 +8,7 @@ import (
 // OrganizationService handles Organizations for the Jira instance / API.
 //
 // Jira API docs: https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-organization/
-type OrganizationService struct {
-	client *Client
-}
+type OrganizationService service
 
 // OrganizationCreationDTO is DTO for creat organization API
 type OrganizationCreationDTO struct {
@@ -68,7 +66,7 @@ func (s *OrganizationService) GetAllOrganizationsWithContext(ctx context.Context
 		apiEndPoint += fmt.Sprintf("&accountId=%s", accountID)
 	}
 
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndPoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndPoint, nil)
 	req.Header.Set("Accept", "application/json")
 
 	if err != nil {
@@ -101,7 +99,7 @@ func (s *OrganizationService) CreateOrganizationWithContext(ctx context.Context,
 		Name: name,
 	}
 
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndPoint, organization)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndPoint, organization)
 	req.Header.Set("Accept", "application/json")
 
 	if err != nil {
@@ -133,7 +131,7 @@ func (s *OrganizationService) CreateOrganization(name string) (*Organization, *R
 func (s *OrganizationService) GetOrganizationWithContext(ctx context.Context, organizationID int) (*Organization, *Response, error) {
 	apiEndPoint := fmt.Sprintf("rest/servicedeskapi/organization/%d", organizationID)
 
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndPoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndPoint, nil)
 	req.Header.Set("Accept", "application/json")
 
 	if err != nil {
@@ -165,7 +163,7 @@ func (s *OrganizationService) GetOrganization(organizationID int) (*Organization
 func (s *OrganizationService) DeleteOrganizationWithContext(ctx context.Context, organizationID int) (*Response, error) {
 	apiEndPoint := fmt.Sprintf("rest/servicedeskapi/organization/%d", organizationID)
 
-	req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndPoint, nil)
+	req, err := s.client.NewRequest(ctx, "DELETE", apiEndPoint, nil)
 
 	if err != nil {
 		return nil, err
@@ -195,7 +193,7 @@ func (s *OrganizationService) DeleteOrganization(organizationID int) (*Response,
 func (s *OrganizationService) GetPropertiesKeysWithContext(ctx context.Context, organizationID int) (*PropertyKeys, *Response, error) {
 	apiEndPoint := fmt.Sprintf("rest/servicedeskapi/organization/%d/property", organizationID)
 
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndPoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndPoint, nil)
 	req.Header.Set("Accept", "application/json")
 
 	if err != nil {
@@ -225,7 +223,7 @@ func (s *OrganizationService) GetPropertiesKeys(organizationID int) (*PropertyKe
 func (s *OrganizationService) GetPropertyWithContext(ctx context.Context, organizationID int, propertyKey string) (*EntityProperty, *Response, error) {
 	apiEndPoint := fmt.Sprintf("rest/servicedeskapi/organization/%d/property/%s", organizationID, propertyKey)
 
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndPoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndPoint, nil)
 	req.Header.Set("Accept", "application/json")
 
 	if err != nil {
@@ -256,7 +254,7 @@ func (s *OrganizationService) GetProperty(organizationID int, propertyKey string
 func (s *OrganizationService) SetPropertyWithContext(ctx context.Context, organizationID int, propertyKey string) (*Response, error) {
 	apiEndPoint := fmt.Sprintf("rest/servicedeskapi/organization/%d/property/%s", organizationID, propertyKey)
 
-	req, err := s.client.NewRequestWithContext(ctx, "PUT", apiEndPoint, nil)
+	req, err := s.client.NewRequest(ctx, "PUT", apiEndPoint, nil)
 	req.Header.Set("Accept", "application/json")
 
 	if err != nil {
@@ -285,7 +283,7 @@ func (s *OrganizationService) SetProperty(organizationID int, propertyKey string
 func (s *OrganizationService) DeletePropertyWithContext(ctx context.Context, organizationID int, propertyKey string) (*Response, error) {
 	apiEndPoint := fmt.Sprintf("rest/servicedeskapi/organization/%d/property/%s", organizationID, propertyKey)
 
-	req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndPoint, nil)
+	req, err := s.client.NewRequest(ctx, "DELETE", apiEndPoint, nil)
 	req.Header.Set("Accept", "application/json")
 
 	if err != nil {
@@ -317,7 +315,7 @@ func (s *OrganizationService) DeleteProperty(organizationID int, propertyKey str
 func (s *OrganizationService) GetUsersWithContext(ctx context.Context, organizationID int, start int, limit int) (*PagedDTO, *Response, error) {
 	apiEndPoint := fmt.Sprintf("rest/servicedeskapi/organization/%d/user?start=%d&limit=%d", organizationID, start, limit)
 
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndPoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndPoint, nil)
 	req.Header.Set("Accept", "application/json")
 
 	if err != nil {
@@ -346,7 +344,7 @@ func (s *OrganizationService) GetUsers(organizationID int, start int, limit int)
 func (s *OrganizationService) AddUsersWithContext(ctx context.Context, organizationID int, users OrganizationUsersDTO) (*Response, error) {
 	apiEndPoint := fmt.Sprintf("rest/servicedeskapi/organization/%d/user", organizationID)
 
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndPoint, users)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndPoint, users)
 
 	if err != nil {
 		return nil, err
@@ -374,7 +372,7 @@ func (s *OrganizationService) AddUsers(organizationID int, users OrganizationUse
 func (s *OrganizationService) RemoveUsersWithContext(ctx context.Context, organizationID int, users OrganizationUsersDTO) (*Response, error) {
 	apiEndPoint := fmt.Sprintf("rest/servicedeskapi/organization/%d/user", organizationID)
 
-	req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndPoint, nil)
+	req, err := s.client.NewRequest(ctx, "DELETE", apiEndPoint, nil)
 	req.Header.Set("Accept", "application/json")
 
 	if err != nil {
diff --git a/cloud/permissionscheme.go b/cloud/permissionscheme.go
index 3c11805..3f8edcc 100644
--- a/cloud/permissionscheme.go
+++ b/cloud/permissionscheme.go
@@ -8,9 +8,8 @@ import (
 // PermissionSchemeService handles permissionschemes for the Jira instance / API.
 //
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-group-Permissionscheme
-type PermissionSchemeService struct {
-	client *Client
-}
+type PermissionSchemeService service
+
 type PermissionSchemes struct {
 	PermissionSchemes []PermissionScheme `json:"permissionSchemes" structs:"permissionSchemes"`
 }
@@ -33,7 +32,7 @@ type Holder struct {
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-api-3-permissionscheme-get
 func (s *PermissionSchemeService) GetListWithContext(ctx context.Context) (*PermissionSchemes, *Response, error) {
 	apiEndpoint := "/rest/api/3/permissionscheme"
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -58,7 +57,7 @@ func (s *PermissionSchemeService) GetList() (*PermissionSchemes, *Response, erro
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-api-3-permissionscheme-schemeId-get
 func (s *PermissionSchemeService) GetWithContext(ctx context.Context, schemeID int) (*PermissionScheme, *Response, error) {
 	apiEndpoint := fmt.Sprintf("/rest/api/3/permissionscheme/%d", schemeID)
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/cloud/priority.go b/cloud/priority.go
index 2cd5971..7f62cd4 100644
--- a/cloud/priority.go
+++ b/cloud/priority.go
@@ -5,9 +5,7 @@ import "context"
 // PriorityService handles priorities for the Jira instance / API.
 //
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-Priority
-type PriorityService struct {
-	client *Client
-}
+type PriorityService service
 
 // Priority represents a priority of a Jira issue.
 // Typical types are "Normal", "Moderate", "Urgent", ...
@@ -25,7 +23,7 @@ type Priority struct {
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-priority-get
 func (s *PriorityService) GetListWithContext(ctx context.Context) ([]Priority, *Response, error) {
 	apiEndpoint := "rest/api/2/priority"
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/cloud/project.go b/cloud/project.go
index e67fd02..60e4eae 100644
--- a/cloud/project.go
+++ b/cloud/project.go
@@ -10,9 +10,7 @@ import (
 // ProjectService handles projects for the Jira instance / API.
 //
 // Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/project
-type ProjectService struct {
-	client *Client
-}
+type ProjectService service
 
 // ProjectList represent a list of Projects
 type ProjectList []struct {
@@ -99,7 +97,7 @@ func (s *ProjectService) GetList() (*ProjectList, *Response, error) {
 // Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/project-getAllProjects
 func (s *ProjectService) ListWithOptionsWithContext(ctx context.Context, options *GetQueryOptions) (*ProjectList, *Response, error) {
 	apiEndpoint := "rest/api/2/project"
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -134,7 +132,7 @@ func (s *ProjectService) ListWithOptions(options *GetQueryOptions) (*ProjectList
 // Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/project-getProject
 func (s *ProjectService) GetWithContext(ctx context.Context, projectID string) (*Project, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/project/%s", projectID)
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -161,7 +159,7 @@ func (s *ProjectService) Get(projectID string) (*Project, *Response, error) {
 // Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/project-getProject
 func (s *ProjectService) GetPermissionSchemeWithContext(ctx context.Context, projectID string) (*PermissionScheme, *Response, error) {
 	apiEndpoint := fmt.Sprintf("/rest/api/2/project/%s/permissionscheme", projectID)
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/cloud/request.go b/cloud/request.go
index 2a0868a..6d276ee 100644
--- a/cloud/request.go
+++ b/cloud/request.go
@@ -6,9 +6,7 @@ import (
 )
 
 // RequestService handles ServiceDesk customer requests for the Jira instance / API.
-type RequestService struct {
-	client *Client
-}
+type RequestService service
 
 // Request represents a ServiceDesk customer request.
 type Request struct {
@@ -78,7 +76,7 @@ func (r *RequestService) CreateWithContext(ctx context.Context, requester string
 		payload.FieldValues[field.FieldID] = field.Value
 	}
 
-	req, err := r.client.NewRequestWithContext(ctx, "POST", apiEndpoint, payload)
+	req, err := r.client.NewRequest(ctx, "POST", apiEndpoint, payload)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -103,7 +101,7 @@ func (r *RequestService) Create(requester string, participants []string, request
 func (r *RequestService) CreateCommentWithContext(ctx context.Context, issueIDOrKey string, comment *RequestComment) (*RequestComment, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/servicedeskapi/request/%v/comment", issueIDOrKey)
 
-	req, err := r.client.NewRequestWithContext(ctx, "POST", apiEndpoint, comment)
+	req, err := r.client.NewRequest(ctx, "POST", apiEndpoint, comment)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/cloud/resolution.go b/cloud/resolution.go
index df5282e..b10e44d 100644
--- a/cloud/resolution.go
+++ b/cloud/resolution.go
@@ -5,9 +5,7 @@ import "context"
 // ResolutionService handles resolutions for the Jira instance / API.
 //
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-Resolution
-type ResolutionService struct {
-	client *Client
-}
+type ResolutionService service
 
 // Resolution represents a resolution of a Jira issue.
 // Typical types are "Fixed", "Suspended", "Won't Fix", ...
@@ -23,7 +21,7 @@ type Resolution struct {
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-resolution-get
 func (s *ResolutionService) GetListWithContext(ctx context.Context) ([]Resolution, *Response, error) {
 	apiEndpoint := "rest/api/2/resolution"
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/cloud/role.go b/cloud/role.go
index 6ebc71b..7854bf3 100644
--- a/cloud/role.go
+++ b/cloud/role.go
@@ -8,9 +8,7 @@ import (
 // RoleService handles roles for the Jira instance / API.
 //
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-group-Role
-type RoleService struct {
-	client *Client
-}
+type RoleService service
 
 // Role represents a Jira product role
 type Role struct {
@@ -41,7 +39,7 @@ type ActorUser struct {
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-api-3-role-get
 func (s *RoleService) GetListWithContext(ctx context.Context) (*[]Role, *Response, error) {
 	apiEndpoint := "rest/api/3/role"
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -64,7 +62,7 @@ func (s *RoleService) GetList() (*[]Role, *Response, error) {
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-api-3-role-id-get
 func (s *RoleService) GetWithContext(ctx context.Context, roleID int) (*Role, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/3/role/%d", roleID)
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/cloud/servicedesk.go b/cloud/servicedesk.go
index 8ee6c04..69df04b 100644
--- a/cloud/servicedesk.go
+++ b/cloud/servicedesk.go
@@ -10,9 +10,7 @@ import (
 )
 
 // ServiceDeskService handles ServiceDesk for the Jira instance / API.
-type ServiceDeskService struct {
-	client *Client
-}
+type ServiceDeskService service
 
 // ServiceDeskOrganizationDTO is a DTO for ServiceDesk organizations
 type ServiceDeskOrganizationDTO struct {
@@ -29,7 +27,7 @@ func (s *ServiceDeskService) GetOrganizationsWithContext(ctx context.Context, se
 		apiEndPoint += fmt.Sprintf("&accountId=%s", accountID)
 	}
 
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndPoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndPoint, nil)
 	req.Header.Set("Accept", "application/json")
 
 	if err != nil {
@@ -65,7 +63,7 @@ func (s *ServiceDeskService) AddOrganizationWithContext(ctx context.Context, ser
 		OrganizationID: organizationID,
 	}
 
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndPoint, organization)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndPoint, organization)
 
 	if err != nil {
 		return nil, err
@@ -100,7 +98,7 @@ func (s *ServiceDeskService) RemoveOrganizationWithContext(ctx context.Context,
 		OrganizationID: organizationID,
 	}
 
-	req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndPoint, organization)
+	req, err := s.client.NewRequest(ctx, "DELETE", apiEndPoint, organization)
 
 	if err != nil {
 		return nil, err
@@ -132,7 +130,7 @@ func (s *ServiceDeskService) AddCustomersWithContext(ctx context.Context, servic
 	}{
 		AccountIDs: acountIDs,
 	}
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, payload)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndpoint, payload)
 	if err != nil {
 		return nil, err
 	}
@@ -164,7 +162,7 @@ func (s *ServiceDeskService) RemoveCustomersWithContext(ctx context.Context, ser
 	}{
 		AccountIDs: acountIDs,
 	}
-	req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, payload)
+	req, err := s.client.NewRequest(ctx, "DELETE", apiEndpoint, payload)
 	if err != nil {
 		return nil, err
 	}
@@ -190,7 +188,7 @@ func (s *ServiceDeskService) RemoveCustomers(serviceDeskID interface{}, acountID
 // https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-servicedesk/#api-rest-servicedeskapi-servicedesk-servicedeskid-customer-get
 func (s *ServiceDeskService) ListCustomersWithContext(ctx context.Context, serviceDeskID interface{}, options *CustomerListOptions) (*CustomerList, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/servicedeskapi/servicedesk/%v/customer", serviceDeskID)
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/cloud/sprint.go b/cloud/sprint.go
index e68338c..e589159 100644
--- a/cloud/sprint.go
+++ b/cloud/sprint.go
@@ -9,9 +9,7 @@ import (
 
 // SprintService handles sprints in Jira Agile API.
 // See https://docs.atlassian.com/jira-software/REST/cloud/
-type SprintService struct {
-	client *Client
-}
+type SprintService service
 
 // IssuesWrapper represents a wrapper struct for moving issues to sprint
 type IssuesWrapper struct {
@@ -34,7 +32,7 @@ func (s *SprintService) MoveIssuesToSprintWithContext(ctx context.Context, sprin
 
 	payload := IssuesWrapper{Issues: issueIDs}
 
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, payload)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndpoint, payload)
 
 	if err != nil {
 		return nil, err
@@ -61,7 +59,7 @@ func (s *SprintService) MoveIssuesToSprint(sprintID int, issueIDs []string) (*Re
 func (s *SprintService) GetIssuesForSprintWithContext(ctx context.Context, sprintID int) ([]Issue, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/agile/1.0/sprint/%d/issue", sprintID)
 
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 
 	if err != nil {
 		return nil, nil, err
@@ -94,7 +92,7 @@ func (s *SprintService) GetIssuesForSprint(sprintID int) ([]Issue, *Response, er
 func (s *SprintService) GetIssueWithContext(ctx context.Context, issueID string, options *GetQueryOptions) (*Issue, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/agile/1.0/issue/%s", issueID)
 
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 
 	if err != nil {
 		return nil, nil, err
diff --git a/cloud/status.go b/cloud/status.go
index 0480d56..181d8ac 100644
--- a/cloud/status.go
+++ b/cloud/status.go
@@ -5,9 +5,7 @@ import "context"
 // StatusService handles staties for the Jira instance / API.
 //
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-group-Workflow-statuses
-type StatusService struct {
-	client *Client
-}
+type StatusService service
 
 // Status represents the current status of a Jira issue.
 // Typical status are "Open", "In Progress", "Closed", ...
@@ -26,7 +24,7 @@ type Status struct {
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-status-get
 func (s *StatusService) GetAllStatusesWithContext(ctx context.Context) ([]Status, *Response, error) {
 	apiEndpoint := "rest/api/2/status"
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 
 	if err != nil {
 		return nil, nil, err
diff --git a/cloud/statuscategory.go b/cloud/statuscategory.go
index c78b03b..d7da81c 100644
--- a/cloud/statuscategory.go
+++ b/cloud/statuscategory.go
@@ -5,9 +5,7 @@ import "context"
 // StatusCategoryService handles status categories for the Jira instance / API.
 //
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-Statuscategory
-type StatusCategoryService struct {
-	client *Client
-}
+type StatusCategoryService service
 
 // StatusCategory represents the category a status belongs to.
 // Those categories can be user defined in every Jira instance.
@@ -32,7 +30,7 @@ const (
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-statuscategory-get
 func (s *StatusCategoryService) GetListWithContext(ctx context.Context) ([]StatusCategory, *Response, error) {
 	apiEndpoint := "rest/api/2/statuscategory"
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/cloud/user.go b/cloud/user.go
index 289ef08..acd038c 100644
--- a/cloud/user.go
+++ b/cloud/user.go
@@ -10,9 +10,7 @@ import (
 // UserService handles users for the Jira instance / API.
 //
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-group-Users
-type UserService struct {
-	client *Client
-}
+type UserService service
 
 // User represents a Jira user.
 type User struct {
@@ -51,7 +49,7 @@ type userSearchF func(userSearch) userSearch
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-user-get
 func (s *UserService) GetWithContext(ctx context.Context, accountId string) (*User, *Response, error) {
 	apiEndpoint := fmt.Sprintf("/rest/api/2/user?accountId=%s", accountId)
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -75,7 +73,7 @@ func (s *UserService) Get(accountId string) (*User, *Response, error) {
 // Jira API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/user-getUser
 func (s *UserService) GetByAccountIDWithContext(ctx context.Context, accountID string) (*User, *Response, error) {
 	apiEndpoint := fmt.Sprintf("/rest/api/2/user?accountId=%s", accountID)
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -98,7 +96,7 @@ func (s *UserService) GetByAccountID(accountID string) (*User, *Response, error)
 // Jira API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/user-createUser
 func (s *UserService) CreateWithContext(ctx context.Context, user *User) (*User, *Response, error) {
 	apiEndpoint := "/rest/api/2/user"
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, user)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndpoint, user)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -135,7 +133,7 @@ func (s *UserService) Create(user *User) (*User, *Response, error) {
 // Caller must close resp.Body
 func (s *UserService) DeleteWithContext(ctx context.Context, accountId string) (*Response, error) {
 	apiEndpoint := fmt.Sprintf("/rest/api/2/user?accountId=%s", accountId)
-	req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "DELETE", apiEndpoint, nil)
 	if err != nil {
 		return nil, err
 	}
@@ -158,7 +156,7 @@ func (s *UserService) Delete(accountId string) (*Response, error) {
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-user-groups-get
 func (s *UserService) GetGroupsWithContext(ctx context.Context, accountId string) (*[]UserGroup, *Response, error) {
 	apiEndpoint := fmt.Sprintf("/rest/api/2/user/groups?accountId=%s", accountId)
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -181,7 +179,7 @@ func (s *UserService) GetGroups(accountId string) (*[]UserGroup, *Response, erro
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-myself-get
 func (s *UserService) GetSelfWithContext(ctx context.Context) (*User, *Response, error) {
 	const apiEndpoint = "rest/api/2/myself"
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -275,7 +273,7 @@ func (s *UserService) FindWithContext(ctx context.Context, property string, twea
 	}
 
 	apiEndpoint := fmt.Sprintf("/rest/api/2/user/search?%s", queryString[:len(queryString)-1])
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/cloud/version.go b/cloud/version.go
index e10cc89..69b4bac 100644
--- a/cloud/version.go
+++ b/cloud/version.go
@@ -10,9 +10,7 @@ import (
 // VersionService handles Versions for the Jira instance / API.
 //
 // Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/version
-type VersionService struct {
-	client *Client
-}
+type VersionService service
 
 // Version represents a single release version of a project
 type Version struct {
@@ -33,7 +31,7 @@ type Version struct {
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-version-id-get
 func (s *VersionService) GetWithContext(ctx context.Context, versionID int) (*Version, *Response, error) {
 	apiEndpoint := fmt.Sprintf("/rest/api/2/version/%v", versionID)
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -56,7 +54,7 @@ func (s *VersionService) Get(versionID int) (*Version, *Response, error) {
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-version-post
 func (s *VersionService) CreateWithContext(ctx context.Context, version *Version) (*Version, *Response, error) {
 	apiEndpoint := "/rest/api/2/version"
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, version)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndpoint, version)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -92,7 +90,7 @@ func (s *VersionService) Create(version *Version) (*Version, *Response, error) {
 // Caller must close resp.Body
 func (s *VersionService) UpdateWithContext(ctx context.Context, version *Version) (*Version, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/version/%v", version.ID)
-	req, err := s.client.NewRequestWithContext(ctx, "PUT", apiEndpoint, version)
+	req, err := s.client.NewRequest(ctx, "PUT", apiEndpoint, version)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/onpremise/auth_transport_cookie.go b/onpremise/auth_transport_cookie.go
index 3b76813..c63104e 100644
--- a/onpremise/auth_transport_cookie.go
+++ b/onpremise/auth_transport_cookie.go
@@ -89,6 +89,7 @@ func (t *CookieAuthTransport) buildAuthRequest() (*http.Request, error) {
 	b := new(bytes.Buffer)
 	json.NewEncoder(b).Encode(body)
 
+	// TODO Use a context here
 	req, err := http.NewRequest("POST", t.AuthURL, b)
 	if err != nil {
 		return nil, err
diff --git a/onpremise/authentication.go b/onpremise/authentication.go
index 4bca28a..baade93 100644
--- a/onpremise/authentication.go
+++ b/onpremise/authentication.go
@@ -67,7 +67,7 @@ func (s *AuthenticationService) AcquireSessionCookieWithContext(ctx context.Cont
 		password,
 	}
 
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, body)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndpoint, body)
 	if err != nil {
 		return false, err
 	}
@@ -133,7 +133,7 @@ func (s *AuthenticationService) LogoutWithContext(ctx context.Context) error {
 	}
 
 	apiEndpoint := "rest/auth/1/session"
-	req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "DELETE", apiEndpoint, nil)
 	if err != nil {
 		return fmt.Errorf("creating the request to log the user out failed : %s", err)
 	}
@@ -174,7 +174,7 @@ func (s *AuthenticationService) GetCurrentUserWithContext(ctx context.Context) (
 	}
 
 	apiEndpoint := "rest/auth/1/session"
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, fmt.Errorf("could not create request for getting user info : %s", err)
 	}
diff --git a/onpremise/board.go b/onpremise/board.go
index 52cbfa6..d85e0a0 100644
--- a/onpremise/board.go
+++ b/onpremise/board.go
@@ -10,9 +10,7 @@ import (
 // BoardService handles Agile Boards for the Jira instance / API.
 //
 // Jira API docs: https://docs.atlassian.com/jira-software/REST/server/
-type BoardService struct {
-	client *Client
-}
+type BoardService service
 
 // BoardsList reflects a list of agile boards
 type BoardsList struct {
@@ -136,7 +134,7 @@ func (s *BoardService) GetAllBoardsWithContext(ctx context.Context, opt *BoardLi
 	if err != nil {
 		return nil, nil, err
 	}
-	req, err := s.client.NewRequestWithContext(ctx, "GET", url, nil)
+	req, err := s.client.NewRequest(ctx, "GET", url, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -162,7 +160,7 @@ func (s *BoardService) GetAllBoards(opt *BoardListOptions) (*BoardsList, *Respon
 // Jira API docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board-getBoard
 func (s *BoardService) GetBoardWithContext(ctx context.Context, boardID int) (*Board, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/agile/1.0/board/%v", boardID)
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -192,7 +190,7 @@ func (s *BoardService) GetBoard(boardID int) (*Board, *Response, error) {
 // Jira API docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board-createBoard
 func (s *BoardService) CreateBoardWithContext(ctx context.Context, board *Board) (*Board, *Response, error) {
 	apiEndpoint := "rest/agile/1.0/board"
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, board)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndpoint, board)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -218,7 +216,7 @@ func (s *BoardService) CreateBoard(board *Board) (*Board, *Response, error) {
 // Caller must close resp.Body
 func (s *BoardService) DeleteBoardWithContext(ctx context.Context, boardID int) (*Board, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/agile/1.0/board/%v", boardID)
-	req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "DELETE", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -269,7 +267,7 @@ func (s *BoardService) GetAllSprintsWithOptionsWithContext(ctx context.Context,
 	if err != nil {
 		return nil, nil, err
 	}
-	req, err := s.client.NewRequestWithContext(ctx, "GET", url, nil)
+	req, err := s.client.NewRequest(ctx, "GET", url, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -293,7 +291,7 @@ func (s *BoardService) GetAllSprintsWithOptions(boardID int, options *GetAllSpri
 func (s *BoardService) GetBoardConfigurationWithContext(ctx context.Context, boardID int) (*BoardConfiguration, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/agile/1.0/board/%d/configuration", boardID)
 
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 
 	if err != nil {
 		return nil, nil, err
diff --git a/onpremise/component.go b/onpremise/component.go
index 7c918dd..c1ac05d 100644
--- a/onpremise/component.go
+++ b/onpremise/component.go
@@ -4,9 +4,7 @@ import "context"
 
 // ComponentService handles components for the Jira instance / API.//
 // Jira API docs: https://docs.atlassian.com/software/jira/docs/api/REST/7.10.1/#api/2/component
-type ComponentService struct {
-	client *Client
-}
+type ComponentService service
 
 // CreateComponentOptions are passed to the ComponentService.Create function to create a new Jira component
 type CreateComponentOptions struct {
@@ -23,7 +21,7 @@ type CreateComponentOptions struct {
 // CreateWithContext creates a new Jira component based on the given options.
 func (s *ComponentService) CreateWithContext(ctx context.Context, options *CreateComponentOptions) (*ProjectComponent, *Response, error) {
 	apiEndpoint := "rest/api/2/component"
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, options)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndpoint, options)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/onpremise/customer.go b/onpremise/customer.go
index 82a46a0..21e9013 100644
--- a/onpremise/customer.go
+++ b/onpremise/customer.go
@@ -6,9 +6,7 @@ import (
 )
 
 // CustomerService handles ServiceDesk customers for the Jira instance / API.
-type CustomerService struct {
-	client *Client
-}
+type CustomerService service
 
 // Customer represents a ServiceDesk customer.
 type Customer struct {
@@ -52,7 +50,7 @@ func (c *CustomerService) CreateWithContext(ctx context.Context, email, displayN
 		DisplayName: displayName,
 	}
 
-	req, err := c.client.NewRequestWithContext(ctx, http.MethodPost, apiEndpoint, payload)
+	req, err := c.client.NewRequest(ctx, http.MethodPost, apiEndpoint, payload)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/onpremise/examples/addlabel/main.go b/onpremise/examples/addlabel/main.go
index 2f0c30a..ca5a6f7 100644
--- a/onpremise/examples/addlabel/main.go
+++ b/onpremise/examples/addlabel/main.go
@@ -8,7 +8,7 @@ import (
 	"strings"
 	"syscall"
 
-	jira "github.com/andygrunwald/go-jira/onpremise"
+	jira "github.com/andygrunwald/go-jira/cloud"
 	"golang.org/x/term"
 )
 
@@ -38,7 +38,7 @@ func main() {
 		Password: strings.TrimSpace(password),
 	}
 
-	client, err := jira.NewClient(tp.Client(), strings.TrimSpace(jiraURL))
+	client, err := jira.NewClient(strings.TrimSpace(jiraURL), tp.Client())
 	if err != nil {
 		fmt.Printf("\nerror: %v\n", err)
 		return
diff --git a/onpremise/examples/basicauth/main.go b/onpremise/examples/basicauth/main.go
index 8995269..7221d87 100644
--- a/onpremise/examples/basicauth/main.go
+++ b/onpremise/examples/basicauth/main.go
@@ -9,7 +9,7 @@ import (
 
 	"golang.org/x/term"
 
-	jira "github.com/andygrunwald/go-jira/onpremise"
+	jira "github.com/andygrunwald/go-jira/cloud"
 )
 
 func main() {
@@ -30,7 +30,7 @@ func main() {
 		Password: strings.TrimSpace(password),
 	}
 
-	client, err := jira.NewClient(tp.Client(), strings.TrimSpace(jiraURL))
+	client, err := jira.NewClient(strings.TrimSpace(jiraURL), tp.Client())
 	if err != nil {
 		fmt.Printf("\nerror: %v\n", err)
 		return
diff --git a/onpremise/examples/create/main.go b/onpremise/examples/create/main.go
index b306760..2dc3837 100644
--- a/onpremise/examples/create/main.go
+++ b/onpremise/examples/create/main.go
@@ -7,7 +7,7 @@ import (
 	"strings"
 	"syscall"
 
-	jira "github.com/andygrunwald/go-jira/onpremise"
+	jira "github.com/andygrunwald/go-jira/cloud"
 	"golang.org/x/term"
 )
 
@@ -29,7 +29,7 @@ func main() {
 		Password: strings.TrimSpace(password),
 	}
 
-	client, err := jira.NewClient(tp.Client(), strings.TrimSpace(jiraURL))
+	client, err := jira.NewClient(strings.TrimSpace(jiraURL), tp.Client())
 	if err != nil {
 		fmt.Printf("\nerror: %v\n", err)
 		return
diff --git a/onpremise/examples/createwithcustomfields/main.go b/onpremise/examples/createwithcustomfields/main.go
index be125c7..68b32b5 100644
--- a/onpremise/examples/createwithcustomfields/main.go
+++ b/onpremise/examples/createwithcustomfields/main.go
@@ -7,7 +7,7 @@ import (
 	"strings"
 	"syscall"
 
-	jira "github.com/andygrunwald/go-jira/onpremise"
+	jira "github.com/andygrunwald/go-jira/cloud"
 	"github.com/trivago/tgo/tcontainer"
 	"golang.org/x/term"
 )
@@ -36,7 +36,7 @@ func main() {
 		Password: strings.TrimSpace(password),
 	}
 
-	client, err := jira.NewClient(tp.Client(), strings.TrimSpace(jiraURL))
+	client, err := jira.NewClient(strings.TrimSpace(jiraURL), tp.Client())
 	if err != nil {
 		fmt.Printf("\nerror: %v\n", err)
 		os.Exit(1)
diff --git a/onpremise/examples/do/main.go b/onpremise/examples/do/main.go
index 56f0210..eee2747 100644
--- a/onpremise/examples/do/main.go
+++ b/onpremise/examples/do/main.go
@@ -1,14 +1,15 @@
 package main
 
 import (
+	"context"
 	"fmt"
 
-	jira "github.com/andygrunwald/go-jira/onpremise"
+	jira "github.com/andygrunwald/go-jira/cloud"
 )
 
 func main() {
-	jiraClient, _ := jira.NewClient(nil, "https://jira.atlassian.com/")
-	req, _ := jiraClient.NewRequest("GET", "/rest/api/2/project", nil)
+	jiraClient, _ := jira.NewClient("https://jira.atlassian.com/", nil)
+	req, _ := jiraClient.NewRequest(context.Background(), "GET", "/rest/api/2/project", nil)
 
 	projects := new([]jira.Project)
 	res, err := jiraClient.Do(req, projects)
diff --git a/onpremise/examples/ignorecerts/main.go b/onpremise/examples/ignorecerts/main.go
index 76e42dc..525cd51 100644
--- a/onpremise/examples/ignorecerts/main.go
+++ b/onpremise/examples/ignorecerts/main.go
@@ -5,7 +5,7 @@ import (
 	"fmt"
 	"net/http"
 
-	jira "github.com/andygrunwald/go-jira/onpremise"
+	jira "github.com/andygrunwald/go-jira/cloud"
 )
 
 func main() {
@@ -14,7 +14,7 @@ func main() {
 	}
 	client := &http.Client{Transport: tr}
 
-	jiraClient, _ := jira.NewClient(client, "https://issues.apache.org/jira/")
+	jiraClient, _ := jira.NewClient("https://issues.apache.org/jira/", client)
 	issue, _, _ := jiraClient.Issue.Get("MESOS-3325", nil)
 
 	fmt.Printf("%s: %+v\n", issue.Key, issue.Fields.Summary)
diff --git a/onpremise/examples/jql/main.go b/onpremise/examples/jql/main.go
index 1a78fe9..ce3ba45 100644
--- a/onpremise/examples/jql/main.go
+++ b/onpremise/examples/jql/main.go
@@ -3,11 +3,11 @@ package main
 import (
 	"fmt"
 
-	jira "github.com/andygrunwald/go-jira/onpremise"
+	jira "github.com/andygrunwald/go-jira/cloud"
 )
 
 func main() {
-	jiraClient, _ := jira.NewClient(nil, "https://issues.apache.org/jira/")
+	jiraClient, _ := jira.NewClient("https://issues.apache.org/jira/", nil)
 
 	// Running JQL query
 
diff --git a/onpremise/examples/newclient/main.go b/onpremise/examples/newclient/main.go
index b63a30f..c03461d 100644
--- a/onpremise/examples/newclient/main.go
+++ b/onpremise/examples/newclient/main.go
@@ -3,11 +3,11 @@ package main
 import (
 	"fmt"
 
-	jira "github.com/andygrunwald/go-jira/onpremise"
+	jira "github.com/andygrunwald/go-jira/cloud"
 )
 
 func main() {
-	jiraClient, _ := jira.NewClient(nil, "https://issues.apache.org/jira/")
+	jiraClient, _ := jira.NewClient("https://issues.apache.org/jira/", nil)
 	issue, _, _ := jiraClient.Issue.Get("MESOS-3325", nil)
 
 	fmt.Printf("%s: %+v\n", issue.Key, issue.Fields.Summary)
diff --git a/onpremise/examples/pagination/main.go b/onpremise/examples/pagination/main.go
index f60642a..07c52ee 100644
--- a/onpremise/examples/pagination/main.go
+++ b/onpremise/examples/pagination/main.go
@@ -3,7 +3,7 @@ package main
 import (
 	"fmt"
 
-	jira "github.com/andygrunwald/go-jira/onpremise"
+	jira "github.com/andygrunwald/go-jira/cloud"
 )
 
 // GetAllIssues will implement pagination of api and get all the issues.
@@ -38,7 +38,7 @@ func GetAllIssues(client *jira.Client, searchString string) ([]jira.Issue, error
 }
 
 func main() {
-	jiraClient, err := jira.NewClient(nil, "https://issues.apache.org/jira/")
+	jiraClient, err := jira.NewClient("https://issues.apache.org/jira/", nil)
 	if err != nil {
 		panic(err)
 	}
diff --git a/onpremise/examples/renderedfields/main.go b/onpremise/examples/renderedfields/main.go
index 45cdcee..274498f 100644
--- a/onpremise/examples/renderedfields/main.go
+++ b/onpremise/examples/renderedfields/main.go
@@ -10,7 +10,7 @@ import (
 
 	"golang.org/x/term"
 
-	jira "github.com/andygrunwald/go-jira/onpremise"
+	jira "github.com/andygrunwald/go-jira/cloud"
 )
 
 func main() {
@@ -43,7 +43,7 @@ func main() {
 		tp = ba.Client()
 	}
 
-	client, err := jira.NewClient(tp, strings.TrimSpace(jiraURL))
+	client, err := jira.NewClient(strings.TrimSpace(jiraURL), tp)
 	if err != nil {
 		fmt.Printf("\nerror: %v\n", err)
 		return
diff --git a/onpremise/examples/searchpages/main.go b/onpremise/examples/searchpages/main.go
index 8ba33ad..51db937 100644
--- a/onpremise/examples/searchpages/main.go
+++ b/onpremise/examples/searchpages/main.go
@@ -9,7 +9,7 @@ import (
 	"syscall"
 	"time"
 
-	jira "github.com/andygrunwald/go-jira/onpremise"
+	jira "github.com/andygrunwald/go-jira/cloud"
 	"golang.org/x/term"
 )
 
@@ -34,7 +34,7 @@ func main() {
 		Password: strings.TrimSpace(password),
 	}
 
-	client, err := jira.NewClient(tp.Client(), strings.TrimSpace(jiraURL))
+	client, err := jira.NewClient(strings.TrimSpace(jiraURL), tp.Client())
 	if err != nil {
 		log.Fatal(err)
 	}
diff --git a/onpremise/field.go b/onpremise/field.go
index 166b9c4..7747721 100644
--- a/onpremise/field.go
+++ b/onpremise/field.go
@@ -5,9 +5,7 @@ import "context"
 // FieldService handles fields for the Jira instance / API.
 //
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-Field
-type FieldService struct {
-	client *Client
-}
+type FieldService service
 
 // Field represents a field of a Jira issue.
 type Field struct {
@@ -36,7 +34,7 @@ type FieldSchema struct {
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-field-get
 func (s *FieldService) GetListWithContext(ctx context.Context) ([]Field, *Response, error) {
 	apiEndpoint := "rest/api/2/field"
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/onpremise/filter.go b/onpremise/filter.go
index 07ead72..4652429 100644
--- a/onpremise/filter.go
+++ b/onpremise/filter.go
@@ -10,9 +10,7 @@ import (
 // FilterService handles fields for the Jira instance / API.
 //
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-group-Filter
-type FilterService struct {
-	client *Client
-}
+type FilterService service
 
 // Filter represents a Filter in Jira
 type Filter struct {
@@ -125,7 +123,7 @@ func (fs *FilterService) GetListWithContext(ctx context.Context) ([]*Filter, *Re
 
 	options := &GetQueryOptions{}
 	apiEndpoint := "rest/api/2/filter"
-	req, err := fs.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := fs.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -153,7 +151,7 @@ func (fs *FilterService) GetList() ([]*Filter, *Response, error) {
 // GetFavouriteListWithContext retrieves the user's favourited filters from Jira
 func (fs *FilterService) GetFavouriteListWithContext(ctx context.Context) ([]*Filter, *Response, error) {
 	apiEndpoint := "rest/api/2/filter/favourite"
-	req, err := fs.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := fs.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -174,7 +172,7 @@ func (fs *FilterService) GetFavouriteList() ([]*Filter, *Response, error) {
 // GetWithContext retrieves a single Filter from Jira
 func (fs *FilterService) GetWithContext(ctx context.Context, filterID int) (*Filter, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/filter/%d", filterID)
-	req, err := fs.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := fs.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -202,7 +200,7 @@ func (fs *FilterService) GetMyFiltersWithContext(ctx context.Context, opts *GetM
 	if err != nil {
 		return nil, nil, err
 	}
-	req, err := fs.client.NewRequestWithContext(ctx, "GET", url, nil)
+	req, err := fs.client.NewRequest(ctx, "GET", url, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -230,7 +228,7 @@ func (fs *FilterService) SearchWithContext(ctx context.Context, opt *FilterSearc
 	if err != nil {
 		return nil, nil, err
 	}
-	req, err := fs.client.NewRequestWithContext(ctx, "GET", url, nil)
+	req, err := fs.client.NewRequest(ctx, "GET", url, nil)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/onpremise/group.go b/onpremise/group.go
index e197922..12a0cf5 100644
--- a/onpremise/group.go
+++ b/onpremise/group.go
@@ -9,9 +9,7 @@ import (
 // GroupService handles Groups for the Jira instance / API.
 //
 // Jira API docs: https://docs.atlassian.com/jira/REST/server/#api/2/group
-type GroupService struct {
-	client *Client
-}
+type GroupService service
 
 // groupMembersResult is only a small wrapper around the Group* methods
 // to be able to parse the results
@@ -68,7 +66,7 @@ type GroupSearchOptions struct {
 // WARNING: This API only returns the first page of group members
 func (s *GroupService) GetWithContext(ctx context.Context, name string) ([]GroupMember, *Response, error) {
 	apiEndpoint := fmt.Sprintf("/rest/api/2/group/member?groupname=%s", url.QueryEscape(name))
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -105,7 +103,7 @@ func (s *GroupService) GetWithOptionsWithContext(ctx context.Context, name strin
 			options.IncludeInactiveUsers,
 		)
 	}
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -132,7 +130,7 @@ func (s *GroupService) AddWithContext(ctx context.Context, groupname string, use
 		Name string `json:"name"`
 	}
 	user.Name = username
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, &user)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndpoint, &user)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -158,7 +156,7 @@ func (s *GroupService) Add(groupname string, username string) (*Group, *Response
 // Caller must close resp.Body
 func (s *GroupService) RemoveWithContext(ctx context.Context, groupname string, username string) (*Response, error) {
 	apiEndpoint := fmt.Sprintf("/rest/api/2/group/user?groupname=%s&username=%s", groupname, username)
-	req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "DELETE", apiEndpoint, nil)
 	if err != nil {
 		return nil, err
 	}
diff --git a/onpremise/issue.go b/onpremise/issue.go
index 4f82017..e031ae4 100644
--- a/onpremise/issue.go
+++ b/onpremise/issue.go
@@ -27,9 +27,7 @@ const (
 // IssueService handles Issues for the Jira instance / API.
 //
 // Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/issue
-type IssueService struct {
-	client *Client
-}
+type IssueService service
 
 // UpdateQueryOptions specifies the optional parameters to the Edit issue
 type UpdateQueryOptions struct {
@@ -617,7 +615,7 @@ type RemoteLinkStatus struct {
 // Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/issue-getIssue
 func (s *IssueService) GetWithContext(ctx context.Context, issueID string, options *GetQueryOptions) (*Issue, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s", issueID)
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -651,7 +649,7 @@ func (s *IssueService) Get(issueID string, options *GetQueryOptions) (*Issue, *R
 // Caller must close resp.Body.
 func (s *IssueService) DownloadAttachmentWithContext(ctx context.Context, attachmentID string) (*Response, error) {
 	apiEndpoint := fmt.Sprintf("secure/attachment/%s/", attachmentID)
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, err
 	}
@@ -719,7 +717,7 @@ func (s *IssueService) PostAttachment(issueID string, r io.Reader, attachmentNam
 func (s *IssueService) DeleteAttachmentWithContext(ctx context.Context, attachmentID string) (*Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/attachment/%s", attachmentID)
 
-	req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "DELETE", apiEndpoint, nil)
 	if err != nil {
 		return nil, err
 	}
@@ -744,7 +742,7 @@ func (s *IssueService) DeleteAttachment(attachmentID string) (*Response, error)
 func (s *IssueService) DeleteLinkWithContext(ctx context.Context, linkID string) (*Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/issueLink/%s", linkID)
 
-	req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "DELETE", apiEndpoint, nil)
 	if err != nil {
 		return nil, err
 	}
@@ -771,7 +769,7 @@ func (s *IssueService) DeleteLink(linkID string) (*Response, error) {
 func (s *IssueService) GetWorklogsWithContext(ctx context.Context, issueID string, options ...func(*http.Request) error) (*Worklog, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/worklog", issueID)
 
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -816,7 +814,7 @@ func WithQueryOptions(options interface{}) func(*http.Request) error {
 // Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/issue-createIssues
 func (s *IssueService) CreateWithContext(ctx context.Context, issue *Issue) (*Issue, *Response, error) {
 	apiEndpoint := "rest/api/2/issue"
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, issue)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndpoint, issue)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -855,7 +853,7 @@ func (s *IssueService) UpdateWithOptionsWithContext(ctx context.Context, issue *
 	if err != nil {
 		return nil, nil, err
 	}
-	req, err := s.client.NewRequestWithContext(ctx, "PUT", url, issue)
+	req, err := s.client.NewRequest(ctx, "PUT", url, issue)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -895,7 +893,7 @@ func (s *IssueService) Update(issue *Issue) (*Issue, *Response, error) {
 // Caller must close resp.Body
 func (s *IssueService) UpdateIssueWithContext(ctx context.Context, jiraID string, data map[string]interface{}) (*Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/issue/%v", jiraID)
-	req, err := s.client.NewRequestWithContext(ctx, "PUT", apiEndpoint, data)
+	req, err := s.client.NewRequest(ctx, "PUT", apiEndpoint, data)
 	if err != nil {
 		return nil, err
 	}
@@ -920,7 +918,7 @@ func (s *IssueService) UpdateIssue(jiraID string, data map[string]interface{}) (
 // Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/issue-addComment
 func (s *IssueService) AddCommentWithContext(ctx context.Context, issueID string, comment *Comment) (*Comment, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/comment", issueID)
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, comment)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndpoint, comment)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -950,7 +948,7 @@ func (s *IssueService) UpdateCommentWithContext(ctx context.Context, issueID str
 		Body: comment.Body,
 	}
 	apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/comment/%s", issueID, comment.ID)
-	req, err := s.client.NewRequestWithContext(ctx, "PUT", apiEndpoint, reqBody)
+	req, err := s.client.NewRequest(ctx, "PUT", apiEndpoint, reqBody)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -974,7 +972,7 @@ func (s *IssueService) UpdateComment(issueID string, comment *Comment) (*Comment
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-api-3-issue-issueIdOrKey-comment-id-delete
 func (s *IssueService) DeleteCommentWithContext(ctx context.Context, issueID, commentID string) error {
 	apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/comment/%s", issueID, commentID)
-	req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "DELETE", apiEndpoint, nil)
 	if err != nil {
 		return err
 	}
@@ -999,7 +997,7 @@ func (s *IssueService) DeleteComment(issueID, commentID string) error {
 // https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-issue-issueIdOrKey-worklog-post
 func (s *IssueService) AddWorklogRecordWithContext(ctx context.Context, issueID string, record *WorklogRecord, options ...func(*http.Request) error) (*WorklogRecord, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/worklog", issueID)
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, record)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndpoint, record)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -1031,7 +1029,7 @@ func (s *IssueService) AddWorklogRecord(issueID string, record *WorklogRecord, o
 // https://docs.atlassian.com/software/jira/docs/api/REST/7.1.2/#api/2/issue-updateWorklog
 func (s *IssueService) UpdateWorklogRecordWithContext(ctx context.Context, issueID, worklogID string, record *WorklogRecord, options ...func(*http.Request) error) (*WorklogRecord, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/worklog/%s", issueID, worklogID)
-	req, err := s.client.NewRequestWithContext(ctx, "PUT", apiEndpoint, record)
+	req, err := s.client.NewRequest(ctx, "PUT", apiEndpoint, record)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -1064,7 +1062,7 @@ func (s *IssueService) UpdateWorklogRecord(issueID, worklogID string, record *Wo
 // Caller must close resp.Body
 func (s *IssueService) AddLinkWithContext(ctx context.Context, issueLink *IssueLink) (*Response, error) {
 	apiEndpoint := "rest/api/2/issueLink"
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, issueLink)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndpoint, issueLink)
 	if err != nil {
 		return nil, err
 	}
@@ -1115,7 +1113,7 @@ func (s *IssueService) SearchWithContext(ctx context.Context, jql string, option
 
 	u.RawQuery = uv.Encode()
 
-	req, err := s.client.NewRequestWithContext(ctx, "GET", u.String(), nil)
+	req, err := s.client.NewRequest(ctx, "GET", u.String(), nil)
 	if err != nil {
 		return []Issue{}, nil, err
 	}
@@ -1185,7 +1183,7 @@ func (s *IssueService) SearchPages(jql string, options *SearchOptions, f func(Is
 // GetCustomFieldsWithContext returns a map of customfield_* keys with string values
 func (s *IssueService) GetCustomFieldsWithContext(ctx context.Context, issueID string) (CustomFields, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s", issueID)
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -1230,7 +1228,7 @@ func (s *IssueService) GetCustomFields(issueID string) (CustomFields, *Response,
 // Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/issue-getTransitions
 func (s *IssueService) GetTransitionsWithContext(ctx context.Context, id string) ([]Transition, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/transitions?expand=transitions.fields", id)
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -1274,7 +1272,7 @@ func (s *IssueService) DoTransition(ticketID, transitionID string) (*Response, e
 func (s *IssueService) DoTransitionWithPayloadWithContext(ctx context.Context, ticketID, payload interface{}) (*Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/transitions", ticketID)
 
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, payload)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndpoint, payload)
 	if err != nil {
 		return nil, err
 	}
@@ -1384,7 +1382,7 @@ func (s *IssueService) DeleteWithContext(ctx context.Context, issueID string) (*
 	deletePayload["deleteSubtasks"] = "true"
 	content, _ := json.Marshal(deletePayload)
 
-	req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, content)
+	req, err := s.client.NewRequest(ctx, "DELETE", apiEndpoint, content)
 	if err != nil {
 		return nil, err
 	}
@@ -1405,7 +1403,7 @@ func (s *IssueService) Delete(issueID string) (*Response, error) {
 func (s *IssueService) GetWatchersWithContext(ctx context.Context, issueID string) (*[]User, *Response, error) {
 	watchesAPIEndpoint := fmt.Sprintf("rest/api/2/issue/%s/watchers", issueID)
 
-	req, err := s.client.NewRequestWithContext(ctx, "GET", watchesAPIEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", watchesAPIEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -1443,7 +1441,7 @@ func (s *IssueService) GetWatchers(issueID string) (*[]User, *Response, error) {
 func (s *IssueService) AddWatcherWithContext(ctx context.Context, issueID string, userName string) (*Response, error) {
 	apiEndPoint := fmt.Sprintf("rest/api/2/issue/%s/watchers", issueID)
 
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndPoint, userName)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndPoint, userName)
 	if err != nil {
 		return nil, err
 	}
@@ -1469,7 +1467,7 @@ func (s *IssueService) AddWatcher(issueID string, userName string) (*Response, e
 func (s *IssueService) RemoveWatcherWithContext(ctx context.Context, issueID string, userName string) (*Response, error) {
 	apiEndPoint := fmt.Sprintf("rest/api/2/issue/%s/watchers", issueID)
 
-	req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndPoint, userName)
+	req, err := s.client.NewRequest(ctx, "DELETE", apiEndPoint, userName)
 	if err != nil {
 		return nil, err
 	}
@@ -1495,7 +1493,7 @@ func (s *IssueService) RemoveWatcher(issueID string, userName string) (*Response
 func (s *IssueService) UpdateAssigneeWithContext(ctx context.Context, issueID string, assignee *User) (*Response, error) {
 	apiEndPoint := fmt.Sprintf("rest/api/2/issue/%s/assignee", issueID)
 
-	req, err := s.client.NewRequestWithContext(ctx, "PUT", apiEndPoint, assignee)
+	req, err := s.client.NewRequest(ctx, "PUT", apiEndPoint, assignee)
 	if err != nil {
 		return nil, err
 	}
@@ -1529,7 +1527,7 @@ func (c ChangelogHistory) CreatedTime() (time.Time, error) {
 // Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/issue-getRemoteIssueLinks
 func (s *IssueService) GetRemoteLinksWithContext(ctx context.Context, id string) (*[]RemoteLink, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/remotelink", id)
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -1553,7 +1551,7 @@ func (s *IssueService) GetRemoteLinks(id string) (*[]RemoteLink, *Response, erro
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issue-issueIdOrKey-remotelink-post
 func (s *IssueService) AddRemoteLinkWithContext(ctx context.Context, issueID string, remotelink *RemoteLink) (*RemoteLink, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/remotelink", issueID)
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, remotelink)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndpoint, remotelink)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -1578,7 +1576,7 @@ func (s *IssueService) AddRemoteLink(issueID string, remotelink *RemoteLink) (*R
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-issue-remote-links/#api-rest-api-2-issue-issueidorkey-remotelink-linkid-put
 func (s *IssueService) UpdateRemoteLinkWithContext(ctx context.Context, issueID string, linkID int, remotelink *RemoteLink) (*Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/remotelink/%d", issueID, linkID)
-	req, err := s.client.NewRequestWithContext(ctx, "PUT", apiEndpoint, remotelink)
+	req, err := s.client.NewRequest(ctx, "PUT", apiEndpoint, remotelink)
 	if err != nil {
 		return nil, err
 	}
diff --git a/onpremise/issuelinktype.go b/onpremise/issuelinktype.go
index c8cf542..c6af34d 100644
--- a/onpremise/issuelinktype.go
+++ b/onpremise/issuelinktype.go
@@ -10,16 +10,14 @@ import (
 // IssueLinkTypeService handles issue link types for the Jira instance / API.
 //
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-group-Issue-link-types
-type IssueLinkTypeService struct {
-	client *Client
-}
+type IssueLinkTypeService service
 
 // GetListWithContext gets all of the issue link types from Jira.
 //
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issueLinkType-get
 func (s *IssueLinkTypeService) GetListWithContext(ctx context.Context) ([]IssueLinkType, *Response, error) {
 	apiEndpoint := "rest/api/2/issueLinkType"
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -42,7 +40,7 @@ func (s *IssueLinkTypeService) GetList() ([]IssueLinkType, *Response, error) {
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issueLinkType-issueLinkTypeId-get
 func (s *IssueLinkTypeService) GetWithContext(ctx context.Context, ID string) (*IssueLinkType, *Response, error) {
 	apiEndPoint := fmt.Sprintf("rest/api/2/issueLinkType/%s", ID)
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndPoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndPoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -65,7 +63,7 @@ func (s *IssueLinkTypeService) Get(ID string) (*IssueLinkType, *Response, error)
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issueLinkType-post
 func (s *IssueLinkTypeService) CreateWithContext(ctx context.Context, linkType *IssueLinkType) (*IssueLinkType, *Response, error) {
 	apiEndpoint := "/rest/api/2/issueLinkType"
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, linkType)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndpoint, linkType)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -101,7 +99,7 @@ func (s *IssueLinkTypeService) Create(linkType *IssueLinkType) (*IssueLinkType,
 // Caller must close resp.Body
 func (s *IssueLinkTypeService) UpdateWithContext(ctx context.Context, linkType *IssueLinkType) (*IssueLinkType, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/issueLinkType/%s", linkType.ID)
-	req, err := s.client.NewRequestWithContext(ctx, "PUT", apiEndpoint, linkType)
+	req, err := s.client.NewRequest(ctx, "PUT", apiEndpoint, linkType)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -125,7 +123,7 @@ func (s *IssueLinkTypeService) Update(linkType *IssueLinkType) (*IssueLinkType,
 // Caller must close resp.Body
 func (s *IssueLinkTypeService) DeleteWithContext(ctx context.Context, ID string) (*Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/issueLinkType/%s", ID)
-	req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "DELETE", apiEndpoint, nil)
 	if err != nil {
 		return nil, err
 	}
diff --git a/onpremise/jira.go b/onpremise/jira.go
index 456708d..dea2181 100644
--- a/onpremise/jira.go
+++ b/onpremise/jira.go
@@ -10,27 +10,37 @@ import (
 	"net/url"
 	"reflect"
 	"strings"
+	"sync"
 
 	"github.com/google/go-querystring/query"
 )
 
-// httpClient defines an interface for an http.Client implementation so that alternative
-// http Clients can be passed in for making requests
-type httpClient interface {
-	Do(request *http.Request) (response *http.Response, err error)
-}
+const (
+	ClientVersion = "2.0.0"
+
+	defaultUserAgent = "go-jira" + "/" + ClientVersion
+)
 
 // A Client manages communication with the Jira API.
 type Client struct {
-	// HTTP client used to communicate with the API.
-	client httpClient
+	clientMu sync.Mutex   // clientMu protects the client during calls that modify it.
+	client   *http.Client // HTTP client used to communicate with the API.
 
 	// Base URL for API requests.
-	baseURL *url.URL
+	// Should be set to a domain endpoint of the Jira instance.
+	// BaseURL should always be specified with a trailing slash.
+	BaseURL *url.URL
+
+	// User agent used when communicating with the Jira API.
+	UserAgent string
 
 	// Session storage if the user authenticates with a Session cookie
+	// TODO Needed in Cloud and/or onpremise?
 	session *Session
 
+	// Reuse a single struct instead of allocating one for each service on the heap.
+	common service
+
 	// Services used for talking to different parts of the Jira API.
 	Authentication   *AuthenticationService
 	Issue            *IssueService
@@ -56,62 +66,77 @@ type Client struct {
 	Request          *RequestService
 }
 
-// NewClient returns a new Jira API client.
-// If a nil httpClient is provided, http.DefaultClient will be used.
-// To use API methods which require authentication you can follow the preferred solution and
-// provide an http.Client that will perform the authentication for you with OAuth and HTTP Basic (such as that provided by the golang.org/x/oauth2 library).
-// As an alternative you can use Session Cookie based authentication provided by this package as well.
-// See https://docs.atlassian.com/jira/REST/latest/#authentication
+// service is the base structure to bundle API services
+// under a sub-struct.
+type service struct {
+	client *Client
+}
+
+// Client returns the http.Client used by this Jira client.
+func (c *Client) Client() *http.Client {
+	c.clientMu.Lock()
+	defer c.clientMu.Unlock()
+	clientCopy := *c.client
+	return &clientCopy
+}
+
+// NewClient returns a new Jira API client with provided base URL (often is your Jira hostname)
+// If a nil httpClient is provided, a new http.Client will be used.
+// To use API methods which require authentication, provide an http.Client that will perform the authentication for you (such as that provided by the golang.org/x/oauth2 library).
 // baseURL is the HTTP endpoint of your Jira instance and should always be specified with a trailing slash.
-func NewClient(httpClient httpClient, baseURL string) (*Client, error) {
+func NewClient(baseURL string, httpClient *http.Client) (*Client, error) {
 	if httpClient == nil {
-		httpClient = http.DefaultClient
-	}
-
-	// ensure the baseURL contains a trailing slash so that all paths are preserved in later calls
-	if !strings.HasSuffix(baseURL, "/") {
-		baseURL += "/"
+		httpClient = &http.Client{}
 	}
 
-	parsedBaseURL, err := url.Parse(baseURL)
+	baseEndpoint, err := url.Parse(baseURL)
 	if err != nil {
 		return nil, err
 	}
+	// ensure the baseURL contains a trailing slash so that all paths are preserved in later calls
+	if !strings.HasSuffix(baseEndpoint.Path, "/") {
+		baseEndpoint.Path += "/"
+	}
 
 	c := &Client{
-		client:  httpClient,
-		baseURL: parsedBaseURL,
+		client:    httpClient,
+		BaseURL:   baseEndpoint,
+		UserAgent: defaultUserAgent,
 	}
+	c.common.client = c
+
+	// TODO Check if the authentication service is still needed (because of the transports)
 	c.Authentication = &AuthenticationService{client: c}
-	c.Issue = &IssueService{client: c}
-	c.Project = &ProjectService{client: c}
-	c.Board = &BoardService{client: c}
-	c.Sprint = &SprintService{client: c}
-	c.User = &UserService{client: c}
-	c.Group = &GroupService{client: c}
-	c.Version = &VersionService{client: c}
-	c.Priority = &PriorityService{client: c}
-	c.Field = &FieldService{client: c}
-	c.Component = &ComponentService{client: c}
-	c.Resolution = &ResolutionService{client: c}
-	c.StatusCategory = &StatusCategoryService{client: c}
-	c.Filter = &FilterService{client: c}
-	c.Role = &RoleService{client: c}
-	c.PermissionScheme = &PermissionSchemeService{client: c}
-	c.Status = &StatusService{client: c}
-	c.IssueLinkType = &IssueLinkTypeService{client: c}
-	c.Organization = &OrganizationService{client: c}
-	c.ServiceDesk = &ServiceDeskService{client: c}
-	c.Customer = &CustomerService{client: c}
-	c.Request = &RequestService{client: c}
+	c.Issue = (*IssueService)(&c.common)
+	c.Project = (*ProjectService)(&c.common)
+	c.Board = (*BoardService)(&c.common)
+	c.Sprint = (*SprintService)(&c.common)
+	c.User = (*UserService)(&c.common)
+	c.Group = (*GroupService)(&c.common)
+	c.Version = (*VersionService)(&c.common)
+	c.Priority = (*PriorityService)(&c.common)
+	c.Field = (*FieldService)(&c.common)
+	c.Component = (*ComponentService)(&c.common)
+	c.Resolution = (*ResolutionService)(&c.common)
+	c.StatusCategory = (*StatusCategoryService)(&c.common)
+	c.Filter = (*FilterService)(&c.common)
+	c.Role = (*RoleService)(&c.common)
+	c.PermissionScheme = (*PermissionSchemeService)(&c.common)
+	c.Status = (*StatusService)(&c.common)
+	c.IssueLinkType = (*IssueLinkTypeService)(&c.common)
+	c.Organization = (*OrganizationService)(&c.common)
+	c.ServiceDesk = (*ServiceDeskService)(&c.common)
+	c.Customer = (*CustomerService)(&c.common)
+	c.Request = (*RequestService)(&c.common)
 
 	return c, nil
 }
 
-// NewRawRequestWithContext creates an API request.
+// TODO Do we need it?
+// NewRawRequest creates an API request.
 // A relative URL can be provided in urlStr, in which case it is resolved relative to the baseURL of the Client.
 // Allows using an optional native io.Reader for sourcing the request body.
-func (c *Client) NewRawRequestWithContext(ctx context.Context, method, urlStr string, body io.Reader) (*http.Request, error) {
+func (c *Client) NewRawRequest(ctx context.Context, method, urlStr string, body io.Reader) (*http.Request, error) {
 	rel, err := url.Parse(urlStr)
 	if err != nil {
 		return nil, err
@@ -119,7 +144,7 @@ func (c *Client) NewRawRequestWithContext(ctx context.Context, method, urlStr st
 	// Relative URLs should be specified without a preceding slash since baseURL will have the trailing slash
 	rel.Path = strings.TrimLeft(rel.Path, "/")
 
-	u := c.baseURL.ResolveReference(rel)
+	u := c.BaseURL.ResolveReference(rel)
 
 	req, err := http.NewRequestWithContext(ctx, method, u.String(), body)
 	if err != nil {
@@ -146,24 +171,21 @@ func (c *Client) NewRawRequestWithContext(ctx context.Context, method, urlStr st
 	return req, nil
 }
 
-// NewRawRequest wraps NewRawRequestWithContext using the background context.
-func (c *Client) NewRawRequest(method, urlStr string, body io.Reader) (*http.Request, error) {
-	return c.NewRawRequestWithContext(context.Background(), method, urlStr, body)
-}
-
-// NewRequestWithContext creates an API request.
-// A relative URL can be provided in urlStr, in which case it is resolved relative to the baseURL of the Client.
+// NewRequest creates an API request.
+// A relative URL can be provided in urlStr, in which case it is resolved relative to the BaseURL of the Client.
 // If specified, the value pointed to by body is JSON encoded and included as the request body.
-func (c *Client) NewRequestWithContext(ctx context.Context, method, urlStr string, body interface{}) (*http.Request, error) {
+func (c *Client) NewRequest(ctx context.Context, method, urlStr string, body interface{}) (*http.Request, error) {
 	rel, err := url.Parse(urlStr)
 	if err != nil {
 		return nil, err
 	}
-	// Relative URLs should be specified without a preceding slash since baseURL will have the trailing slash
+	// Relative URLs should be specified without a preceding slash since BaseURL will have the trailing slash
 	rel.Path = strings.TrimLeft(rel.Path, "/")
 
-	u := c.baseURL.ResolveReference(rel)
+	u := c.BaseURL.ResolveReference(rel)
 
+	// TODO This part is the difference between NewRawRequestWithContext
+	// Check if we can get this working in one function
 	var buf io.ReadWriter
 	if body != nil {
 		buf = new(bytes.Buffer)
@@ -198,15 +220,10 @@ func (c *Client) NewRequestWithContext(ctx context.Context, method, urlStr strin
 	return req, nil
 }
 
-// NewRequest wraps NewRequestWithContext using the background context.
-func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error) {
-	return c.NewRequestWithContext(context.Background(), method, urlStr, body)
-}
-
-// addOptions adds the parameters in opt as URL query parameters to s.  opt
+// addOptions adds the parameters in opts as URL query parameters to s. opts
 // must be a struct whose fields may contain "url" tags.
-func addOptions(s string, opt interface{}) (string, error) {
-	v := reflect.ValueOf(opt)
+func addOptions(s string, opts interface{}) (string, error) {
+	v := reflect.ValueOf(opts)
 	if v.Kind() == reflect.Ptr && v.IsNil() {
 		return s, nil
 	}
@@ -216,7 +233,7 @@ func addOptions(s string, opt interface{}) (string, error) {
 		return s, err
 	}
 
-	qs, err := query.Values(opt)
+	qs, err := query.Values(opts)
 	if err != nil {
 		return s, err
 	}
@@ -236,7 +253,7 @@ func (c *Client) NewMultiPartRequestWithContext(ctx context.Context, method, url
 	// Relative URLs should be specified without a preceding slash since baseURL will have the trailing slash
 	rel.Path = strings.TrimLeft(rel.Path, "/")
 
-	u := c.baseURL.ResolveReference(rel)
+	u := c.BaseURL.ResolveReference(rel)
 
 	req, err := http.NewRequestWithContext(ctx, method, u.String(), buf)
 	if err != nil {
@@ -307,12 +324,6 @@ func CheckResponse(r *http.Response) error {
 	return err
 }
 
-// GetBaseURL will return you the Base URL.
-// This is the same URL as in the NewClient constructor
-func (c *Client) GetBaseURL() url.URL {
-	return *c.baseURL
-}
-
 // Response represents Jira API response. It wraps http.Response returned from
 // API and provides information about paging.
 type Response struct {
diff --git a/onpremise/metaissue.go b/onpremise/metaissue.go
index 6fff176..dd35a7f 100644
--- a/onpremise/metaissue.go
+++ b/onpremise/metaissue.go
@@ -62,7 +62,7 @@ func (s *IssueService) GetCreateMeta(projectkeys string) (*CreateMetaInfo, *Resp
 func (s *IssueService) GetCreateMetaWithOptionsWithContext(ctx context.Context, options *GetQueryOptions) (*CreateMetaInfo, *Response, error) {
 	apiEndpoint := "rest/api/2/issue/createmeta"
 
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -93,7 +93,7 @@ func (s *IssueService) GetCreateMetaWithOptions(options *GetQueryOptions) (*Crea
 func (s *IssueService) GetEditMetaWithContext(ctx context.Context, issue *Issue) (*EditMetaInfo, *Response, error) {
 	apiEndpoint := fmt.Sprintf("/rest/api/2/issue/%s/editmeta", issue.Key)
 
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/onpremise/organization.go b/onpremise/organization.go
index 373479b..7a752d5 100644
--- a/onpremise/organization.go
+++ b/onpremise/organization.go
@@ -8,9 +8,7 @@ import (
 // OrganizationService handles Organizations for the Jira instance / API.
 //
 // Jira API docs: https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-organization/
-type OrganizationService struct {
-	client *Client
-}
+type OrganizationService service
 
 // OrganizationCreationDTO is DTO for creat organization API
 type OrganizationCreationDTO struct {
@@ -68,7 +66,7 @@ func (s *OrganizationService) GetAllOrganizationsWithContext(ctx context.Context
 		apiEndPoint += fmt.Sprintf("&accountId=%s", accountID)
 	}
 
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndPoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndPoint, nil)
 	req.Header.Set("Accept", "application/json")
 
 	if err != nil {
@@ -101,7 +99,7 @@ func (s *OrganizationService) CreateOrganizationWithContext(ctx context.Context,
 		Name: name,
 	}
 
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndPoint, organization)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndPoint, organization)
 	req.Header.Set("Accept", "application/json")
 
 	if err != nil {
@@ -133,7 +131,7 @@ func (s *OrganizationService) CreateOrganization(name string) (*Organization, *R
 func (s *OrganizationService) GetOrganizationWithContext(ctx context.Context, organizationID int) (*Organization, *Response, error) {
 	apiEndPoint := fmt.Sprintf("rest/servicedeskapi/organization/%d", organizationID)
 
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndPoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndPoint, nil)
 	req.Header.Set("Accept", "application/json")
 
 	if err != nil {
@@ -165,7 +163,7 @@ func (s *OrganizationService) GetOrganization(organizationID int) (*Organization
 func (s *OrganizationService) DeleteOrganizationWithContext(ctx context.Context, organizationID int) (*Response, error) {
 	apiEndPoint := fmt.Sprintf("rest/servicedeskapi/organization/%d", organizationID)
 
-	req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndPoint, nil)
+	req, err := s.client.NewRequest(ctx, "DELETE", apiEndPoint, nil)
 
 	if err != nil {
 		return nil, err
@@ -195,7 +193,7 @@ func (s *OrganizationService) DeleteOrganization(organizationID int) (*Response,
 func (s *OrganizationService) GetPropertiesKeysWithContext(ctx context.Context, organizationID int) (*PropertyKeys, *Response, error) {
 	apiEndPoint := fmt.Sprintf("rest/servicedeskapi/organization/%d/property", organizationID)
 
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndPoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndPoint, nil)
 	req.Header.Set("Accept", "application/json")
 
 	if err != nil {
@@ -225,7 +223,7 @@ func (s *OrganizationService) GetPropertiesKeys(organizationID int) (*PropertyKe
 func (s *OrganizationService) GetPropertyWithContext(ctx context.Context, organizationID int, propertyKey string) (*EntityProperty, *Response, error) {
 	apiEndPoint := fmt.Sprintf("rest/servicedeskapi/organization/%d/property/%s", organizationID, propertyKey)
 
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndPoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndPoint, nil)
 	req.Header.Set("Accept", "application/json")
 
 	if err != nil {
@@ -256,7 +254,7 @@ func (s *OrganizationService) GetProperty(organizationID int, propertyKey string
 func (s *OrganizationService) SetPropertyWithContext(ctx context.Context, organizationID int, propertyKey string) (*Response, error) {
 	apiEndPoint := fmt.Sprintf("rest/servicedeskapi/organization/%d/property/%s", organizationID, propertyKey)
 
-	req, err := s.client.NewRequestWithContext(ctx, "PUT", apiEndPoint, nil)
+	req, err := s.client.NewRequest(ctx, "PUT", apiEndPoint, nil)
 	req.Header.Set("Accept", "application/json")
 
 	if err != nil {
@@ -285,7 +283,7 @@ func (s *OrganizationService) SetProperty(organizationID int, propertyKey string
 func (s *OrganizationService) DeletePropertyWithContext(ctx context.Context, organizationID int, propertyKey string) (*Response, error) {
 	apiEndPoint := fmt.Sprintf("rest/servicedeskapi/organization/%d/property/%s", organizationID, propertyKey)
 
-	req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndPoint, nil)
+	req, err := s.client.NewRequest(ctx, "DELETE", apiEndPoint, nil)
 	req.Header.Set("Accept", "application/json")
 
 	if err != nil {
@@ -317,7 +315,7 @@ func (s *OrganizationService) DeleteProperty(organizationID int, propertyKey str
 func (s *OrganizationService) GetUsersWithContext(ctx context.Context, organizationID int, start int, limit int) (*PagedDTO, *Response, error) {
 	apiEndPoint := fmt.Sprintf("rest/servicedeskapi/organization/%d/user?start=%d&limit=%d", organizationID, start, limit)
 
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndPoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndPoint, nil)
 	req.Header.Set("Accept", "application/json")
 
 	if err != nil {
@@ -346,7 +344,7 @@ func (s *OrganizationService) GetUsers(organizationID int, start int, limit int)
 func (s *OrganizationService) AddUsersWithContext(ctx context.Context, organizationID int, users OrganizationUsersDTO) (*Response, error) {
 	apiEndPoint := fmt.Sprintf("rest/servicedeskapi/organization/%d/user", organizationID)
 
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndPoint, users)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndPoint, users)
 
 	if err != nil {
 		return nil, err
@@ -374,7 +372,7 @@ func (s *OrganizationService) AddUsers(organizationID int, users OrganizationUse
 func (s *OrganizationService) RemoveUsersWithContext(ctx context.Context, organizationID int, users OrganizationUsersDTO) (*Response, error) {
 	apiEndPoint := fmt.Sprintf("rest/servicedeskapi/organization/%d/user", organizationID)
 
-	req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndPoint, nil)
+	req, err := s.client.NewRequest(ctx, "DELETE", apiEndPoint, nil)
 	req.Header.Set("Accept", "application/json")
 
 	if err != nil {
diff --git a/onpremise/permissionscheme.go b/onpremise/permissionscheme.go
index f81794e..3257eed 100644
--- a/onpremise/permissionscheme.go
+++ b/onpremise/permissionscheme.go
@@ -8,9 +8,8 @@ import (
 // PermissionSchemeService handles permissionschemes for the Jira instance / API.
 //
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-group-Permissionscheme
-type PermissionSchemeService struct {
-	client *Client
-}
+type PermissionSchemeService service
+
 type PermissionSchemes struct {
 	PermissionSchemes []PermissionScheme `json:"permissionSchemes" structs:"permissionSchemes"`
 }
@@ -33,7 +32,7 @@ type Holder struct {
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-api-3-permissionscheme-get
 func (s *PermissionSchemeService) GetListWithContext(ctx context.Context) (*PermissionSchemes, *Response, error) {
 	apiEndpoint := "/rest/api/3/permissionscheme"
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -58,7 +57,7 @@ func (s *PermissionSchemeService) GetList() (*PermissionSchemes, *Response, erro
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-api-3-permissionscheme-schemeId-get
 func (s *PermissionSchemeService) GetWithContext(ctx context.Context, schemeID int) (*PermissionScheme, *Response, error) {
 	apiEndpoint := fmt.Sprintf("/rest/api/3/permissionscheme/%d", schemeID)
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/onpremise/priority.go b/onpremise/priority.go
index 1d3e46b..9ec0dbe 100644
--- a/onpremise/priority.go
+++ b/onpremise/priority.go
@@ -5,9 +5,7 @@ import "context"
 // PriorityService handles priorities for the Jira instance / API.
 //
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-Priority
-type PriorityService struct {
-	client *Client
-}
+type PriorityService service
 
 // Priority represents a priority of a Jira issue.
 // Typical types are "Normal", "Moderate", "Urgent", ...
@@ -25,7 +23,7 @@ type Priority struct {
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-priority-get
 func (s *PriorityService) GetListWithContext(ctx context.Context) ([]Priority, *Response, error) {
 	apiEndpoint := "rest/api/2/priority"
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/onpremise/project.go b/onpremise/project.go
index 47782f4..6b6e8d7 100644
--- a/onpremise/project.go
+++ b/onpremise/project.go
@@ -10,9 +10,7 @@ import (
 // ProjectService handles projects for the Jira instance / API.
 //
 // Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/project
-type ProjectService struct {
-	client *Client
-}
+type ProjectService service
 
 // ProjectList represent a list of Projects
 type ProjectList []struct {
@@ -99,7 +97,7 @@ func (s *ProjectService) GetList() (*ProjectList, *Response, error) {
 // Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/project-getAllProjects
 func (s *ProjectService) ListWithOptionsWithContext(ctx context.Context, options *GetQueryOptions) (*ProjectList, *Response, error) {
 	apiEndpoint := "rest/api/2/project"
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -134,7 +132,7 @@ func (s *ProjectService) ListWithOptions(options *GetQueryOptions) (*ProjectList
 // Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/project-getProject
 func (s *ProjectService) GetWithContext(ctx context.Context, projectID string) (*Project, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/project/%s", projectID)
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -161,7 +159,7 @@ func (s *ProjectService) Get(projectID string) (*Project, *Response, error) {
 // Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/project-getProject
 func (s *ProjectService) GetPermissionSchemeWithContext(ctx context.Context, projectID string) (*PermissionScheme, *Response, error) {
 	apiEndpoint := fmt.Sprintf("/rest/api/2/project/%s/permissionscheme", projectID)
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/onpremise/request.go b/onpremise/request.go
index 3336ce7..b096005 100644
--- a/onpremise/request.go
+++ b/onpremise/request.go
@@ -6,9 +6,7 @@ import (
 )
 
 // RequestService handles ServiceDesk customer requests for the Jira instance / API.
-type RequestService struct {
-	client *Client
-}
+type RequestService service
 
 // Request represents a ServiceDesk customer request.
 type Request struct {
@@ -78,7 +76,7 @@ func (r *RequestService) CreateWithContext(ctx context.Context, requester string
 		payload.FieldValues[field.FieldID] = field.Value
 	}
 
-	req, err := r.client.NewRequestWithContext(ctx, "POST", apiEndpoint, payload)
+	req, err := r.client.NewRequest(ctx, "POST", apiEndpoint, payload)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -103,7 +101,7 @@ func (r *RequestService) Create(requester string, participants []string, request
 func (r *RequestService) CreateCommentWithContext(ctx context.Context, issueIDOrKey string, comment *RequestComment) (*RequestComment, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/servicedeskapi/request/%v/comment", issueIDOrKey)
 
-	req, err := r.client.NewRequestWithContext(ctx, "POST", apiEndpoint, comment)
+	req, err := r.client.NewRequest(ctx, "POST", apiEndpoint, comment)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/onpremise/resolution.go b/onpremise/resolution.go
index 76db3d6..c2613fb 100644
--- a/onpremise/resolution.go
+++ b/onpremise/resolution.go
@@ -5,9 +5,7 @@ import "context"
 // ResolutionService handles resolutions for the Jira instance / API.
 //
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-Resolution
-type ResolutionService struct {
-	client *Client
-}
+type ResolutionService service
 
 // Resolution represents a resolution of a Jira issue.
 // Typical types are "Fixed", "Suspended", "Won't Fix", ...
@@ -23,7 +21,7 @@ type Resolution struct {
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-resolution-get
 func (s *ResolutionService) GetListWithContext(ctx context.Context) ([]Resolution, *Response, error) {
 	apiEndpoint := "rest/api/2/resolution"
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/onpremise/role.go b/onpremise/role.go
index 01d94bf..f3b9f72 100644
--- a/onpremise/role.go
+++ b/onpremise/role.go
@@ -8,9 +8,7 @@ import (
 // RoleService handles roles for the Jira instance / API.
 //
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-group-Role
-type RoleService struct {
-	client *Client
-}
+type RoleService service
 
 // Role represents a Jira product role
 type Role struct {
@@ -41,7 +39,7 @@ type ActorUser struct {
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-api-3-role-get
 func (s *RoleService) GetListWithContext(ctx context.Context) (*[]Role, *Response, error) {
 	apiEndpoint := "rest/api/3/role"
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -64,7 +62,7 @@ func (s *RoleService) GetList() (*[]Role, *Response, error) {
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-api-3-role-id-get
 func (s *RoleService) GetWithContext(ctx context.Context, roleID int) (*Role, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/3/role/%d", roleID)
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/onpremise/servicedesk.go b/onpremise/servicedesk.go
index d935cc1..c303904 100644
--- a/onpremise/servicedesk.go
+++ b/onpremise/servicedesk.go
@@ -10,9 +10,7 @@ import (
 )
 
 // ServiceDeskService handles ServiceDesk for the Jira instance / API.
-type ServiceDeskService struct {
-	client *Client
-}
+type ServiceDeskService service
 
 // ServiceDeskOrganizationDTO is a DTO for ServiceDesk organizations
 type ServiceDeskOrganizationDTO struct {
@@ -29,7 +27,7 @@ func (s *ServiceDeskService) GetOrganizationsWithContext(ctx context.Context, se
 		apiEndPoint += fmt.Sprintf("&accountId=%s", accountID)
 	}
 
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndPoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndPoint, nil)
 	req.Header.Set("Accept", "application/json")
 
 	if err != nil {
@@ -65,7 +63,7 @@ func (s *ServiceDeskService) AddOrganizationWithContext(ctx context.Context, ser
 		OrganizationID: organizationID,
 	}
 
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndPoint, organization)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndPoint, organization)
 
 	if err != nil {
 		return nil, err
@@ -100,7 +98,7 @@ func (s *ServiceDeskService) RemoveOrganizationWithContext(ctx context.Context,
 		OrganizationID: organizationID,
 	}
 
-	req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndPoint, organization)
+	req, err := s.client.NewRequest(ctx, "DELETE", apiEndPoint, organization)
 
 	if err != nil {
 		return nil, err
@@ -132,7 +130,7 @@ func (s *ServiceDeskService) AddCustomersWithContext(ctx context.Context, servic
 	}{
 		AccountIDs: acountIDs,
 	}
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, payload)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndpoint, payload)
 	if err != nil {
 		return nil, err
 	}
@@ -164,7 +162,7 @@ func (s *ServiceDeskService) RemoveCustomersWithContext(ctx context.Context, ser
 	}{
 		AccountIDs: acountIDs,
 	}
-	req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, payload)
+	req, err := s.client.NewRequest(ctx, "DELETE", apiEndpoint, payload)
 	if err != nil {
 		return nil, err
 	}
@@ -190,7 +188,7 @@ func (s *ServiceDeskService) RemoveCustomers(serviceDeskID interface{}, acountID
 // https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-servicedesk/#api-rest-servicedeskapi-servicedesk-servicedeskid-customer-get
 func (s *ServiceDeskService) ListCustomersWithContext(ctx context.Context, serviceDeskID interface{}, options *CustomerListOptions) (*CustomerList, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/servicedeskapi/servicedesk/%v/customer", serviceDeskID)
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/onpremise/sprint.go b/onpremise/sprint.go
index d7560ca..667df05 100644
--- a/onpremise/sprint.go
+++ b/onpremise/sprint.go
@@ -9,9 +9,7 @@ import (
 
 // SprintService handles sprints in Jira Agile API.
 // See https://docs.atlassian.com/jira-software/REST/cloud/
-type SprintService struct {
-	client *Client
-}
+type SprintService service
 
 // IssuesWrapper represents a wrapper struct for moving issues to sprint
 type IssuesWrapper struct {
@@ -34,7 +32,7 @@ func (s *SprintService) MoveIssuesToSprintWithContext(ctx context.Context, sprin
 
 	payload := IssuesWrapper{Issues: issueIDs}
 
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, payload)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndpoint, payload)
 
 	if err != nil {
 		return nil, err
@@ -61,7 +59,7 @@ func (s *SprintService) MoveIssuesToSprint(sprintID int, issueIDs []string) (*Re
 func (s *SprintService) GetIssuesForSprintWithContext(ctx context.Context, sprintID int) ([]Issue, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/agile/1.0/sprint/%d/issue", sprintID)
 
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 
 	if err != nil {
 		return nil, nil, err
@@ -94,7 +92,7 @@ func (s *SprintService) GetIssuesForSprint(sprintID int) ([]Issue, *Response, er
 func (s *SprintService) GetIssueWithContext(ctx context.Context, issueID string, options *GetQueryOptions) (*Issue, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/agile/1.0/issue/%s", issueID)
 
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 
 	if err != nil {
 		return nil, nil, err
diff --git a/onpremise/status.go b/onpremise/status.go
index 93b4ca9..0ca307f 100644
--- a/onpremise/status.go
+++ b/onpremise/status.go
@@ -5,9 +5,7 @@ import "context"
 // StatusService handles staties for the Jira instance / API.
 //
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-group-Workflow-statuses
-type StatusService struct {
-	client *Client
-}
+type StatusService service
 
 // Status represents the current status of a Jira issue.
 // Typical status are "Open", "In Progress", "Closed", ...
@@ -26,7 +24,7 @@ type Status struct {
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-status-get
 func (s *StatusService) GetAllStatusesWithContext(ctx context.Context) ([]Status, *Response, error) {
 	apiEndpoint := "rest/api/2/status"
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 
 	if err != nil {
 		return nil, nil, err
diff --git a/onpremise/statuscategory.go b/onpremise/statuscategory.go
index 8918fbe..2035d7a 100644
--- a/onpremise/statuscategory.go
+++ b/onpremise/statuscategory.go
@@ -5,9 +5,7 @@ import "context"
 // StatusCategoryService handles status categories for the Jira instance / API.
 //
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-Statuscategory
-type StatusCategoryService struct {
-	client *Client
-}
+type StatusCategoryService service
 
 // StatusCategory represents the category a status belongs to.
 // Those categories can be user defined in every Jira instance.
@@ -32,7 +30,7 @@ const (
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-statuscategory-get
 func (s *StatusCategoryService) GetListWithContext(ctx context.Context) ([]StatusCategory, *Response, error) {
 	apiEndpoint := "rest/api/2/statuscategory"
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/onpremise/user.go b/onpremise/user.go
index bec5009..1064c0e 100644
--- a/onpremise/user.go
+++ b/onpremise/user.go
@@ -10,9 +10,7 @@ import (
 // UserService handles users for the Jira instance / API.
 //
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-group-Users
-type UserService struct {
-	client *Client
-}
+type UserService service
 
 // User represents a Jira user.
 type User struct {
@@ -51,7 +49,7 @@ type userSearchF func(userSearch) userSearch
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-user-get
 func (s *UserService) GetWithContext(ctx context.Context, accountId string) (*User, *Response, error) {
 	apiEndpoint := fmt.Sprintf("/rest/api/2/user?accountId=%s", accountId)
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -75,7 +73,7 @@ func (s *UserService) Get(accountId string) (*User, *Response, error) {
 // Jira API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/user-getUser
 func (s *UserService) GetByAccountIDWithContext(ctx context.Context, accountID string) (*User, *Response, error) {
 	apiEndpoint := fmt.Sprintf("/rest/api/2/user?accountId=%s", accountID)
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -98,7 +96,7 @@ func (s *UserService) GetByAccountID(accountID string) (*User, *Response, error)
 // Jira API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/user-createUser
 func (s *UserService) CreateWithContext(ctx context.Context, user *User) (*User, *Response, error) {
 	apiEndpoint := "/rest/api/2/user"
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, user)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndpoint, user)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -135,7 +133,7 @@ func (s *UserService) Create(user *User) (*User, *Response, error) {
 // Caller must close resp.Body
 func (s *UserService) DeleteWithContext(ctx context.Context, accountId string) (*Response, error) {
 	apiEndpoint := fmt.Sprintf("/rest/api/2/user?accountId=%s", accountId)
-	req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "DELETE", apiEndpoint, nil)
 	if err != nil {
 		return nil, err
 	}
@@ -158,7 +156,7 @@ func (s *UserService) Delete(accountId string) (*Response, error) {
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-user-groups-get
 func (s *UserService) GetGroupsWithContext(ctx context.Context, accountId string) (*[]UserGroup, *Response, error) {
 	apiEndpoint := fmt.Sprintf("/rest/api/2/user/groups?accountId=%s", accountId)
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -181,7 +179,7 @@ func (s *UserService) GetGroups(accountId string) (*[]UserGroup, *Response, erro
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-myself-get
 func (s *UserService) GetSelfWithContext(ctx context.Context) (*User, *Response, error) {
 	const apiEndpoint = "rest/api/2/myself"
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -275,7 +273,7 @@ func (s *UserService) FindWithContext(ctx context.Context, property string, twea
 	}
 
 	apiEndpoint := fmt.Sprintf("/rest/api/2/user/search?%s", queryString[:len(queryString)-1])
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/onpremise/version.go b/onpremise/version.go
index f4a8266..ec4ce2c 100644
--- a/onpremise/version.go
+++ b/onpremise/version.go
@@ -10,9 +10,7 @@ import (
 // VersionService handles Versions for the Jira instance / API.
 //
 // Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/version
-type VersionService struct {
-	client *Client
-}
+type VersionService service
 
 // Version represents a single release version of a project
 type Version struct {
@@ -33,7 +31,7 @@ type Version struct {
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-version-id-get
 func (s *VersionService) GetWithContext(ctx context.Context, versionID int) (*Version, *Response, error) {
 	apiEndpoint := fmt.Sprintf("/rest/api/2/version/%v", versionID)
-	req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
+	req, err := s.client.NewRequest(ctx, "GET", apiEndpoint, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -56,7 +54,7 @@ func (s *VersionService) Get(versionID int) (*Version, *Response, error) {
 // Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-version-post
 func (s *VersionService) CreateWithContext(ctx context.Context, version *Version) (*Version, *Response, error) {
 	apiEndpoint := "/rest/api/2/version"
-	req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, version)
+	req, err := s.client.NewRequest(ctx, "POST", apiEndpoint, version)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -92,7 +90,7 @@ func (s *VersionService) Create(version *Version) (*Version, *Response, error) {
 // Caller must close resp.Body
 func (s *VersionService) UpdateWithContext(ctx context.Context, version *Version) (*Version, *Response, error) {
 	apiEndpoint := fmt.Sprintf("rest/api/2/version/%v", version.ID)
-	req, err := s.client.NewRequestWithContext(ctx, "PUT", apiEndpoint, version)
+	req, err := s.client.NewRequest(ctx, "PUT", apiEndpoint, version)
 	if err != nil {
 		return nil, nil, err
 	}


Test patch:
diff --git a/cloud/auth_transport_basic_test.go b/cloud/auth_transport_basic_test.go
index db4704e..c7db151 100644
--- a/cloud/auth_transport_basic_test.go
+++ b/cloud/auth_transport_basic_test.go
@@ -1,6 +1,7 @@
 package cloud
 
 import (
+	"context"
 	"net/http"
 	"testing"
 )
@@ -29,8 +30,8 @@ func TestBasicAuthTransport(t *testing.T) {
 		Password: password,
 	}
 
-	basicAuthClient, _ := NewClient(tp.Client(), testServer.URL)
-	req, _ := basicAuthClient.NewRequest("GET", ".", nil)
+	basicAuthClient, _ := NewClient(testServer.URL, tp.Client())
+	req, _ := basicAuthClient.NewRequest(context.Background(), "GET", ".", nil)
 	basicAuthClient.Do(req, nil)
 }
 
diff --git a/cloud/auth_transport_cookie_test.go b/cloud/auth_transport_cookie_test.go
index c9d1cfc..14dde2c 100644
--- a/cloud/auth_transport_cookie_test.go
+++ b/cloud/auth_transport_cookie_test.go
@@ -1,6 +1,7 @@
 package cloud
 
 import (
+	"context"
 	"net/http"
 	"net/http/httptest"
 	"testing"
@@ -36,8 +37,8 @@ func TestCookieAuthTransport_SessionObject_Exists(t *testing.T) {
 		SessionObject: []*http.Cookie{testCookie},
 	}
 
-	basicAuthClient, _ := NewClient(tp.Client(), testServer.URL)
-	req, _ := basicAuthClient.NewRequest("GET", ".", nil)
+	basicAuthClient, _ := NewClient(testServer.URL, tp.Client())
+	req, _ := basicAuthClient.NewRequest(context.Background(), "GET", ".", nil)
 	basicAuthClient.Do(req, nil)
 }
 
@@ -72,8 +73,8 @@ func TestCookieAuthTransport_SessionObject_ExistsWithEmptyCookie(t *testing.T) {
 		SessionObject: []*http.Cookie{emptyCookie, testCookie},
 	}
 
-	basicAuthClient, _ := NewClient(tp.Client(), testServer.URL)
-	req, _ := basicAuthClient.NewRequest("GET", ".", nil)
+	basicAuthClient, _ := NewClient(testServer.URL, tp.Client())
+	req, _ := basicAuthClient.NewRequest(context.Background(), "GET", ".", nil)
 	basicAuthClient.Do(req, nil)
 }
 
@@ -113,7 +114,7 @@ func TestCookieAuthTransport_SessionObject_DoesNotExist(t *testing.T) {
 		AuthURL:  ts.URL,
 	}
 
-	basicAuthClient, _ := NewClient(tp.Client(), testServer.URL)
-	req, _ := basicAuthClient.NewRequest("GET", ".", nil)
+	basicAuthClient, _ := NewClient(testServer.URL, tp.Client())
+	req, _ := basicAuthClient.NewRequest(context.Background(), "GET", ".", nil)
 	basicAuthClient.Do(req, nil)
 }
diff --git a/cloud/auth_transport_jwt_test.go b/cloud/auth_transport_jwt_test.go
index e577bb6..2ce50f2 100644
--- a/cloud/auth_transport_jwt_test.go
+++ b/cloud/auth_transport_jwt_test.go
@@ -26,6 +26,6 @@ func TestJWTAuthTransport_HeaderContainsJWT(t *testing.T) {
 		}
 	})
 
-	jwtClient, _ := NewClient(jwtTransport.Client(), testServer.URL)
+	jwtClient, _ := NewClient(testServer.URL, jwtTransport.Client())
 	jwtClient.Issue.Get("TEST-1", nil)
 }
diff --git a/cloud/auth_transport_personal_access_token_test.go b/cloud/auth_transport_personal_access_token_test.go
index 5ad4926..397a8e8 100644
--- a/cloud/auth_transport_personal_access_token_test.go
+++ b/cloud/auth_transport_personal_access_token_test.go
@@ -23,7 +23,7 @@ func TestPATAuthTransport_HeaderContainsAuth(t *testing.T) {
 		}
 	})
 
-	client, _ := NewClient(patTransport.Client(), testServer.URL)
+	client, _ := NewClient(testServer.URL, patTransport.Client())
 	client.User.GetSelf()
 
 }
diff --git a/cloud/error_test.go b/cloud/error_test.go
index cb60c1c..b8e55cd 100644
--- a/cloud/error_test.go
+++ b/cloud/error_test.go
@@ -1,6 +1,7 @@
 package cloud
 
 import (
+	"context"
 	"errors"
 	"fmt"
 	"net/http"
@@ -17,7 +18,7 @@ func TestError_NewJiraError(t *testing.T) {
 		fmt.Fprint(w, `{"errorMessages":["Issue does not exist or you do not have permission to see it."],"errors":{}}`)
 	})
 
-	req, _ := testClient.NewRequest("GET", "/", nil)
+	req, _ := testClient.NewRequest(context.Background(), "GET", "/", nil)
 	resp, _ := testClient.Do(req, nil)
 
 	err := NewJiraError(resp, errors.New("Original http error"))
@@ -51,7 +52,7 @@ func TestError_NoJSON(t *testing.T) {
 		fmt.Fprint(w, `Original message body`)
 	})
 
-	req, _ := testClient.NewRequest("GET", "/", nil)
+	req, _ := testClient.NewRequest(context.Background(), "GET", "/", nil)
 	resp, _ := testClient.Do(req, nil)
 
 	err := NewJiraError(resp, errors.New("Original http error"))
@@ -71,7 +72,7 @@ func TestError_Unauthorized_NilError(t *testing.T) {
 		fmt.Fprint(w, `User is not authorized`)
 	})
 
-	req, _ := testClient.NewRequest("GET", "/", nil)
+	req, _ := testClient.NewRequest(context.Background(), "GET", "/", nil)
 	resp, _ := testClient.Do(req, nil)
 
 	err := NewJiraError(resp, nil)
@@ -90,7 +91,7 @@ func TestError_BadJSON(t *testing.T) {
 		fmt.Fprint(w, `<html>Not JSON</html>`)
 	})
 
-	req, _ := testClient.NewRequest("GET", "/", nil)
+	req, _ := testClient.NewRequest(context.Background(), "GET", "/", nil)
 	resp, _ := testClient.Do(req, nil)
 
 	err := NewJiraError(resp, errors.New("Original http error"))
diff --git a/cloud/jira_test.go b/cloud/jira_test.go
index 65b2272..224af7b 100644
--- a/cloud/jira_test.go
+++ b/cloud/jira_test.go
@@ -2,6 +2,7 @@ package cloud
 
 import (
 	"bytes"
+	"context"
 	"fmt"
 	"io"
 	"net/http"
@@ -36,7 +37,7 @@ func setup() {
 	testServer = httptest.NewServer(testMux)
 
 	// jira client configured to use test server
-	testClient, _ = NewClient(nil, testServer.URL)
+	testClient, _ = NewClient(testServer.URL, nil)
 }
 
 // teardown closes the test HTTP server.
@@ -73,7 +74,7 @@ func testRequestParams(t *testing.T, r *http.Request, want map[string]string) {
 }
 
 func TestNewClient_WrongUrl(t *testing.T) {
-	c, err := NewClient(nil, "://issues.apache.org/jira/")
+	c, err := NewClient("://issues.apache.org/jira/", nil)
 
 	if err == nil {
 		t.Error("Expected an error. Got none")
@@ -87,7 +88,7 @@ func TestNewClient_WithHttpClient(t *testing.T) {
 	httpClient := http.DefaultClient
 	httpClient.Timeout = 10 * time.Minute
 
-	c, err := NewClient(httpClient, testJiraInstanceURL)
+	c, err := NewClient(testJiraInstanceURL, httpClient)
 	if err != nil {
 		t.Errorf("Got an error: %s", err)
 	}
@@ -101,7 +102,7 @@ func TestNewClient_WithHttpClient(t *testing.T) {
 }
 
 func TestNewClient_WithServices(t *testing.T) {
-	c, err := NewClient(nil, testJiraInstanceURL)
+	c, err := NewClient(testJiraInstanceURL, nil)
 
 	if err != nil {
 		t.Errorf("Got an error: %s", err)
@@ -157,14 +158,14 @@ func TestCheckResponse(t *testing.T) {
 }
 
 func TestClient_NewRequest(t *testing.T) {
-	c, err := NewClient(nil, testJiraInstanceURL)
+	c, err := NewClient(testJiraInstanceURL, nil)
 	if err != nil {
 		t.Errorf("An error occurred. Expected nil. Got %+v.", err)
 	}
 
 	inURL, outURL := "rest/api/2/issue/", testJiraInstanceURL+"rest/api/2/issue/"
 	inBody, outBody := &Issue{Key: "MESOS"}, `{"key":"MESOS"}`+"\n"
-	req, _ := c.NewRequest("GET", inURL, inBody)
+	req, _ := c.NewRequest(context.Background(), "GET", inURL, inBody)
 
 	// Test that relative URL was expanded
 	if got, want := req.URL.String(), outURL; got != want {
@@ -179,7 +180,7 @@ func TestClient_NewRequest(t *testing.T) {
 }
 
 func TestClient_NewRawRequest(t *testing.T) {
-	c, err := NewClient(nil, testJiraInstanceURL)
+	c, err := NewClient(testJiraInstanceURL, nil)
 	if err != nil {
 		t.Errorf("An error occurred. Expected nil. Got %+v.", err)
 	}
@@ -188,7 +189,7 @@ func TestClient_NewRawRequest(t *testing.T) {
 
 	outBody := `{"key":"MESOS"}` + "\n"
 	inBody := outBody
-	req, _ := c.NewRawRequest("GET", inURL, strings.NewReader(outBody))
+	req, _ := c.NewRawRequest(context.Background(), "GET", inURL, strings.NewReader(outBody))
 
 	// Test that relative URL was expanded
 	if got, want := req.URL.String(), outURL; got != want {
@@ -212,16 +213,16 @@ func testURLParseError(t *testing.T, err error) {
 }
 
 func TestClient_NewRequest_BadURL(t *testing.T) {
-	c, err := NewClient(nil, testJiraInstanceURL)
+	c, err := NewClient(testJiraInstanceURL, nil)
 	if err != nil {
 		t.Errorf("An error occurred. Expected nil. Got %+v.", err)
 	}
-	_, err = c.NewRequest("GET", ":", nil)
+	_, err = c.NewRequest(context.Background(), "GET", ":", nil)
 	testURLParseError(t, err)
 }
 
 func TestClient_NewRequest_SessionCookies(t *testing.T) {
-	c, err := NewClient(nil, testJiraInstanceURL)
+	c, err := NewClient(testJiraInstanceURL, nil)
 	if err != nil {
 		t.Errorf("An error occurred. Expected nil. Got %+v.", err)
 	}
@@ -232,7 +233,7 @@ func TestClient_NewRequest_SessionCookies(t *testing.T) {
 
 	inURL := "rest/api/2/issue/"
 	inBody := &Issue{Key: "MESOS"}
-	req, err := c.NewRequest("GET", inURL, inBody)
+	req, err := c.NewRequest(context.Background(), "GET", inURL, inBody)
 
 	if err != nil {
 		t.Errorf("An error occurred. Expected nil. Got %+v.", err)
@@ -250,7 +251,7 @@ func TestClient_NewRequest_SessionCookies(t *testing.T) {
 }
 
 func TestClient_NewRequest_BasicAuth(t *testing.T) {
-	c, err := NewClient(nil, testJiraInstanceURL)
+	c, err := NewClient(testJiraInstanceURL, nil)
 	if err != nil {
 		t.Errorf("An error occurred. Expected nil. Got %+v.", err)
 	}
@@ -259,7 +260,7 @@ func TestClient_NewRequest_BasicAuth(t *testing.T) {
 
 	inURL := "rest/api/2/issue/"
 	inBody := &Issue{Key: "MESOS"}
-	req, err := c.NewRequest("GET", inURL, inBody)
+	req, err := c.NewRequest(context.Background(), "GET", inURL, inBody)
 
 	if err != nil {
 		t.Errorf("An error occurred. Expected nil. Got %+v.", err)
@@ -276,11 +277,11 @@ func TestClient_NewRequest_BasicAuth(t *testing.T) {
 // since there is no difference between an HTTP request body that is an empty string versus one that is not set at all.
 // However in certain cases, intermediate systems may treat these differently resulting in subtle errors.
 func TestClient_NewRequest_EmptyBody(t *testing.T) {
-	c, err := NewClient(nil, testJiraInstanceURL)
+	c, err := NewClient(testJiraInstanceURL, nil)
 	if err != nil {
 		t.Errorf("An error occurred. Expected nil. Got %+v.", err)
 	}
-	req, err := c.NewRequest("GET", "/", nil)
+	req, err := c.NewRequest(context.Background(), "GET", "/", nil)
 	if err != nil {
 		t.Fatalf("NewRequest returned unexpected error: %v", err)
 	}
@@ -290,7 +291,7 @@ func TestClient_NewRequest_EmptyBody(t *testing.T) {
 }
 
 func TestClient_NewMultiPartRequest(t *testing.T) {
-	c, err := NewClient(nil, testJiraInstanceURL)
+	c, err := NewClient(testJiraInstanceURL, nil)
 	if err != nil {
 		t.Errorf("An error occurred. Expected nil. Got %+v.", err)
 	}
@@ -323,7 +324,7 @@ func TestClient_NewMultiPartRequest(t *testing.T) {
 }
 
 func TestClient_NewMultiPartRequest_BasicAuth(t *testing.T) {
-	c, err := NewClient(nil, testJiraInstanceURL)
+	c, err := NewClient(testJiraInstanceURL, nil)
 	if err != nil {
 		t.Errorf("An error occurred. Expected nil. Got %+v.", err)
 	}
@@ -363,7 +364,7 @@ func TestClient_Do(t *testing.T) {
 		fmt.Fprint(w, `{"A":"a"}`)
 	})
 
-	req, _ := testClient.NewRequest("GET", "/", nil)
+	req, _ := testClient.NewRequest(context.Background(), "GET", "/", nil)
 	body := new(foo)
 	testClient.Do(req, body)
 
@@ -384,7 +385,7 @@ func TestClient_Do_HTTPResponse(t *testing.T) {
 		fmt.Fprint(w, `{"A":"a"}`)
 	})
 
-	req, _ := testClient.NewRequest("GET", "/", nil)
+	req, _ := testClient.NewRequest(context.Background(), "GET", "/", nil)
 	res, _ := testClient.Do(req, nil)
 	_, err := io.ReadAll(res.Body)
 
@@ -403,7 +404,7 @@ func TestClient_Do_HTTPError(t *testing.T) {
 		http.Error(w, "Bad Request", 400)
 	})
 
-	req, _ := testClient.NewRequest("GET", "/", nil)
+	req, _ := testClient.NewRequest(context.Background(), "GET", "/", nil)
 	_, err := testClient.Do(req, nil)
 
 	if err == nil {
@@ -421,7 +422,7 @@ func TestClient_Do_RedirectLoop(t *testing.T) {
 		http.Redirect(w, r, "/", http.StatusFound)
 	})
 
-	req, _ := testClient.NewRequest("GET", "/", nil)
+	req, _ := testClient.NewRequest(context.Background(), "GET", "/", nil)
 	_, err := testClient.Do(req, nil)
 
 	if err == nil {
@@ -431,22 +432,3 @@ func TestClient_Do_RedirectLoop(t *testing.T) {
 		t.Errorf("Expected a URL error; got %+v.", err)
 	}
 }
-
-func TestClient_GetBaseURL_WithURL(t *testing.T) {
-	u, err := url.Parse(testJiraInstanceURL)
-	if err != nil {
-		t.Errorf("URL parsing -> Got an error: %s", err)
-	}
-
-	c, err := NewClient(nil, testJiraInstanceURL)
-	if err != nil {
-		t.Errorf("Client creation -> Got an error: %s", err)
-	}
-	if c == nil {
-		t.Error("Expected a client. Got none")
-	}
-
-	if b := c.GetBaseURL(); !reflect.DeepEqual(b, *u) {
-		t.Errorf("Base URLs are not equal. Expected %+v, got %+v", *u, b)
-	}
-}
diff --git a/onpremise/auth_transport_basic_test.go b/onpremise/auth_transport_basic_test.go
index 37918f6..e85cf27 100644
--- a/onpremise/auth_transport_basic_test.go
+++ b/onpremise/auth_transport_basic_test.go
@@ -1,6 +1,7 @@
 package onpremise
 
 import (
+	"context"
 	"net/http"
 	"testing"
 )
@@ -29,8 +30,8 @@ func TestBasicAuthTransport(t *testing.T) {
 		Password: password,
 	}
 
-	basicAuthClient, _ := NewClient(tp.Client(), testServer.URL)
-	req, _ := basicAuthClient.NewRequest("GET", ".", nil)
+	basicAuthClient, _ := NewClient(testServer.URL, tp.Client())
+	req, _ := basicAuthClient.NewRequest(context.Background(), "GET", ".", nil)
 	basicAuthClient.Do(req, nil)
 }
 
diff --git a/onpremise/auth_transport_cookie_test.go b/onpremise/auth_transport_cookie_test.go
index 19a33bc..dbf6b2b 100644
--- a/onpremise/auth_transport_cookie_test.go
+++ b/onpremise/auth_transport_cookie_test.go
@@ -1,6 +1,7 @@
 package onpremise
 
 import (
+	"context"
 	"net/http"
 	"net/http/httptest"
 	"testing"
@@ -36,8 +37,8 @@ func TestCookieAuthTransport_SessionObject_Exists(t *testing.T) {
 		SessionObject: []*http.Cookie{testCookie},
 	}
 
-	basicAuthClient, _ := NewClient(tp.Client(), testServer.URL)
-	req, _ := basicAuthClient.NewRequest("GET", ".", nil)
+	basicAuthClient, _ := NewClient(testServer.URL, tp.Client())
+	req, _ := basicAuthClient.NewRequest(context.Background(), "GET", ".", nil)
 	basicAuthClient.Do(req, nil)
 }
 
@@ -72,8 +73,8 @@ func TestCookieAuthTransport_SessionObject_ExistsWithEmptyCookie(t *testing.T) {
 		SessionObject: []*http.Cookie{emptyCookie, testCookie},
 	}
 
-	basicAuthClient, _ := NewClient(tp.Client(), testServer.URL)
-	req, _ := basicAuthClient.NewRequest("GET", ".", nil)
+	basicAuthClient, _ := NewClient(testServer.URL, tp.Client())
+	req, _ := basicAuthClient.NewRequest(context.Background(), "GET", ".", nil)
 	basicAuthClient.Do(req, nil)
 }
 
@@ -113,7 +114,7 @@ func TestCookieAuthTransport_SessionObject_DoesNotExist(t *testing.T) {
 		AuthURL:  ts.URL,
 	}
 
-	basicAuthClient, _ := NewClient(tp.Client(), testServer.URL)
-	req, _ := basicAuthClient.NewRequest("GET", ".", nil)
+	basicAuthClient, _ := NewClient(testServer.URL, tp.Client())
+	req, _ := basicAuthClient.NewRequest(context.Background(), "GET", ".", nil)
 	basicAuthClient.Do(req, nil)
 }
diff --git a/onpremise/auth_transport_jwt_test.go b/onpremise/auth_transport_jwt_test.go
index ccf308b..6332d54 100644
--- a/onpremise/auth_transport_jwt_test.go
+++ b/onpremise/auth_transport_jwt_test.go
@@ -26,6 +26,6 @@ func TestJWTAuthTransport_HeaderContainsJWT(t *testing.T) {
 		}
 	})
 
-	jwtClient, _ := NewClient(jwtTransport.Client(), testServer.URL)
+	jwtClient, _ := NewClient(testServer.URL, jwtTransport.Client())
 	jwtClient.Issue.Get("TEST-1", nil)
 }
diff --git a/onpremise/auth_transport_personal_access_token_test.go b/onpremise/auth_transport_personal_access_token_test.go
index b0ac50f..b205a5f 100644
--- a/onpremise/auth_transport_personal_access_token_test.go
+++ b/onpremise/auth_transport_personal_access_token_test.go
@@ -23,7 +23,7 @@ func TestPATAuthTransport_HeaderContainsAuth(t *testing.T) {
 		}
 	})
 
-	client, _ := NewClient(patTransport.Client(), testServer.URL)
+	client, _ := NewClient(testServer.URL, patTransport.Client())
 	client.User.GetSelf()
 
 }
diff --git a/onpremise/error_test.go b/onpremise/error_test.go
index 4238f64..4aca07d 100644
--- a/onpremise/error_test.go
+++ b/onpremise/error_test.go
@@ -1,6 +1,7 @@
 package onpremise
 
 import (
+	"context"
 	"errors"
 	"fmt"
 	"net/http"
@@ -17,7 +18,7 @@ func TestError_NewJiraError(t *testing.T) {
 		fmt.Fprint(w, `{"errorMessages":["Issue does not exist or you do not have permission to see it."],"errors":{}}`)
 	})
 
-	req, _ := testClient.NewRequest("GET", "/", nil)
+	req, _ := testClient.NewRequest(context.Background(), "GET", "/", nil)
 	resp, _ := testClient.Do(req, nil)
 
 	err := NewJiraError(resp, errors.New("Original http error"))
@@ -51,7 +52,7 @@ func TestError_NoJSON(t *testing.T) {
 		fmt.Fprint(w, `Original message body`)
 	})
 
-	req, _ := testClient.NewRequest("GET", "/", nil)
+	req, _ := testClient.NewRequest(context.Background(), "GET", "/", nil)
 	resp, _ := testClient.Do(req, nil)
 
 	err := NewJiraError(resp, errors.New("Original http error"))
@@ -71,7 +72,7 @@ func TestError_Unauthorized_NilError(t *testing.T) {
 		fmt.Fprint(w, `User is not authorized`)
 	})
 
-	req, _ := testClient.NewRequest("GET", "/", nil)
+	req, _ := testClient.NewRequest(context.Background(), "GET", "/", nil)
 	resp, _ := testClient.Do(req, nil)
 
 	err := NewJiraError(resp, nil)
@@ -90,7 +91,7 @@ func TestError_BadJSON(t *testing.T) {
 		fmt.Fprint(w, `<html>Not JSON</html>`)
 	})
 
-	req, _ := testClient.NewRequest("GET", "/", nil)
+	req, _ := testClient.NewRequest(context.Background(), "GET", "/", nil)
 	resp, _ := testClient.Do(req, nil)
 
 	err := NewJiraError(resp, errors.New("Original http error"))
diff --git a/onpremise/jira_test.go b/onpremise/jira_test.go
index f9a5f6e..1a0b9e1 100644
--- a/onpremise/jira_test.go
+++ b/onpremise/jira_test.go
@@ -2,6 +2,7 @@ package onpremise
 
 import (
 	"bytes"
+	"context"
 	"fmt"
 	"io"
 	"net/http"
@@ -36,7 +37,7 @@ func setup() {
 	testServer = httptest.NewServer(testMux)
 
 	// jira client configured to use test server
-	testClient, _ = NewClient(nil, testServer.URL)
+	testClient, _ = NewClient(testServer.URL, nil)
 }
 
 // teardown closes the test HTTP server.
@@ -73,7 +74,7 @@ func testRequestParams(t *testing.T, r *http.Request, want map[string]string) {
 }
 
 func TestNewClient_WrongUrl(t *testing.T) {
-	c, err := NewClient(nil, "://issues.apache.org/jira/")
+	c, err := NewClient("://issues.apache.org/jira/", nil)
 
 	if err == nil {
 		t.Error("Expected an error. Got none")
@@ -87,7 +88,7 @@ func TestNewClient_WithHttpClient(t *testing.T) {
 	httpClient := http.DefaultClient
 	httpClient.Timeout = 10 * time.Minute
 
-	c, err := NewClient(httpClient, testJiraInstanceURL)
+	c, err := NewClient(testJiraInstanceURL, httpClient)
 	if err != nil {
 		t.Errorf("Got an error: %s", err)
 	}
@@ -101,7 +102,7 @@ func TestNewClient_WithHttpClient(t *testing.T) {
 }
 
 func TestNewClient_WithServices(t *testing.T) {
-	c, err := NewClient(nil, testJiraInstanceURL)
+	c, err := NewClient(testJiraInstanceURL, nil)
 
 	if err != nil {
 		t.Errorf("Got an error: %s", err)
@@ -157,14 +158,14 @@ func TestCheckResponse(t *testing.T) {
 }
 
 func TestClient_NewRequest(t *testing.T) {
-	c, err := NewClient(nil, testJiraInstanceURL)
+	c, err := NewClient(testJiraInstanceURL, nil)
 	if err != nil {
 		t.Errorf("An error occurred. Expected nil. Got %+v.", err)
 	}
 
 	inURL, outURL := "rest/api/2/issue/", testJiraInstanceURL+"rest/api/2/issue/"
 	inBody, outBody := &Issue{Key: "MESOS"}, `{"key":"MESOS"}`+"\n"
-	req, _ := c.NewRequest("GET", inURL, inBody)
+	req, _ := c.NewRequest(context.Background(), "GET", inURL, inBody)
 
 	// Test that relative URL was expanded
 	if got, want := req.URL.String(), outURL; got != want {
@@ -179,7 +180,7 @@ func TestClient_NewRequest(t *testing.T) {
 }
 
 func TestClient_NewRawRequest(t *testing.T) {
-	c, err := NewClient(nil, testJiraInstanceURL)
+	c, err := NewClient(testJiraInstanceURL, nil)
 	if err != nil {
 		t.Errorf("An error occurred. Expected nil. Got %+v.", err)
 	}
@@ -188,7 +189,7 @@ func TestClient_NewRawRequest(t *testing.T) {
 
 	outBody := `{"key":"MESOS"}` + "\n"
 	inBody := outBody
-	req, _ := c.NewRawRequest("GET", inURL, strings.NewReader(outBody))
+	req, _ := c.NewRawRequest(context.Background(), "GET", inURL, strings.NewReader(outBody))
 
 	// Test that relative URL was expanded
 	if got, want := req.URL.String(), outURL; got != want {
@@ -212,16 +213,16 @@ func testURLParseError(t *testing.T, err error) {
 }
 
 func TestClient_NewRequest_BadURL(t *testing.T) {
-	c, err := NewClient(nil, testJiraInstanceURL)
+	c, err := NewClient(testJiraInstanceURL, nil)
 	if err != nil {
 		t.Errorf("An error occurred. Expected nil. Got %+v.", err)
 	}
-	_, err = c.NewRequest("GET", ":", nil)
+	_, err = c.NewRequest(context.Background(), "GET", ":", nil)
 	testURLParseError(t, err)
 }
 
 func TestClient_NewRequest_SessionCookies(t *testing.T) {
-	c, err := NewClient(nil, testJiraInstanceURL)
+	c, err := NewClient(testJiraInstanceURL, nil)
 	if err != nil {
 		t.Errorf("An error occurred. Expected nil. Got %+v.", err)
 	}
@@ -232,7 +233,7 @@ func TestClient_NewRequest_SessionCookies(t *testing.T) {
 
 	inURL := "rest/api/2/issue/"
 	inBody := &Issue{Key: "MESOS"}
-	req, err := c.NewRequest("GET", inURL, inBody)
+	req, err := c.NewRequest(context.Background(), "GET", inURL, inBody)
 
 	if err != nil {
 		t.Errorf("An error occurred. Expected nil. Got %+v.", err)
@@ -250,7 +251,7 @@ func TestClient_NewRequest_SessionCookies(t *testing.T) {
 }
 
 func TestClient_NewRequest_BasicAuth(t *testing.T) {
-	c, err := NewClient(nil, testJiraInstanceURL)
+	c, err := NewClient(testJiraInstanceURL, nil)
 	if err != nil {
 		t.Errorf("An error occurred. Expected nil. Got %+v.", err)
 	}
@@ -259,7 +260,7 @@ func TestClient_NewRequest_BasicAuth(t *testing.T) {
 
 	inURL := "rest/api/2/issue/"
 	inBody := &Issue{Key: "MESOS"}
-	req, err := c.NewRequest("GET", inURL, inBody)
+	req, err := c.NewRequest(context.Background(), "GET", inURL, inBody)
 
 	if err != nil {
 		t.Errorf("An error occurred. Expected nil. Got %+v.", err)
@@ -276,11 +277,11 @@ func TestClient_NewRequest_BasicAuth(t *testing.T) {
 // since there is no difference between an HTTP request body that is an empty string versus one that is not set at all.
 // However in certain cases, intermediate systems may treat these differently resulting in subtle errors.
 func TestClient_NewRequest_EmptyBody(t *testing.T) {
-	c, err := NewClient(nil, testJiraInstanceURL)
+	c, err := NewClient(testJiraInstanceURL, nil)
 	if err != nil {
 		t.Errorf("An error occurred. Expected nil. Got %+v.", err)
 	}
-	req, err := c.NewRequest("GET", "/", nil)
+	req, err := c.NewRequest(context.Background(), "GET", "/", nil)
 	if err != nil {
 		t.Fatalf("NewRequest returned unexpected error: %v", err)
 	}
@@ -290,7 +291,7 @@ func TestClient_NewRequest_EmptyBody(t *testing.T) {
 }
 
 func TestClient_NewMultiPartRequest(t *testing.T) {
-	c, err := NewClient(nil, testJiraInstanceURL)
+	c, err := NewClient(testJiraInstanceURL, nil)
 	if err != nil {
 		t.Errorf("An error occurred. Expected nil. Got %+v.", err)
 	}
@@ -323,7 +324,7 @@ func TestClient_NewMultiPartRequest(t *testing.T) {
 }
 
 func TestClient_NewMultiPartRequest_BasicAuth(t *testing.T) {
-	c, err := NewClient(nil, testJiraInstanceURL)
+	c, err := NewClient(testJiraInstanceURL, nil)
 	if err != nil {
 		t.Errorf("An error occurred. Expected nil. Got %+v.", err)
 	}
@@ -363,7 +364,7 @@ func TestClient_Do(t *testing.T) {
 		fmt.Fprint(w, `{"A":"a"}`)
 	})
 
-	req, _ := testClient.NewRequest("GET", "/", nil)
+	req, _ := testClient.NewRequest(context.Background(), "GET", "/", nil)
 	body := new(foo)
 	testClient.Do(req, body)
 
@@ -384,7 +385,7 @@ func TestClient_Do_HTTPResponse(t *testing.T) {
 		fmt.Fprint(w, `{"A":"a"}`)
 	})
 
-	req, _ := testClient.NewRequest("GET", "/", nil)
+	req, _ := testClient.NewRequest(context.Background(), "GET", "/", nil)
 	res, _ := testClient.Do(req, nil)
 	_, err := io.ReadAll(res.Body)
 
@@ -403,7 +404,7 @@ func TestClient_Do_HTTPError(t *testing.T) {
 		http.Error(w, "Bad Request", 400)
 	})
 
-	req, _ := testClient.NewRequest("GET", "/", nil)
+	req, _ := testClient.NewRequest(context.Background(), "GET", "/", nil)
 	_, err := testClient.Do(req, nil)
 
 	if err == nil {
@@ -421,7 +422,7 @@ func TestClient_Do_RedirectLoop(t *testing.T) {
 		http.Redirect(w, r, "/", http.StatusFound)
 	})
 
-	req, _ := testClient.NewRequest("GET", "/", nil)
+	req, _ := testClient.NewRequest(context.Background(), "GET", "/", nil)
 	_, err := testClient.Do(req, nil)
 
 	if err == nil {
@@ -431,22 +432,3 @@ func TestClient_Do_RedirectLoop(t *testing.T) {
 		t.Errorf("Expected a URL error; got %+v.", err)
 	}
 }
-
-func TestClient_GetBaseURL_WithURL(t *testing.T) {
-	u, err := url.Parse(testJiraInstanceURL)
-	if err != nil {
-		t.Errorf("URL parsing -> Got an error: %s", err)
-	}
-
-	c, err := NewClient(nil, testJiraInstanceURL)
-	if err != nil {
-		t.Errorf("Client creation -> Got an error: %s", err)
-	}
-	if c == nil {
-		t.Error("Expected a client. Got none")
-	}
-
-	if b := c.GetBaseURL(); !reflect.DeepEqual(b, *u) {
-		t.Errorf("Base URLs are not equal. Expected %+v, got %+v", *u, b)
-	}
-}