Repository: prometheus/alertmanager
File: notify/jira/jira.go (Go)

Code before:
	logger.Debug("search for recent issues", "jql", jql.String())

	responseBody, shouldRetry, err := n.doAPIRequestFullPath(ctx, http.MethodPost, searchPath, requestBody)
	if err != nil {
		return nil, shouldRetry, fmt.Errorf("HTTP request to JIRA API: %w", err)
	}

	var issueSearchResult issueSearchResult
	err = json.Unmarshal(responseBody, &issueSearchResult)
	if err != nil {
		return nil, false, err
	}

	if issueSearchResult.Total == 0 {
		logger.Debug("found no existing issue")
		return nil, false, nil
	}

	if issueSearchResult.Total > 1 {
		logger.Warn("more than one issue matched, selecting the most recently resolved", "selected_issue", issueSearchResult.Issues[0].Key)
	}

	return &issueSearchResult.Issues[0], false, nil
}

func (n *Notifier) prepareSearchRequest(jql string) (any, string) {
	requestBody := issueSearch{
		JQL:        jql,
		MaxResults: 2,
		Fields:     []string{"status"},
	}

	// if the API type is datacenter always go for v2
	if n.conf.APIType == "datacenter" {
		searchPath := n.conf.APIURL.JoinPath("/search").String()
		return requestBody, searchPath
	}

	// if the API type is cloud always go for v3
	// if the API type is auto and the URL has an atlassian.net suffix, go for v3
	if n.conf.APIType == "cloud" || n.conf.APIType == "auto" && strings.HasSuffix(n.conf.APIURL.Host, "atlassian.net") {
		searchPath := strings.Replace(n.conf.APIURL.JoinPath("/search/jql").String(), "/2", "/3", 1)
		return requestBody, searchPath
	}

	// if the API type is auto or empty and the URL does not have an atlassian.net suffix, go for v2
	searchPath := n.conf.APIURL.JoinPath("/search").String()
	return requestBody, searchPath
}

func (n *Notifier) getIssueTransitionByName(ctx context.Context, issueKey, transitionName string) (string, bool, error) {

Review comment (suggestion):
```suggestion
// prepareSearchRequest builds the request body and search path for Jira issue search.
//
// Atlassian announced (see https://developer.atlassian.com/changelog/#CHANGE-2046) that
// the legacy /search endpoint is no longer available on Jira Cloud. The replacement
// endpoint (/rest/api/3/search/jql) is currently not available in Jira Data Center.
//
// Selection logic:
//   • If APIType is "datacenter", always use the v2 /search endpoint.
//   • If APIType is "cloud", or if APIType is "auto" and the host ends with
//     "atlassian.net", use the v3 /search/jql endpoint.
//   • Otherwise (APIType is "auto" or empty without an atlassian.net host),
//     use the v2 /search endpoint.
func (n *Notifier) prepareSearchRequest(jql string) (issueSearch, string) {
```



Code after:
	logger.Debug("search for recent issues", "jql", jql.String())

	responseBody, shouldRetry, err := n.doAPIRequestFullPath(ctx, http.MethodPost, searchPath, requestBody)
	if err != nil {
		return nil, shouldRetry, fmt.Errorf("HTTP request to JIRA API: %w", err)
	}

	var issueSearchResult issueSearchResult
	err = json.Unmarshal(responseBody, &issueSearchResult)
	if err != nil {
		return nil, false, err
	}

	if issueSearchResult.Total == 0 {
		logger.Debug("found no existing issue")
		return nil, false, nil
	}

	if issueSearchResult.Total > 1 {
		logger.Warn("more than one issue matched, selecting the most recently resolved", "selected_issue", issueSearchResult.Issues[0].Key)
	}

	return &issueSearchResult.Issues[0], false, nil
}

// prepareSearchRequest builds the request body and search path for Jira issue search.
//
// Atlassian announced (see https://developer.atlassian.com/changelog/#CHANGE-2046) that
// the legacy /search endpoint is no longer available on Jira Cloud. The replacement
// endpoint (/rest/api/3/search/jql) is currently not available in Jira Data Center.
//
// Selection logic:
//   - If APIType is "datacenter", always use the v2 /search endpoint.
//   - If APIType is "cloud", or if APIType is "auto" and the host ends with
//     "atlassian.net", use the v3 /search/jql endpoint.
//   - Otherwise (APIType is "auto" without an atlassian.net host),
//     use the v2 /search endpoint.
func (n *Notifier) prepareSearchRequest(jql string) (issueSearch, string) {
	requestBody := issueSearch{
		JQL:        jql,
		MaxResults: 2,
		Fields:     []string{"status"},
	}

	if n.conf.APIType == "datacenter" {
		searchPath := n.conf.APIURL.JoinPath("/search").String()
		return requestBody, searchPath
	}

	if n.conf.APIType == "cloud" || n.conf.APIType == "auto" && strings.HasSuffix(n.conf.APIURL.Host, "atlassian.net") {
		searchPath := strings.Replace(n.conf.APIURL.JoinPath("/search/jql").String(), "/2", "/3", 1)