> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cloudthinker.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Thêm nguồn tín hiệu tùy chỉnh

> Kết nối bất kỳ hệ thống nào có thể POST JSON đến Resolve, ghi nhận một sự kiện thực, và ánh xạ các trường của nó bằng cách nhấn vào chúng.

Nguồn tùy chỉnh dùng để kết nối một hệ thống không có preset trong Resolve: một công cụ kiểm tra uptime nội bộ, một cron job, một script deploy, hoặc một nhà cung cấp mà CloudThinker chưa liệt kê. Nếu hệ thống đó có thể gửi HTTP POST với body JSON, các sự kiện của nó có thể mở sự cố và kích hoạt phân tích nguyên nhân gốc (RCA).

## Cách hoạt động

Bạn tạo một endpoint, gửi đến đó một sự kiện thực, rồi nhấn vào các trường trong sự kiện đó để cho Resolve biết trường nào là tiêu đề, mức độ nghiêm trọng, v.v. Vì bạn chọn từ một payload mà hệ thống của bạn thực sự đã gửi, mọi ánh xạ bạn lưu đều trỏ đến một trường có tồn tại.

Trong [Pulse](https://docs.cloudthinker.io/vi/guide/pulse/overview.md), mỗi sự kiện được lọc nhiễu trước. Những gì còn lại là một **signal**, được nhóm với các signal liên quan và chuyển vào một sự cố khi cần xử lý.

> [!NOTE]
>
> Nguồn tùy chỉnh không giống với webhook hội thoại. Nguồn tùy chỉnh cung cấp dữ liệu cho Pulse và mở sự cố. Nếu bạn muốn sự kiện bắt đầu một cuộc chat với CloudThinker, hãy dùng [webhook](https://docs.cloudthinker.io/vi/guide/webhooks/overview.md).

## Điều kiện tiên quyết

* Vai trò **Admin** của workspace. Developer và Viewer có thể xem nguồn nhưng không thể thêm hay thay đổi. Xem [Vai trò](https://docs.cloudthinker.io/vi/guide/security/roles.md).
* Một hệ thống có thể gửi request `POST` với body là JSON object. Nguồn mới yêu cầu header `Authorization`. Nếu hệ thống của bạn không thể đặt header, xem [Tùy chọn xác thực](#tùy-chọn-xác-thực).
* Một cách để kích hoạt một sự kiện tiêu biểu theo yêu cầu, chẳng hạn nút test hoặc lệnh `curl`.

## Tạo nguồn

Vào **Resolve → Integrations**, mở thẻ **Custom source**, và nhấn **Add signal**. Mọi nguồn tùy chỉnh bạn tạo đều được liệt kê trên thẻ này.

**1. Đặt tên nguồn**

Nhập **Source name** mà nhóm của bạn sẽ nhận ra, ví dụ `Acme uptime`, và nhấn **Create endpoint**.

**2. Sao chép URL và secret**

Hộp thoại hiển thị **Webhook URL** và **Secret Key**. Sao chép cả hai vào hệ thống của bạn ngay.

**Secret Key** chỉ hiển thị một lần. Nếu bạn làm mất, xem [Xoay vòng hoặc thay thế thông tin xác thực](#xoay-vòng-hoặc-thay-thế-thông-tin-xác-thực).

**3. Gửi một sự kiện thực**

> [!WARNING]
>
> Sự kiện ghi nhận đi qua Pulse như mọi sự kiện khác, và **Auto-investigate** đã được bật. Nếu Pulse đánh giá sự kiện này cần xử lý, bước phân loại có thể mở một sự cố và bắt đầu điều tra. Để giảm khả năng đó, hãy thêm `"title": "Test notification, ignore"` và `"severity": "info"` ở cấp cao nhất vào body ghi nhận, cạnh các trường thực của bạn. Cho đến khi bạn lưu ánh xạ, Resolve đọc hai key đó, và Pulse coi thông báo test là không cần xử lý. Cách này không đảm bảo tuyệt đối: nếu sự cố vẫn được mở, hãy đóng sự cố đó.
>
> Hai key đó xuất hiện trong bộ chọn. Đừng ánh xạ chúng. Thay vào đó hãy chọn các trường thực của bạn, chẳng hạn `event.data.title`.

Kích hoạt sự kiện từ hệ thống của bạn. Hộp thoại chờ và tự cập nhật khi sự kiện đến. Nếu hộp thoại không cập nhật sau khi bạn gửi, hãy gửi lại và nhấn **Check again**.

Gửi một sự kiện tiêu biểu, không phải body test trống. Bạn chỉ có thể ánh xạ các trường mà sự kiện này chứa.

**4. Ánh xạ các trường**

Khi hộp thoại hiển thị **Payload received**, nhấn **Map the fields**. Mọi trường trong JSON đã ghi nhận được liệt kê cùng giá trị. Chọn trường cho **Incident title**, rồi bất kỳ trường nào trong **Severity**, **Service**, **Resource** và **Description**. Nhấn **Save mapping**. Hộp thoại đóng lại.

Các trường bên trong một danh sách hiển thị kèm chỉ số, chẳng hạn `alerts[0].labels.severity`, cho ba phần tử đầu tiên. Ở đây bạn chỉ có thể chọn từ sự kiện đã ghi nhận. Để dùng một đường dẫn mà sự kiện không chứa, hãy nhập nó vào trường tương ứng trong **Edit**, nơi mọi đường dẫn là một trường văn bản.

**Resource** ở đây chỉ là tên hiển thị (màn hình Edit gọi nó là **Resource Name Path**). Pulse nhóm sự kiện bằng **Resource ID Path**, mà bạn đặt ở bước tiếp theo.

Nếu một sự kiện sau này thiếu một trường đã ánh xạ, Resolve dùng giá trị dự phòng: tiêu đề dùng key `title` cấp cao nhất hoặc `Webhook signal`, mức độ nghiêm trọng dùng key `severity` hoặc `priority` cấp cao nhất rồi đến Medium, dịch vụ dùng `unknown`, và timestamp dùng thời điểm nhận.

**5. Hoàn tất trong Edit**

Mở menu **...** của nguồn trong **Signals to Resolve** và chọn **Edit**. Đặt ba trường sau, vốn không có trong hộp thoại thiết lập:

* **Resource ID Path**: một định danh ổn định, chẳng hạn host ID hoặc check ID. Pulse dùng nó để nhóm các lần lặp lại của cùng một lỗi, kể cả khi tiêu đề khác nhau. Nếu không có, sự kiện chỉ được nhóm theo tiêu đề hoặc dịch vụ chung. Xem [Cách signal gia nhập một cluster](https://docs.cloudthinker.io/vi/guide/pulse/clusters.md#tín-hiệu-gia-nhập-cluster-như-thế-nào).
* **Severity Mapping**: một hàng cho mỗi giá trị mức độ nghiêm trọng mà hệ thống của bạn gửi không phải `critical`, `high`, `medium`, `low` hoặc `info`. Nếu không có, các sự kiện đó đến dưới dạng Medium.
* **Rate limit (requests per hour)**: tăng từ 1.000 nếu hệ thống của bạn có thể gửi nhiều hơn trong một giờ.

Đây cũng là nơi bạn tắt **Auto-investigate**, hoặc đặt **Only from a minimum severity**.

**Trạng thái thành công:** nguồn xuất hiện trong **Signals to Resolve** trên thẻ Custom source. Sự kiện tiếp theo đến Pulse với tiêu đề và mức độ nghiêm trọng bạn đã ánh xạ, và các lần lặp lại của cùng một lỗi gia nhập một cluster.

## Sửa một nguồn

Mở **Resolve → Integrations → Custom source**, tìm nguồn trong **Signals to Resolve**, và chọn **Edit** từ menu **...** của nó. Màn hình sửa có mọi trường ánh xạ (kể cả **Resource ID Path** và **Timestamp Path**), **Severity Mapping**, **Auto-investigate** với **Only from a minimum severity**, và **Rate limit (requests per hour)**.

**Severity Mapping** khớp giá trị không phân biệt hoa thường. Ví dụ:

| Giá trị của bạn | Mức độ nghiêm trọng CloudThinker |
| - | - |
| `P1` | critical |
| `P2` | high |
| `P3` | medium |

## Gửi sự kiện

Nguồn tùy chỉnh mới yêu cầu `Authorization: Bearer <secret>`, với **Secret Key** sau `Bearer`. Nguồn này không chấp nhận `x-api-key` cho đến khi bạn đổi phương thức (xem [Tùy chọn xác thực](#tùy-chọn-xác-thực)). Đây là một ví dụ đầy đủ:

```bash theme={null}
curl -X POST "<your-webhook-url>" \
  -H "Authorization: Bearer <your-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "event": {
      "data": {
        "title": "Checkout latency above 2s",
        "priority": "critical",
        "service": "checkout-api",
        "host": "web-03",
        "summary": "p95 latency 2.4s for 5 minutes"
      }
    }
  }'
```

Khi ánh xạ với payload này, các trường trông như sau:

| Trường | Nơi bạn đặt | Đường dẫn bạn sẽ chọn | Tác dụng |
| - | - | - | - |
| **Incident title** (bắt buộc) | Thiết lập hoặc Edit | `event.data.title` | Đặt tên cho signal và sự cố. |
| **Severity** | Thiết lập hoặc Edit | `event.data.priority` | Dùng cho định tuyến và ngưỡng RCA tự động ([Only from a minimum severity](https://docs.cloudthinker.io/vi/guide/pulse/setup.md#các-tùy-chọn-webhook-làm-thay-đổi-hành-vi)). |
| **Service** | Thiết lập hoặc Edit | `event.data.service` | Nêu tên dịch vụ bị ảnh hưởng trên signal. Hiển thị `unknown` khi chưa ánh xạ. |
| **Resource** | Thiết lập hoặc Edit (**Resource Name Path**) | `event.data.host` | Tên hiển thị của host, cluster hoặc thành phần bị ảnh hưởng. |
| **Resource ID** | Chỉ Edit (**Resource ID Path**) | `event.data.host` | Key ổn định để nhóm. Pulse gộp một lần lặp lại vào cluster đang mở khi nguồn, loại cảnh báo và resource ID khớp. Khi chưa ánh xạ, key `resource_id` cấp cao nhất được đọc. Nếu không có resource ID nào, sự kiện chỉ được nhóm theo tiêu đề hoặc dịch vụ chung ([Cách signal gia nhập một cluster](https://docs.cloudthinker.io/vi/guide/pulse/clusters.md#tín-hiệu-gia-nhập-cluster-như-thế-nào)). |
| **Description** | Thiết lập hoặc Edit | `event.data.summary` | Cung cấp cho người ứng cứu và CloudThinker triệu chứng theo cách diễn đạt của hệ thống bạn. |
| **Timestamp** | Chỉ Edit (**Timestamp Path**) | không có ở đây | Thời điểm sự kiện bắt đầu. Phải theo ISO 8601, như `2026-10-25T14:02:00Z`. Giá trị không có múi giờ được hiểu là UTC. Khi chưa ánh xạ, key `timestamp` cấp cao nhất được đọc. Khi trường bị thiếu hoặc ở định dạng khác, chẳng hạn epoch seconds, thời điểm nhận được sử dụng. |

Với ánh xạ từ bảng trên, sự kiện này đến Pulse dưới dạng một signal có tiêu đề `Checkout latency above 2s`, mức độ nghiêm trọng **Critical**, dịch vụ `checkout-api`, tài nguyên `web-03`.

## Tùy chọn xác thực

Đổi phương thức trong màn hình sửa của nguồn, tại **Auth Method**. Thay đổi phương thức sẽ làm các bên gửi hiện tại lỗi ngay lập tức.

| Auth Method | Bên gửi gửi gì | Rủi ro |
| - | - | - |
| **Bearer Token** (mặc định) | `Authorization: Bearer <secret>` | Secret đi trong header. Ổn qua HTTPS với hầu hết bên gửi. |
| **HMAC Signature** | `X-Webhook-Signature: sha256=<hex digest>` của body thô (tên header có thể cấu hình) | Tùy chọn mạnh nhất: secret không bao giờ rời khỏi bên gửi, và body bị thay đổi sẽ không qua được kiểm tra. |
| **API Key** | Secret trong một header do bạn đặt tên, chẳng hạn `X-Api-Key`, với tiền tố tùy chọn | Giống Bearer. Dùng khi bên gửi chỉ hỗ trợ một header key có tên. |
| **No Authentication** | Không gì cả | URL là thông tin xác thực duy nhất. Bất kỳ ai có URL đều có thể gửi sự kiện. Chỉ dùng khi bên gửi không thể đặt header nào. |

## Phản hồi

Viết logic thử lại của bên gửi dựa trên các phản hồi sau:

| Trạng thái | Ý nghĩa | Cần làm gì |
| - | - | - |
| `2xx` | Đã nhận và xếp hàng. Chưa có nghĩa là một signal đã được tạo. | Kiểm tra Pulse (xem [Xác minh toàn bộ luồng](#xác-minh-toàn-bộ-luồng)). |
| `400` | Nguồn đang tạm dừng hoặc bị vô hiệu hóa, và hàng của nó trong **Signals to Resolve** hiển thị **Paused**. CloudThinker đã làm điều này: không màn hình Resolve nào tạm dừng hay tiếp tục một webhook, và nguyên nhân không nằm ở bên gửi của bạn. | Không thử lại. Gửi email đến [support@cloudthinker.io](mailto:support@cloudthinker.io) kèm tên nguồn và workspace, và yêu cầu bật lại nguồn. |
| `401` | Thiếu header `Authorization`, hoặc URL không phải **Webhook URL** hiện tại (sai, hoặc đã bị thay bằng **Regenerate credentials**). | Sửa header hoặc URL. Không thử lại khi chưa thay đổi. |
| `403` | Secret sai, hoặc header không có dạng `Bearer <secret>`. | Sửa header. Không thử lại với cùng secret. |
| `413` | Body lớn hơn 1 MB. | Gửi body nhỏ hơn. |
| `422` | Body không phải JSON object: JSON không hợp lệ, một mảng, hoặc một giá trị thuần. | Sửa body. Không thử lại khi chưa thay đổi. |
| `429` | Vượt 10 lần rate limit mỗi giờ, 10 lần xác thực thất bại trong 15 phút, hoặc CloudThinker đang bận tạm thời. | Thử lại với backoff, sau thời gian trong header `Retry-After` nếu có. Với khóa do xác thực, hãy sửa secret, rồi chờ: cửa sổ bắt đầu từ lần thất bại đầu tiên và được xóa sau 15 phút. **Regenerate credentials** không xóa được khóa này. |
| `5xx` | Sự cố tạm thời phía CloudThinker. | Thử lại với backoff. |

Mỗi nguồn mặc định cho phép 1.000 request mỗi giờ. Thay đổi trong màn hình sửa tại **Rate limit (requests per hour)**: tối đa 10.000, hoặc `0` để không giới hạn. `429` chỉ trả về khi vượt 10 lần giới hạn: với mặc định 1.000, sau 10.000 request trong cùng một giờ.

> [!WARNING]
>
> Các sự kiện vượt giới hạn mỗi giờ vẫn nhận `2xx`, rồi bị bỏ trước khi đến Pulse, bất kể mức độ nghiêm trọng. Bên gửi không thấy lỗi, và không màn hình Resolve nào liệt kê các sự kiện bị bỏ. Hãy tăng giới hạn trước một đợt gửi dồn dập đã lên kế hoạch.
>
> Để kiểm tra sự kiện bị bỏ sau một đợt gửi dồn dập, đặt cửa sổ thời gian của **Resolve → Pulse** thành **Last 1 hour** và đọc số **raw events** trong header. So sánh với số lượng mà bên gửi báo đã gửi trong cùng giờ. Raw events đếm mọi nguồn trong workspace, nên một con số thấp hơn nghĩa là sự kiện đã bị bỏ.

## Quy tắc cho payload và đường dẫn

| Quy tắc | Ý nghĩa với bạn |
| - | - |
| Gửi một JSON object | Mảng JSON hoặc giá trị thuần bị từ chối với `422`. `{}` được chấp nhận nhưng không có gì để ánh xạ, và hộp thoại sẽ cho bạn biết. |
| Đường dẫn dùng dấu chấm | `event.data.title` đọc `title` bên trong `data` bên trong `event`. Chỉ số danh sách là `[0]`, như trong `alerts[0].labels.severity`. |
| Tối đa 8 phân đoạn | Đường dẫn sâu hơn 8 cấp bị từ chối. Hãy gửi body phẳng hơn nếu giá trị của bạn lồng sâu hơn. |
| Tiền tố JSONPath là tùy chọn | `$.` ở đầu được chấp nhận và loại bỏ, nên `$.event.data.title` cũng hoạt động. |
| Giá trị mức độ nghiêm trọng | Chỉ `critical`, `high`, `medium`, `low` và `info` được hiểu nguyên dạng (không phân biệt hoa thường). Mọi giá trị khác, kể cả `warning`, `error`, `sev1` và `P1`, trở thành Medium trừ khi bạn ánh xạ chúng trong **Severity Mapping**. |
| Lần lặp lại bị bỏ | Pulse bỏ một lần lặp lại chính xác: cùng tiêu đề, mức độ nghiêm trọng và resource ID, với timestamp sự kiện trong cùng phút với một sự kiện Pulse đã thấy trong giờ qua. Không có **Timestamp Path**, timestamp là key `timestamp` cấp cao nhất, hoặc thời điểm nhận khi không có. Với thời điểm nhận, chỉ hai lần gửi trong cùng một phút mới khớp. Với timestamp sự kiện, một lần gửi lại mang timestamp gốc sẽ bị bỏ trong tối đa một giờ. Khi gửi lại một test, hãy đổi tiêu đề. Xem [chặn nhiễu](https://docs.cloudthinker.io/vi/guide/pulse/clusters.md#lọc-nhiễu). |

## Xác minh toàn bộ luồng

Phản hồi HTTP thành công chỉ chứng minh sự kiện đã được gửi đến. Mở **Resolve → Pulse**: signal sẽ xuất hiện trong vài giây. Xác nhận tiêu đề và mức độ nghiêm trọng của nó. Nếu **Auto-investigate** đang bật, mức độ nghiêm trọng vượt ngưỡng tối thiểu, và bước phân loại mở một sự cố, sự cố đó hiển thị **AI is handling** trên **Resolve → Investigation**. Xem [Điều gì khởi động một cuộc điều tra](https://docs.cloudthinker.io/vi/guide/incident/settings.md#điều-gì-khởi-động-một-cuộc-điều-tra).

Sự kiện test được xử lý như sự kiện thực, nên khi **Auto-investigate** bật, sự kiện đó có thể khởi động một cuộc điều tra thực. Để chỉ kiểm tra việc gửi, hãy tắt **Auto-investigate** trong màn hình sửa trước, và bật lại sau đó. Tránh tạm dừng **On duty** cho việc này: thao tác đó ảnh hưởng đến mọi nguồn trong workspace.

## Xoay vòng hoặc thay thế thông tin xác thực

**Regenerate credentials** (menu **...** của nguồn trong **Signals to Resolve**) thay thế cả **Webhook URL** và **Secret Key**. Cặp cũ ngừng hoạt động ngay lập tức, không có thời gian chồng lấn, nên các bên gửi sẽ thất bại cho đến khi bạn cập nhật chúng. Mỗi lần tạo lại được ghi trong [Audit Logs](https://docs.cloudthinker.io/vi/guide/security/audit-logs.md).

Để xoay vòng mà không bị gián đoạn:

1. Tạo một nguồn tùy chỉnh thứ hai và ánh xạ nó từ một sự kiện thực, như trong [Tạo nguồn](#tạo-nguồn).
2. Trong màn hình sửa của nguồn mới, sao chép những gì hộp thoại thiết lập không mang theo: **Resource ID Path**, **Timestamp Path**, **Severity Mapping**, **Rate limit (requests per hour)**, **Auto-investigate** và **Only from a minimum severity**, và **Auth Method** nếu bạn đã thay đổi.
3. Trỏ từng bên gửi đến URL và secret mới.
4. Khi sự kiện của nguồn cũ ngừng đến Pulse, hãy xóa nguồn cũ.

Nếu gián đoạn ngắn không sao, hãy tạo lại và cập nhật mọi bên gửi ngay sau đó.

## Bảo mật và dữ liệu

| Chủ đề | Điều cần biết |
| - | - |
| URL | Mỗi nguồn có **Webhook URL** riêng với một token ngẫu nhiên dài, nên không thể đoán URL của một nguồn từ URL của nguồn khác. |
| Xác thực | Mặc định, mọi request cần **Secret Key** dưới dạng `Authorization: Bearer <secret>`. Sau 10 lần thất bại trong 15 phút, nguồn trả về `429` cho đến khi cửa sổ được xóa (xem [Phản hồi](#phản-hồi)). |
| Ký payload | Với **HMAC Signature**, bên gửi ký body thô bằng HMAC-SHA256. Dán secret của riêng bạn vào **Provider signing secret** trong màn hình sửa, cách này giữ nguyên **Webhook URL**. Hoặc nhấn **Regenerate credentials** sau khi lưu để nhận secret mới, cách này cũng thay đổi **Webhook URL**. Xem ví dụ bên dưới. |
| Payload được lưu | Body sự kiện được lưu hết hạn sau 90 ngày. |

Để gửi một sự kiện có chữ ký, đặt **Auth Method** thành **HMAC Signature** trong màn hình sửa và lưu. Sau đó đặt signing secret (**Provider signing secret** của bạn, hoặc secret từ **Regenerate credentials**) vào `SECRET` và ký đúng các byte bạn gửi. Ví dụ dùng tên header mặc định:

```bash theme={null}
read -rs SECRET   # paste the signing secret, then press Enter
BODY='{"title":"Checkout latency above 2s","severity":"high"}'
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -hex | sed 's/^.* //')
curl -X POST "<your-webhook-url>" \
  -H "X-Webhook-Signature: sha256=$SIG" \
  -H "Content-Type: application/json" \
  --data-raw "$BODY"
```

## Khắc phục sự cố

> **Hộp thoại cứ chờ payload đầu tiên**
>
> Kiểm tra hệ thống của bạn gửi đến đúng **Webhook URL** và gửi `Authorization: Bearer <secret>`. Nhấn **Check again** sau khi gửi lại.

> **Sự kiện đã đến nhưng không có trường nào để ánh xạ**
>
> Body không phải là JSON object có giá trị. Gửi một object như `{"title": "..."}`, không phải mảng hay body trống, rồi nhấn **Check again**.

> **Signal có tiêu đề 'Webhook signal'**
>
> Các sự kiện đến trước khi bạn lưu ánh xạ vẫn trở thành signal. Resolve dùng key `title` cấp cao nhất nếu có, nếu không thì dùng giá trị dự phòng `Webhook signal`. Lưu một ánh xạ để khắc phục cho các sự kiện mới.

> **Không có payload để chọn khi sửa**
>
> Bộ chọn hiển thị sự kiện được lưu gần nhất, và body sự kiện được lưu hết hạn sau 90 ngày. Kích hoạt nguồn thêm một lần nữa, rồi ánh xạ.

> **Mọi sự kiện đều đến với mức độ nghiêm trọng Medium**
>
> Hệ thống của bạn gửi giá trị mức độ nghiêm trọng mà Resolve không nhận ra. Thêm hàng vào **Severity Mapping** trong màn hình sửa, ví dụ `p1` → critical. Xem [Sửa một nguồn](#sửa-một-nguồn).

## Bước tiếp theo

> **[Tìm hiểu Pulse](https://docs.cloudthinker.io/vi/guide/pulse/overview.md)**
>
> Xem cách signal được lọc, nhóm và chuyển thành sự cố.

> **[Dùng preset thay thế](https://docs.cloudthinker.io/vi/guide/incident/webhook-integrations/overview.md)**
>
> Kết nối Datadog, Grafana, PagerDuty và nhiều công cụ khác với ánh xạ được điền sẵn.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.