---
name: jira-api
description: |
  Team-shareable Jira REST API skill for accessing PingCAP Jira, searching issues,
  understanding key support/oncall/DM projects and custom fields, escalating support
  tickets to GTOC/TCOC, and creating DM tasks safely.
---

# Jira API

Use this skill when operating PingCAP Jira through Atlassian Cloud REST API: authentication, search, project/field discovery, reading support tickets, escalating support tickets to OnCall (`GTOC` / `TCOC`), linking issues, creating DM tasks, and verifying results.

This skill is intended to be **team-shareable**. Do not hard-code personal tokens or personal local paths. Each operator should configure their own credential file or environment variable.

---

## 1. Jira access basics

### Base URL

```text
https://tidb.atlassian.net
```

### REST API base

```text
https://tidb.atlassian.net/rest/api/3
```

### Authentication model

PingCAP Jira uses Atlassian Cloud API token authentication.

REST header pattern:

```http
Authorization: Basic base64(email:atlassian_api_token)
Accept: application/json
Content-Type: application/json
```

### Recommended credential setup

Do **not** store someone else's token in a shared skill.

#### Option A: environment variable

```bash
export JIRA_BASIC_AUTH_RAW='your.email@pingcap.com:your_atlassian_api_token'
```

Python header pattern:

```python
import os
import base64

raw = os.environ["JIRA_BASIC_AUTH_RAW"]
headers = {
    "Authorization": "Basic " + base64.b64encode(raw.encode()).decode(),
    "Accept": "application/json",
    "Content-Type": "application/json",
}
```

#### Option B: local credential file

Each operator can create their own local file, for example:

```text
~/.jira/tidb-atlassian.key
```

File format:

```text
your.email@pingcap.com:your_atlassian_api_token
```

Python header pattern:

```python
import base64
from pathlib import Path

cred_path = Path.home() / ".jira" / "tidb-atlassian.key"
raw = cred_path.read_text().strip()

headers = {
    "Authorization": "Basic " + base64.b64encode(raw.encode()).decode(),
    "Accept": "application/json",
    "Content-Type": "application/json",
}
```

### Security rules

- Never print the raw token.
- Never paste the token into shared docs, tickets, or chat.
- Never commit the credential file.
- If a script logs headers, redact `Authorization`.
- If sharing this skill with team members, keep credential paths as placeholders.

---

## 2. Core REST helper pattern

Minimal Python helper:

```python
import os
import json
import base64
import urllib.request

BASE = "https://tidb.atlassian.net"

raw = os.environ["JIRA_BASIC_AUTH_RAW"]
HEADERS = {
    "Authorization": "Basic " + base64.b64encode(raw.encode()).decode(),
    "Accept": "application/json",
    "Content-Type": "application/json",
}

def jira_get(path):
    req = urllib.request.Request(BASE + path, headers=HEADERS)
    with urllib.request.urlopen(req, timeout=60) as r:
        return json.load(r)

def jira_post(path, body):
    req = urllib.request.Request(
        BASE + path,
        data=json.dumps(body).encode(),
        method="POST",
        headers=HEADERS,
    )
    with urllib.request.urlopen(req, timeout=60) as r:
        if r.status == 204:
            return {"ok": True, "status": 204}
        return json.load(r)

def jira_put(path, body):
    req = urllib.request.Request(
        BASE + path,
        data=json.dumps(body).encode(),
        method="PUT",
        headers=HEADERS,
    )
    with urllib.request.urlopen(req, timeout=60) as r:
        if r.status == 204:
            return {"ok": True, "status": 204}
        return json.load(r)
```

ADF paragraph helper:

```python
def paragraph(text):
    return {
        "type": "paragraph",
        "content": [{"type": "text", "text": text}],
    }
```

---

## 3. Search issues

### Preferred endpoint

Use:

```text
POST /rest/api/3/search/jql
```

Avoid relying on legacy:

```text
GET /rest/api/3/search
```

The legacy endpoint may return `410 Gone` in this Jira Cloud instance.

### Basic search example

```python
body = {
    "jql": "project = TCID ORDER BY updated DESC",
    "maxResults": 20,
    "fields": [
        "summary",
        "issuetype",
        "status",
        "priority",
        "assignee",
        "reporter",
        "created",
        "updated",
        "components",
        "labels",
        "customfield_10053",
        "customfield_12493",
        "comment",
    ],
}

data = jira_post("/rest/api/3/search/jql", body)
```

### Useful JQL examples

Search one project:

```jql
project = TCID ORDER BY updated DESC
```

Search multiple support projects:

```jql
project in (ST, APID, NAID, EMID, JPID, TCID) ORDER BY updated DESC
```

Search TiDB / TiDB Cloud oncall:

```jql
project in (GTOC, TCOC) ORDER BY updated DESC
```

Search by text, such as a cluster id, tenant id, customer name, or error:

```jql
project in (TCID, TCOC, GTOC) AND text ~ "cluster_id_here" ORDER BY updated DESC
```

Search unresolved cases:

```jql
project in (ST, APID, NAID, EMID, JPID, TCID, GTOC, TCOC)
AND statusCategory != Done
ORDER BY updated DESC
```

Search by L2 assignee:

```jql
project in (TCID, TCOC, GTOC)
AND "L2 assignee" is not EMPTY
ORDER BY updated DESC
```

Search DM tasks under an epic:

```jql
project = DM AND parent = DM-11220 ORDER BY updated DESC
```

Search DM by sprint:

```jql
project = DM AND sprint = "Resilience FY27Q1" ORDER BY updated DESC
```

Search issues linked to another issue:

```jql
issue in linkedIssues(TCOC-1234) ORDER BY updated DESC
```

---

## 4. Important projects

| Project | Name | Type | Meaning / typical usage |
|---|---|---|---|
| `ST` | Rapid-ticket | JSM service desk | General rapid/support ticket project. |
| `APID` | APAC-Ticket | JSM service desk | APAC regional customer/support tickets. |
| `NAID` | NA-Ticket | JSM service desk | North America regional customer/support tickets. |
| `EMID` | EMEA-Ticket | JSM service desk | EMEA regional customer/support tickets. |
| `JPID` | Japan-Ticket | JSM service desk | Japan regional customer/support tickets. |
| `TCID` | TiDB-Cloud-Ticket | JSM service desk | TiDB Cloud customer/support tickets. |
| `GTOC` | Global TiDB OnCall | Jira software | Global TiDB oncall / escalation / incident tracking. |
| `TCOC` | TiDBCloud-OnCall | Jira business/software-style project | TiDB Cloud oncall / incident / customer question tracking. |
| `DM` | Development Management | Jira software | Engineering development management: initiatives, epics, stories, tasks, bugs. |

---

## 5. Common issue types by project

### ST

| ID | Name |
|---|---|
| `10002` | New Feature |
| `10003` | Support |
| `10010` | Developer escalation |
| `10001` | Bug |

### APID / NAID / EMID / JPID / TCID

| ID | Name |
|---|---|
| `10003` | Support |
| `10013` | Slack Ticket |
| `10014` | Email Request |

### GTOC

| ID | Name |
|---|---|
| `10029` | Incident |
| `10028` | Customer Question |
| `10008` | Sub-task |

### TCOC

| ID | Name |
|---|---|
| `10029` | Incident |
| `10028` | Customer Question |
| `10007` | Task |
| `10008` | Sub-task |
| `14062` | POC Issues |

### DM

| ID | Name |
|---|---|
| `10021` | Initiative |
| `10000` | Epic |
| `10011` | Story |
| `10007` | Task |
| `10008` | Sub-task |
| `10001` | Bug |

---

## 6. Important standard fields

| Field | API name | Notes |
|---|---|---|
| Summary | `summary` | Short issue title. Some projects may reject non-ASCII summaries. |
| Description | `description` | Jira Cloud API v3 requires Atlassian Document Format (ADF), not plain Markdown. |
| Issue type | `issuetype` | Use either `{ "id": "..." }` or `{ "name": "..." }`; ID is safer. |
| Project | `project` | Usually `{ "key": "TCID" }`. |
| Status | `status` | Read-only in create payload. Use transitions to change status. |
| Priority | `priority` | Use `{ "id": "..." }` or `{ "name": "..." }`; IDs vary by priority scheme. |
| Assignee | `assignee` | Use Jira `accountId`, not email or Feishu open_id. |
| Reporter | `reporter` | Often customer account for JSM tickets. Be careful with customer vs Atlassian account. |
| Components | `components` | Important for routing by product/team, e.g. `TiDB`, `TiKV`, `PD`, `TiDB Cloud`. |
| Labels | `labels` | Useful for durable grouping/search. |
| Parent | `parent` | Modern Jira parent/epic relation, e.g. `{ "key": "DM-11220" }`. |
| Versions | `versions` | Affects versions. Required by some GTOC screens. |
| Issue links | `issuelinks` | Read via issue fields; create via `/rest/api/3/issueLink`. |
| Comments | `comment` | Search can return `comment.total`; fetch bodies separately if needed. |

---

## 7. Important custom fields

Always verify fields in the target Jira instance with:

```text
GET /rest/api/3/field
```

Known important custom fields:

| Custom field | Name | Type | Common usage |
|---|---|---|---|
| `customfield_10053` | `L2 assignee` | User picker | Support/oncall analysis in `TCID`, `TCOC`, `GTOC`, sometimes regional ticket projects. Use this for L2 attribution, not worklog author. |
| `customfield_12493` | `Customer's Expectation in Time` | Date | Customer expected date / deadline, often on FRM or support-related issues. |
| `customfield_13303` | `ETA` | Date | Used in TCOCP / follow-up filters. Important: this is ETA, not priority. |
| `customfield_10020` | `Sprint` | GreenHopper sprint | DM sprint field. During create it may require a number, not an array. |
| `customfield_10014` | `Epic Link` | Legacy epic link | Legacy Jira epic field. For modern Jira also set `parent`. |
| `customfield_10146` | `Environment(TCOC)` | Array option | Required on TCOC oncall create. |
| `customfield_10149` | `Cluster ID` | String | Required on TCOC oncall create. |
| `customfield_10152` | `TICKET issue key (migrated)` | String | Required on GTOC/TCOC create; use source support ticket key. |
| `customfield_10157` | `Tenant ID` | String | Required on TCOC oncall create. |
| `customfield_10158` | `Severity (migrated)` | Option | Required on GTOC/TCOC create. |
| `customfield_10164` | `Tier` | Option | Required on TCOC oncall create. |
| `customfield_10211` | `Environment(GTOC)` | Option | Required on GTOC oncall create. |
| `customfield_10213` | `Account Level (migrated)` | String | Required on GTOC oncall create. |
| `customfield_10217` | `Account Code (migrated)` | String | Required on GTOC oncall create. |
| `customfield_10223` | `Alert Required?` | Option | Required on GTOC oncall create. |
| `customfield_10205` | `Security Verification` | Select | Required for some DM tasks. Known-good value: `{ "id": "10298" }` = `Not Required`. |
| `customfield_15835` | `TiDB-X kernel version` | Cascading select | Required for some DM tasks. Known-good value: `{ "id": "16257" }` = `TiDB Cloud infra only`. |
| `customfield_12482` | `EG team` | Multi-select | Required for some DM tasks. Known-good support value: `[{"id": "13948"}]` = `II-DevOps Service`. |
| `customfield_10183` | `Affects metering and billing` | Radio button | Required for some DM tasks. Known-good value: `{ "id": "10328" }` = `No`. Other observed value: `{ "id": "10330" }` = `Yes, Approved`. |
| `customfield_13765` | `Product Plan(s)` | Multi-checkbox | Required for some DM tasks. Known-good value: `[{"id": "13949"}]` = `NA`. |

---

## 8. Priority values

Observed priority IDs:

| ID | Name |
|---|---|
| `10006` | P0 |
| `10007` | P1 |
| `10008` | P2 |
| `10009` | P3 |
| `10010` | P4 |
| `10011` | Critical |
| `10012` | Major |
| `10013` | Warning |
| `10014` | Undefined |
| `1` | Highest |
| `2` | High |
| `3` | Medium |
| `4` | Low |
| `5` | Lowest |

Caution:

- Some projects use `P0/P1/P2/P3/P4`.
- Some JSM projects use `Highest/High/Medium/Low/Lowest`.
- Always inspect an existing issue in the same project before bulk updates.

---

## 9. Useful components

Components are important for support routing and oncall aggregation.

Common component examples across ticket/oncall projects:

```text
TiDB
TiKV
PD
TiFlash
TiCDC
TiDB Cloud
TiDB Compute
TiDB Optimizer
TiDB SQL
TiDB Transaction
TiDB Lightning
TiDB Operator
DM
BR
Clinic
Dashboard
Billing
Account
DBaaS Frontend
Cloud Service
Cluster Service
```

For `GTOC` / `TCOC` team aggregation, prefer `components` over free-text assumptions.

Do not infer team hierarchy from fields like `Engineer Leader` unless explicitly requested and verified.

---

## 10. Reading one issue

Use:

```text
GET /rest/api/3/issue/{KEY}
```

Recommended fields:

```python
fields = [
    "summary",
    "description",
    "issuetype",
    "project",
    "status",
    "priority",
    "assignee",
    "reporter",
    "created",
    "updated",
    "components",
    "labels",
    "parent",
    "issuelinks",
    "customfield_10053",
    "customfield_10020",
    "customfield_10014",
    "customfield_12493",
    "customfield_13303",
]

key = "TCID-12849"
issue = jira_get(f"/rest/api/3/issue/{key}?fields={','.join(fields)}")
```

---

## 11. Escalate a support ticket to OnCall

There is usually **no need to create a new support ticket** through this skill.

The common workflow is:

1. Start from an existing support ticket:
   - `ST`
   - `APID`
   - `NAID`
   - `EMID`
   - `JPID`
   - `TCID`
2. Read the source ticket.
3. Check whether it already has an OnCall link to avoid duplicate escalation.
4. Decide whether to escalate to:
   - `GTOC` — Global TiDB OnCall
   - `TCOC` — TiDB Cloud OnCall
5. Create an OnCall issue in `GTOC` or `TCOC`.
6. Link the OnCall issue back to the source support ticket.
7. Verify both source ticket and OnCall issue.

### 11.1 Source support ticket projects

| Project | Meaning |
|---|---|
| `ST` | Rapid-ticket / general support ticket |
| `APID` | APAC regional support ticket |
| `NAID` | North America regional support ticket |
| `EMID` | EMEA regional support ticket |
| `JPID` | Japan regional support ticket |
| `TCID` | TiDB Cloud support ticket |

Typical source ticket issue types:

| Issue Type ID | Name |
|---|---|
| `10003` | Support |
| `10013` | Slack Ticket |
| `10014` | Email Request |

### 11.2 Read source support ticket first

Before escalation, always read the source ticket.

```python
source_key = "TCID-12345"

fields = [
    "summary",
    "description",
    "issuetype",
    "project",
    "status",
    "priority",
    "assignee",
    "reporter",
    "created",
    "updated",
    "components",
    "labels",
    "customfield_10053",  # L2 assignee
    "customfield_12493",  # Customer's Expectation in Time
    "issuelinks",
]

source = jira_get(f"/rest/api/3/issue/{source_key}?fields={','.join(fields)}")
```

Use the source ticket to extract:

- customer / account context
- issue summary
- impact
- product component
- cluster id / tenant id if available
- priority / severity
- L2 assignee if available
- existing links to avoid duplicate escalation

### 11.3 Decide GTOC vs TCOC

| Target | Use when |
|---|---|
| `GTOC` | TiDB / self-managed / database kernel / non-Cloud global oncall escalation |
| `TCOC` | TiDB Cloud / DBaaS / tenant / cluster / Cloud platform escalation |

Examples:

- TiDB SQL / TiKV / PD / TiFlash / BR issue for non-Cloud customer → usually `GTOC`.
- TiDB Cloud cluster / tenant / DBaaS / billing / account / serverless / dedicated cluster issue → usually `TCOC`.
- If the source is `TCID`, default to `TCOC` unless the issue is clearly kernel-only and should go to `GTOC`.

---

## 12. Create GTOC OnCall issue

### GTOC project info

| Item | Value |
|---|---|
| Project key | `GTOC` |
| Project name | `Global TiDB OnCall` |
| Project type | Jira software |
| Common issue type | `10029` = `Incident` |
| Alternative issue type | `10028` = `Customer Question` |

### Required fields observed for GTOC Incident / Customer Question

For `GTOC` issue types `10029` / `10028`, createmeta shows these required fields:

| Field | API field | Required | Notes |
|---|---|---|---|
| Summary | `summary` | Yes | Short escalation title |
| Project | `project` | Yes | `{ "key": "GTOC" }` |
| Issue type | `issuetype` | Yes | Usually `{ "id": "10029" }` |
| Components | `components` | Yes | Product area, e.g. `TiDB`, `TiKV`, `PD`, `BR` |
| Reporter | `reporter` | Yes | Jira account ID |
| Affects versions | `versions` | Yes | Pick known version if available; otherwise verify allowed value |
| TICKET issue key (migrated) | `customfield_10152` | Yes | Put source support ticket key, e.g. `TCID-12345` |
| Severity (migrated) | `customfield_10158` | Yes | S1/S2/S3/S4/None |
| Environment(GTOC) | `customfield_10211` | Yes | production / pre-production / poc / test |
| Account Level (migrated) | `customfield_10213` | Yes | String |
| Account Code (migrated) | `customfield_10217` | Yes | String |
| Alert Required? | `customfield_10223` | Yes | Yes / No |

Known values:

```json
"customfield_10158": { "id": "10210" }  // S1 - Service Unavailable
"customfield_10158": { "id": "10211" }  // S2 - Significant Impact
"customfield_10158": { "id": "10212" }  // S3 - Non-critical issue
"customfield_10158": { "id": "10213" }  // S4 - Trivial issue or enhancement suggestion
"customfield_10158": { "id": "10214" }  // None

"customfield_10211": { "id": "10413" }  // production
"customfield_10211": { "id": "10416" }  // pre-production
"customfield_10211": { "id": "10418" }  // poc
"customfield_10211": { "id": "10420" }  // test
"customfield_10211": { "id": "10422" }  // pre-production(预生产/灰度)

"customfield_10223": { "id": "10352" }  // Yes
"customfield_10223": { "id": "10353" }  // No
```

### GTOC create payload skeleton

```python
source_key = "APID-12345"

body = {
    "fields": {
        "project": {"key": "GTOC"},
        "issuetype": {"id": "10029"},  # Incident
        "summary": "[Escalation] Short issue title from source ticket",
        "description": {
            "type": "doc",
            "version": 1,
            "content": [
                paragraph(f"Source ticket: {source_key}"),
                paragraph("Customer impact: ..."),
                paragraph("Problem summary: ..."),
                paragraph("Observed behavior: ..."),
                paragraph("Expected behavior: ..."),
                paragraph("Evidence / logs / metrics: ..."),
                paragraph("Requested oncall help: ..."),
            ],
        },
        "components": [{"name": "TiDB"}],

        # Required GTOC fields
        "customfield_10152": source_key,
        "customfield_10158": {"id": "10212"},  # S3 by default; adjust based on impact
        "customfield_10211": {"id": "10413"},  # production; adjust if needed
        "customfield_10213": "Unknown",        # account level if not known
        "customfield_10217": "Unknown",        # account code if not known
        "customfield_10223": {"id": "10353"},  # Alert Required? No by default

        # Required by GTOC create screen. Pick real affected version if known.
        "versions": [{"name": "master"}],
    }
}

created = jira_post("/rest/api/3/issue", body)
```

Caution:

- `versions` is required in GTOC createmeta. Do not guess if version matters; inspect source ticket or ask owner.
- For unknown account fields, use a clear placeholder like `Unknown` only if Jira accepts it and escalation should not block.
- Always verify created fields after creation.

---

## 13. Create TCOC OnCall issue

### TCOC project info

| Item | Value |
|---|---|
| Project key | `TCOC` |
| Project name | `TiDBCloud-OnCall` |
| Common issue type | `10029` = `Incident` |
| Alternative issue type | `10028` = `Customer Question` |
| Alternative issue type | `10007` = `Task` |

### Required fields observed for TCOC Incident / Customer Question / Task

For `TCOC` issue types `10029`, `10028`, and `10007`, createmeta shows these required fields:

| Field | API field | Required | Notes |
|---|---|---|---|
| Summary | `summary` | Yes | Short escalation title |
| Project | `project` | Yes | `{ "key": "TCOC" }` |
| Issue type | `issuetype` | Yes | Usually `10029` = Incident |
| Components | `components` | Yes | Cloud component, e.g. `TiDB Cloud`, `Cluster Service`, `Billing`, `Account` |
| Environment(TCOC) | `customfield_10146` | Yes | Array field |
| Cluster ID | `customfield_10149` | Yes | String |
| TICKET issue key (migrated) | `customfield_10152` | Yes | Source ticket key, e.g. `TCID-12345` |
| Tenant ID | `customfield_10157` | Yes | String |
| Severity (migrated) | `customfield_10158` | Yes | S1/S2/S3/S4/None |
| Tier | `customfield_10164` | Yes | Serverless / Dedicated / TiDB X |

Known values:

```json
"customfield_10146": [{ "id": "10195" }]  // DBaas System Control Panel
"customfield_10146": [{ "id": "10196" }]  // POC/On-Demand Customer Cluster
"customfield_10146": [{ "id": "10197" }]  // Serverless TIER Cluster
"customfield_10146": [{ "id": "10198" }]  // Customer STG (DRILL)
"customfield_10146": [{ "id": "10199" }]  // OTHER

"customfield_10158": { "id": "10210" }    // S1 - Service Unavailable
"customfield_10158": { "id": "10211" }    // S2 - Significant Impact
"customfield_10158": { "id": "10212" }    // S3 - Non-critical issue
"customfield_10158": { "id": "10213" }    // S4 - Trivial issue or enhancement suggestion
"customfield_10158": { "id": "10214" }    // None

"customfield_10164": { "id": "10215" }    // Serverless
"customfield_10164": { "id": "10216" }    // Dedicated
"customfield_10164": { "id": "13880" }    // TiDB X
```

### TCOC create payload skeleton

```python
source_key = "TCID-12345"

body = {
    "fields": {
        "project": {"key": "TCOC"},
        "issuetype": {"id": "10029"},  # Incident
        "summary": "[Escalation] Short issue title from source ticket",
        "description": {
            "type": "doc",
            "version": 1,
            "content": [
                paragraph(f"Source ticket: {source_key}"),
                paragraph("Customer impact: ..."),
                paragraph("Tenant ID: ..."),
                paragraph("Cluster ID: ..."),
                paragraph("Tier: ..."),
                paragraph("Problem summary: ..."),
                paragraph("Observed behavior: ..."),
                paragraph("Expected behavior: ..."),
                paragraph("Evidence / logs / metrics: ..."),
                paragraph("Requested oncall help: ..."),
            ],
        },
        "components": [{"name": "TiDB Cloud"}],

        # Required TCOC fields
        "customfield_10146": [{"id": "10199"}],  # Environment(TCOC): OTHER; adjust if known
        "customfield_10149": "unknown",          # Cluster ID
        "customfield_10152": source_key,          # Source support ticket key
        "customfield_10157": "unknown",          # Tenant ID
        "customfield_10158": {"id": "10212"},    # S3 by default; adjust based on impact
        "customfield_10164": {"id": "10216"},    # Dedicated by default; adjust if Serverless/TiDB X
    }
}

created = jira_post("/rest/api/3/issue", body)
```

Caution:

- Do not invent `Tenant ID` or `Cluster ID`.
- If unknown but escalation must proceed, use explicit placeholder text like `unknown` and include the uncertainty in description.
- If the support ticket contains cluster/tenant info, copy it exactly.
- For TiDB Cloud incidents, `TCOC` usually requires these Cloud-specific fields even if the UI looks simpler.

---

## 14. Link OnCall issue back to source support ticket

After creating `GTOC` or `TCOC`, link it back to the source support ticket.

First inspect existing link types if unsure:

```text
GET /rest/api/3/issueLinkType
```

Known useful link type:

```json
{
  "type": {"name": "Ticket-Oncall"},
  "outwardIssue": {"key": "TCID-12345"},
  "inwardIssue": {"key": "TCOC-5678"}
}
```

Python:

```python
link_body = {
    "type": {"name": "Ticket-Oncall"},
    "outwardIssue": {"key": source_key},
    "inwardIssue": {"key": oncall_key},
}

jira_post("/rest/api/3/issueLink", link_body)
```

For source support ticket → GTOC:

```json
{
  "type": {"name": "Ticket-Oncall"},
  "outwardIssue": {"key": "APID-12345"},
  "inwardIssue": {"key": "GTOC-6789"}
}
```

For source support ticket → TCOC:

```json
{
  "type": {"name": "Ticket-Oncall"},
  "outwardIssue": {"key": "TCID-12345"},
  "inwardIssue": {"key": "TCOC-6789"}
}
```

Always verify link direction:

```python
source = jira_get(f"/rest/api/3/issue/{source_key}?fields=issuelinks")
oncall = jira_get(f"/rest/api/3/issue/{oncall_key}?fields=issuelinks")
```

Expected result:

- Source ticket has a `Ticket-Oncall` link to the OnCall issue.
- OnCall issue has a reciprocal link back to the source support ticket.

---

## 15. Escalation verification checklist

After escalation:

- [ ] Source support ticket was read first.
- [ ] Duplicate existing OnCall link was checked.
- [ ] Correct target project selected:
  - `GTOC` for TiDB / kernel / non-Cloud oncall
  - `TCOC` for TiDB Cloud / tenant / cluster oncall
- [ ] Required fields were populated.
- [ ] Description includes:
  - source ticket key
  - customer impact
  - observed behavior
  - expected behavior
  - evidence
  - requested oncall action
- [ ] OnCall issue was created successfully.
- [ ] Source ticket and OnCall issue were linked with `Ticket-Oncall`.
- [ ] Both issues were re-read after creation/linking.
- [ ] Final response includes both browse URLs:

```text
https://tidb.atlassian.net/browse/{SOURCE_KEY}
https://tidb.atlassian.net/browse/{ONCALL_KEY}
```

---

## 16. Creating DM tasks

DM is different from support projects. It often requires specific custom fields.

### Common DM project info

| Item | Value |
|---|---|
| Project key | `DM` |
| Project name | `Development Management` |
| Common Task issue type | `10007` = `Task` |
| Epic issue type | `10000` = `Epic` |
| Bug issue type | `10001` = `Bug` |

### Known DM required fields for Task

For many DM Task creations, Jira rejects the request unless these are set:

```json
{
  "customfield_10205": {"id": "10298"},
  "customfield_15835": {"id": "16257"},
  "customfield_12482": [{"id": "13948"}],
  "customfield_10183": {"id": "10328"},
  "customfield_13765": [{"id": "13949"}]
}
```

Meanings:

| Field | Meaning | Known-good value |
|---|---|---|
| `customfield_10205` | Security Verification | `{ "id": "10298" }` = `Not Required` |
| `customfield_15835` | TiDB-X kernel version | `{ "id": "16257" }` = `TiDB Cloud infra only` |
| `customfield_12482` | EG team | `[{"id": "13948"}]` = `II-DevOps Service` |
| `customfield_10183` | Affects metering and billing | `{ "id": "10328" }` = `No` |
| `customfield_13765` | Product Plan(s) | `[{"id": "13949"}]` = `NA` |

### Common DM epics and sprints

Known useful values:

| Purpose | Key / Name | Notes |
|---|---|---|
| Support ticket system epic | `DM-1269` — `Support: Ticket system` | Used for support-ticket-system work. |
| Incident management epic | `DM-11220` — `INCIDENT MGMT` | Used for incident-management requirements. |
| Sprint | `Resilience FY27Q1` | Known sprint name. |
| Sprint ID | `3582104` | Use as number during create: `"customfield_10020": 3582104`. |

Before using these, verify they are still current:

```jql
project = DM AND issuetype = Epic AND summary ~ "incident" ORDER BY updated DESC
```

or:

```jql
project = DM AND sprint = "Resilience FY27Q1" ORDER BY updated DESC
```

### Create a DM task under an epic

```python
body = {
    "fields": {
        "project": {"key": "DM"},
        "issuetype": {"id": "10007"},
        "summary": "Auto-generate event timeline",
        "parent": {"key": "DM-11220"},
        "description": {
            "type": "doc",
            "version": 1,
            "content": [
                paragraph("Chinese title: 自动生成 event timeline"),
                paragraph("Requirement: ..."),
                paragraph("Acceptance criteria: ..."),
            ],
        },
        "customfield_10205": {"id": "10298"},
        "customfield_15835": {"id": "16257"},
        "customfield_12482": [{"id": "13948"}],
        "customfield_10183": {"id": "10328"},
        "customfield_13765": [{"id": "13949"}],
    }
}

created = jira_post("/rest/api/3/issue", body)
```

### Create a DM task under an epic and sprint

```python
body["fields"].update({
    "parent": {"key": "DM-1269"},
    "customfield_10014": "DM-1269",
    "customfield_10020": 3582104,
})
```

Important:

- `customfield_10020` may require a **number**, not an array.
- This worked:

```json
"customfield_10020": 3582104
```

- This may fail:

```json
"customfield_10020": [3582104]
```

Typical error:

```text
Number value expected as the Sprint id.
The Sprint (id) must be a number.
```

### DM summary caution

DM summary may reject Chinese or non-ASCII text.

If create fails with a regex error, use an English summary and preserve Chinese text in description.

Example:

```text
Summary: Auto-generate event timeline
Description: 中文标题：自动生成 event timeline
```

### DM due date caution

`duedate` may fail:

```text
Field 'duedate' cannot be set. It is not on the appropriate screen, or unknown.
```

Fallback:

- Put target date in the ADF description.
- Or use another available date field if verified.

---

## 17. User and account lookup

### Search user

```text
GET /rest/api/3/user/search?query={email_or_name}
```

Important:

- Jira may return both `customer` and `atlassian` accounts.
- For assignee, project role, group, and internal Jira operations, use active `accountType = atlassian`.
- Customer accounts are portal identities and may not be usable for internal assignment or group membership.

### Assignee format

```json
"assignee": {
  "accountId": "712020:..."
}
```

Do not use:

- Feishu `ou_...`
- Email directly in issue create payload
- Display name

---

## 18. Project access / JSM access

Support ticket projects usually require more than Jira Software access.

Common layers:

1. Jira Software seat
2. JSM agent seat
3. Project-level JSM role membership

Important groups / roles observed:

| Item | Meaning |
|---|---|
| `jira-software-users-pingcap-ticket` | Jira Software access; not enough for JSM support projects. |
| `jira-servicemanagement-users-pingcap-ticket` | JSM agent seat group. |
| `Service Desk Team` | Project-level role, commonly role id `10005`. Required for many ticket projects. |

Common ticket projects needing `Service Desk Team` role:

```text
APID
NAID
JPID
EMID
ST
TCID
```

Verify effective permission:

```text
GET /rest/api/3/user/permission/search?query={email}&permissions=BROWSE_PROJECTS,CREATE_ISSUES&projectKey={PROJECT}
```

Do not report "access granted" based only on group membership. Verify project role and effective permission.

---

## 19. Final verification checklist

After any create/update/link operation:

- Re-read the issue by key.
- Verify:
  - `summary`
  - `issuetype`
  - `status`
  - `priority`
  - `assignee`
  - `components`
  - `labels`
  - `parent` / epic
  - `customfield_10020` sprint
  - relevant custom fields
  - issue links
- Include the browse URL in the final result:

```text
https://tidb.atlassian.net/browse/{KEY}
```

---

## 20. Common pitfalls

- Jira Cloud API v3 description is ADF, not Markdown.
- `POST /rest/api/3/search/jql` is preferred over legacy search.
- There is usually no need to create support tickets from scratch; the common flow is support ticket → OnCall escalation.
- TCOC/GTOC create screens have required custom fields; inspect createmeta before assuming a minimal payload will work.
- `customfield_10152` should contain the source ticket key when escalating to OnCall.
- DM Task creation often requires custom fields even when the UI hides them.
- DM `summary` may reject Chinese; put Chinese text in description.
- Sprint field may require a number, not an array.
- `duedate` may not be settable on some screens.
- `customfield_13303` is `ETA`, not priority.
- Jira `priority` field is separate from labels like `priority-p1`.
- Feishu `ou_...` IDs are not Jira account IDs.
- Customer accounts are not the same as Atlassian employee accounts.
- Always verify with a fresh read after writes.
