# 代码托管 API

> 连接 Gitea 或 GitHub 的 Webhook 和推送记录，以及任务关联的 PR 和提交。

原文：https://qunli.co/docs/developers/api/code

由群历 REST API 的 OpenAPI 契约（`openapi/qunli-api.yaml`，qunli-api 提交 `b127eb8`）生成。路径都相对于 `https://qunli.co`；认证见[认证](https://qunli.co/docs/developers/authentication)，响应包装、分页和错误格式见[约定](https://qunli.co/docs/developers/conventions)。字段说明是契约里的英文原文。

## 代码托管

### `GET /api/v1/accounts/{account_id}/git_connections`

List the team's code-hosting connections (Gitea / GitHub webhooks)

Active human team owners/admins only (agents and other members 403): every connection carries its webhook signing secret. Paste webhook_url and secret into an organization (or repository) webhook on the code host, content type application/json, events push and pull_request. Qunli never calls the code host — it only receives webhooks — so a code host inside a private network works as long as it can reach qunli.co.

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Connections, oldest first | `data`: `GitConnection`[] |
| `403` | Authenticated but not permitted. | `ProblemDetails` |

### `POST /api/v1/accounts/{account_id}/git_connections`

Connect a Gitea or GitHub organization

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |

请求体（`application/json`，必填）：

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `git_connection` | object | 是 |  |
| `git_connection.provider` | `gitea` \| `github` | 是 |  |
| `git_connection.name` | string | 是 |  |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `201` | The connection with its webhook URL and secret | `data`: `GitConnection` |
| `400` | Malformed request or invalid parameter. | `ProblemDetails` |
| `403` | Authenticated but not permitted. | `ProblemDetails` |
| `422` | Validation failed. | `ProblemDetails` |

### `GET /api/v1/accounts/{account_id}/git_connections/{id}`

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `id` | 路径 | string (uuid) | 是 |  |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | The connection | `data`: `GitConnection` |
| `403` | Authenticated but not permitted. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |

### `PUT /api/v1/accounts/{account_id}/git_connections/{id}`

Same as PATCH

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `id` | 路径 | string (uuid) | 是 |  |

请求体（`application/json`，必填）：

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `git_connection` | object | 是 |  |
| `git_connection.name` | string | 否 |  |
| `git_connection.status` | `active` \| `disabled` | 否 |  |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | The updated connection | `data`: `GitConnection` |
| `400` | Malformed request or invalid parameter. | `ProblemDetails` |
| `403` | Authenticated but not permitted. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |
| `422` | Validation failed. | `ProblemDetails` |

### `PATCH /api/v1/accounts/{account_id}/git_connections/{id}`

Rename, disable or re-enable a connection

A disabled connection answers its webhooks with 404 and keeps the code already linked to tasks.

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `id` | 路径 | string (uuid) | 是 |  |

请求体（`application/json`，必填）：

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `git_connection` | object | 是 |  |
| `git_connection.name` | string | 否 |  |
| `git_connection.status` | `active` \| `disabled` | 否 |  |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | The updated connection | `data`: `GitConnection` |
| `400` | Malformed request or invalid parameter. | `ProblemDetails` |
| `403` | Authenticated but not permitted. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |
| `422` | Validation failed. | `ProblemDetails` |

### `DELETE /api/v1/accounts/{account_id}/git_connections/{id}`

Remove a connection and the code it linked to tasks

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `id` | 路径 | string (uuid) | 是 |  |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Removed (`{ "data": null }`). | `data`: null |
| `403` | Authenticated but not permitted. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |

### `POST /api/v1/accounts/{account_id}/git_connections/{git_connection_id}/secret`

Issue a new webhook signing secret

Deliveries signed with the previous secret get 401 from now on; update the code host's webhook right away.

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `git_connection_id` | 路径 | string (uuid) | 是 |  |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `201` | The connection with its new secret | `data`: `GitConnection` |
| `403` | Authenticated but not permitted. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |

### `GET /api/v1/accounts/{account_id}/git_connections/{git_connection_id}/deliveries`

Recent webhook deliveries of a connection, newest first

Kept 14 days. Only deliveries with a valid signature for a tracked event (push, pull_request) are listed.

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `git_connection_id` | 路径 | string (uuid) | 是 |  |
| `page` | 查询 | integer | 否 | 默认 `1`。 |
| `limit` | 查询 | integer | 否 | Clamped to 1–100 (default 30). |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Delivery page | `data`: `GitDelivery`[] · `meta`: `PaginationMeta` |
| `403` | Authenticated but not permitted. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |

### `GET /api/v1/accounts/{account_id}/recordings/{recording_id}/code_references`

Pull requests and commits linked to a task

A pull request is linked when its title or description names the task: its canonical URL (https://qunli.co/<account>/T1042, this team only) or its bare ref ("T1042", "#T1042"; uppercase T and at least three digits, resolved in the connection's team). Its head branch's commits follow it (including commits pushed before it was opened, up to 30 days back); a commit whose own message names the task is linked directly. Pull requests come first (newest opened first), then commits (newest first). Empty for anything without linked code. Guests get 403; trashed recordings and others' drafts 404.

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `recording_id` | 路径 | string (ref) | 是 | 引用号，例如 `T1042`，大小写不敏感，可带前导 `#`。 |
| `page` | 查询 | integer | 否 | 默认 `1`。 |
| `limit` | 查询 | integer | 否 | Clamped to 1–200 (default 100). |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Linked code page | `data`: `CodeReference`[] · `meta`: `PaginationMeta` |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `403` | Authenticated but not permitted. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |

## 数据结构

上面的响应用到的对象。字段的类型是另一个对象时，它也列在这里。

### GitConnection

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `id` | string (uuid) | 是 |  |
| `provider` | `gitea` \| `github` | 是 |  |
| `name` | string | 是 |  |
| `status` | `active` \| `disabled` | 是 |  |
| `webhook_url` | string | 是 | Payload URL to paste into the code host's webhook. |
| `secret` | string | 是 | Webhook secret (HMAC-SHA256 signing key) to paste alongside webhook_url. |
| `events` | string[] | 是 | Events to enable on the code host (push, pull_request). |
| `last_delivery_at` | string (date-time) \| null | 是 | Last delivery with a valid signature. |
| `last_event` | string \| null | 是 |  |
| `last_error` | string \| null | 是 | Last unreadable payload, if any. |
| `last_error_at` | string (date-time) \| null | 是 |  |
| `creator` | `UserRef` | 是 |  |
| `created_at` | string (date-time) | 是 |  |
| `updated_at` | string (date-time) | 是 |  |

### GitDelivery

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `id` | string (uuid) | 是 |  |
| `delivery_id` | string | 是 | The code host's delivery id. |
| `event` | `push` \| `pull_request` | 是 |  |
| `status` | `pending` \| `processed` \| `ignored` \| `failed` | 是 |  |
| `error` | string \| null | 是 |  |
| `created_at` | string (date-time) | 是 |  |
| `processed_at` | string (date-time) \| null | 是 |  |

### PaginationMeta

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `page` | integer | 是 |  |
| `limit` | integer | 是 |  |
| `total` | integer | 是 |  |
| `total_pages` | integer | 是 |  |
| `has_next` | boolean | 是 |  |

### CodeReference

A pull request (kind pull_request: number … closed_at set, commit fields null) or a commit (kind commit: sha … committed_at set, pull request fields null) linked to a task.

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `id` | string (uuid) | 是 |  |
| `kind` | `pull_request` \| `commit` | 是 |  |
| `git_connection` | `GitConnectionRef` | 是 |  |
| `repository` | string | 是 | Full name, "org/repo". |
| `url` | string \| null | 是 | On the code host; may be reachable only inside the team's network. |
| `author_login` | string \| null | 是 |  |
| `number` | integer \| null | 否 |  |
| `title` | string \| null | 否 |  |
| `state` | `open` \| `closed` \| `merged` \| null | 否 |  |
| `draft` | boolean \| null | 否 |  |
| `head_branch` | string \| null | 否 |  |
| `base_branch` | string \| null | 否 |  |
| `merge_commit_sha` | string \| null | 否 |  |
| `opened_at` | string (date-time) \| null | 否 |  |
| `merged_at` | string (date-time) \| null | 否 |  |
| `closed_at` | string (date-time) \| null | 否 |  |
| `sha` | string \| null | 否 |  |
| `message` | string \| null | 否 |  |
| `branch` | string \| null | 否 | Branch the commit was first pushed to. |
| `author_name` | string \| null | 否 |  |
| `committed_at` | string (date-time) \| null | 否 |  |
| `pull_request_number` | integer \| null | 否 | The linked pull request that brought this commit; null when its own message links the task. |
| `created_at` | string (date-time) | 是 |  |

### UserRef

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `id` | string (uuid) | 是 | User UUID. |
| `public_id` | string \| null | 是 | Member ref ("U12") in this account. |
| `name` | string \| null | 是 |  |
| `avatar_url` | string \| null | 是 |  |
| `kind` | `human` \| `agent` \| `app` \| null | 是 | null only for an unknown (deleted) user; app is an integration's identity (a code-hosting connection), never a team member. |

### GitConnectionRef

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `id` | string (uuid) | 是 |  |
| `provider` | `gitea` \| `github` | 是 |  |
| `name` | string | 是 |  |
